[
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\non:\n  push:\n    branches:\n      - master-11\n  pull_request:\n    types: [opened, synchronize, reopened]\njobs:\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n    if: startsWith(github.event.head_commit.message, '🏁 Releasing version') != true\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis\n      - name: Set up JDK 17\n        uses: actions/setup-java@v1\n        with:\n          java-version: 17\n      - name: Cache SonarCloud packages\n        uses: actions/cache@v1\n        with:\n          path: ~/.sonar/cache\n          key: ${{ runner.os }}-sonar\n          restore-keys: ${{ runner.os }}-sonar\n      - name: Cache Maven packages\n        uses: actions/cache@v1\n        with:\n          path: ~/.m2\n          key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}\n          restore-keys: ${{ runner.os }}-m2\n      - name: Build and analyze\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any\n          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}\n        run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: \"Release version\"\n        required: true\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          fetch-depth: 0\n\n      - name: 'Set up Java'\n        uses: actions/setup-java@v2\n        with:\n          java-version: 14\n          distribution: 'zulu'\n          server-id: central\n          server-username: MAVEN_USERNAME\n          server-password: MAVEN_CENTRAL_TOKEN\n          gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}\n          gpg-passphrase: MAVEN_GPG_PASSPHRASE\n\n      - name: 'Cache Maven packages'\n        uses: actions/cache@v2\n        with:\n          path: ~/.m2\n          key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}\n          restore-keys: ${{ runner.os }}-m2\n\n      - name: Update version\n        id: version\n        run: |\n          VERSION=${{ github.event.inputs.version }}\n          echo \"Updating POMs to version $VERSION\"\n          ./mvnw -B versions:set versions:commit -DnewVersion=$VERSION\n          git config --global user.email \"41898282+github-actions[bot]@users.noreply.github.com\"\n          git config --global user.name \"GitHub Action\"\n          git commit -a -m \"🏁 Releasing version $VERSION\"\n          git push origin HEAD:master-11\n\n      - name: Release to Maven Central\n        env:\n          MAVEN_USERNAME: ${{ secrets.SONATYPE_USERNAME }}\n          MAVEN_CENTRAL_TOKEN: ${{ secrets.SONATYPE_PASSWORD }}\n          MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}\n        run: |\n          export GPG_TTY=$(tty)\n          ./mvnw --no-transfer-progress -B --file pom.xml \\\n            -Drepository.url=https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git \\\n            -Dmaven.site.skip=true -Drelease=true deploy\n\n      - name: Release to GitHub\n        env:\n          JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: ./mvnw -B --file pom.xml -Prelease -pl :formsfx jreleaser:full-release\n\n      - name: JReleaser output\n        if: always()\n        uses: actions/upload-artifact@v2\n        with:\n          name: jreleaser-logs\n          path: |\n            target/jreleaser/trace.log\n            target/jreleaser/output.properties\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea\n/target\n*.iml\n/out"
  },
  {
    "path": ".mvn/wrapper/maven-wrapper.properties",
    "content": "# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n# \n#   http://www.apache.org/licenses/LICENSE-2.0\n# \n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\ndistributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip\nwrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\n\n## [11.4.1](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/11.4.1) (2019-09-01)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/formsfx/compare/8.4.0...11.4.1)\n\n## [8.4.0](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/8.4.0) (2019-08-25)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/formsfx/compare/11.4.0...8.4.0)\n\n## [11.4.0](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/11.4.0) (2019-08-25)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/formsfx/compare/v11.3.2...11.4.0)\n\n**Implemented enhancements:**\n\n- Add Continuous Delivery to Maven Central [\\#47](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/47) ([martinfrancois](https://github.com/martinfrancois))\n\n## [v11.3.2](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/v11.3.2) (2019-01-09)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/formsfx/compare/v10.3.1...v11.3.2)\n\n**Closed issues:**\n\n- Date Field [\\#39](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/39)\n\n## [v10.3.1](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/v10.3.1) (2019-01-09)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/formsfx/compare/v11.3.1...v10.3.1)\n\n## [v11.3.1](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/v11.3.1) (2019-01-09)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/formsfx/compare/v1.3.1...v11.3.1)\n\n## [v1.3.1](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/v1.3.1) (2019-01-09)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/formsfx/compare/v11.3.0...v1.3.1)\n\n**Closed issues:**\n\n- JDK 11 compatible [\\#38](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/38)\n\n**Merged pull requests:**\n\n- New DateField support. [\\#42](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/42) ([dlemmermann](https://github.com/dlemmermann))\n- New Field - DatePicker [\\#40](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/40) ([pershy](https://github.com/pershy))\n\n## [v11.3.0](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/v11.3.0) (2018-09-17)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/formsfx/compare/v10.3.0...v11.3.0)\n\n## [v10.3.0](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/v10.3.0) (2018-09-17)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/formsfx/compare/v1.3.0...v10.3.0)\n\n## [v1.3.0](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/v1.3.0) (2018-09-06)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/formsfx/compare/v1.2.1...v1.3.0)\n\n**Implemented enhancements:**\n\n- PasswordFields [\\#7](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/7)\n\n**Closed issues:**\n\n- Required field fails when I18N is enabled [\\#32](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/32)\n- DataField assumes edited value can be transformed to a suitable String representation [\\#30](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/30)\n- Open SimpleControl impls for extension [\\#27](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/27)\n- Trigger an event when Form is persisted/reset [\\#25](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/25)\n- Embed additional content inside a Group/Section [\\#23](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/23)\n- Support additional descriptions on Field [\\#18](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/18)\n- Section.title should return Section [\\#15](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/15)\n- Allow SectionRenderer.titledPane.collapsible to be set [\\#13](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/13)\n- Add custom style-class to Form/Section renderers [\\#12](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/12)\n- Update codebase with fixes from PreferencesFX [\\#11](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/11)\n\n**Merged pull requests:**\n\n- Use StringConverter instead of ValueTransformer in DataField [\\#34](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/34) ([aalmiray](https://github.com/aalmiray))\n- Check against empty requiredError instead of null [\\#33](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/33) ([aalmiray](https://github.com/aalmiray))\n- Enable additional content inside Form/Section/Group [\\#29](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/29) ([aalmiray](https://github.com/aalmiray))\n- Extension points [\\#28](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/28) ([aalmiray](https://github.com/aalmiray))\n- Add event handlers to Form/Group/Field [\\#26](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/26) ([aalmiray](https://github.com/aalmiray))\n- Additional description support for label/value [\\#21](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/21) ([aalmiray](https://github.com/aalmiray))\n- Fix fluent interface methods in Section [\\#17](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/17) ([aalmiray](https://github.com/aalmiray))\n- Add a collapsible property to Section [\\#16](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/16) ([aalmiray](https://github.com/aalmiray))\n- Add style classes to FormRenderer and SectionRenderer [\\#14](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/14) ([aalmiray](https://github.com/aalmiray))\n- PasswordField support [\\#10](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/10) ([aalmiray](https://github.com/aalmiray))\n\n## [v1.2.1](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/v1.2.1) (2018-04-30)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/formsfx/compare/v1.2-2...v1.2.1)\n\n## [v1.2-2](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/v1.2-2) (2018-04-27)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/formsfx/compare/v1.2...v1.2-2)\n\n## [v1.2](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/v1.2) (2018-04-27)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/formsfx/compare/v1.1...v1.2)\n\n**Fixed bugs:**\n\n- Single Selection Type Fields Not Persisting [\\#2](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/2)\n\n**Closed issues:**\n\n- Any plans to publish the project to Maven? [\\#5](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/5)\n\n**Merged pull requests:**\n\n- Fix dependency issues [\\#8](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/8) ([martinfrancois](https://github.com/martinfrancois))\n\n## [v1.1](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/v1.1) (2017-12-06)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/formsfx/compare/v1.0...v1.1)\n\n**Merged pull requests:**\n\n- Bugfixing and Changes of Visibility for Inheritance [\\#6](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/6) ([martinfrancois](https://github.com/martinfrancois))\n\n## [v1.0](https://github.com/dlsc-software-consulting-gmbh/formsfx/tree/v1.0) (2017-09-26)\n**Closed issues:**\n\n- Make Field Instance Variable Protected In SimpleControl [\\#3](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/3)\n- add license [\\#1](https://github.com/dlsc-software-consulting-gmbh/formsfx/issues/1)\n\n**Merged pull requests:**\n\n- adds default encoding for project [\\#4](https://github.com/dlsc-software-consulting-gmbh/formsfx/pull/4) ([rladstaetter](https://github.com/rladstaetter))\n\n\n\n\\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "[![JFXCentral](https://img.shields.io/badge/Find_me_on-JFXCentral-blue?logo=googlechrome&logoColor=white)](https://www.jfx-central.com/libraries/formsfx)\n\n# FormsFX\n**Forms for business application made easy. Creating forms in Java has never been this easy!**\n\n## Maven\n\nTo use this framework as part of your Maven build simply add the following dependency to your pom.xml file.\n\n```XML\n<dependency>\n  <groupId>com.dlsc.formsfx</groupId>\n  <artifactId>formsfx-core</artifactId>\n  <version>11.3.2</version>\n</dependency>\n```\n\n## What is FormsFX?\n\nCreating forms in JavaFX is a tedious and very error-prone task. FormsFX is a framework which solves this problem. It enables the developer to create forms with ease and creates well-designed and user-friendly forms by default. FormsFX offers a fluent API that is very easy to understand and reduces the amount of coding needed. It creates all the necessary bindings for the properties and *it just works*.\n\n## Main Features\n\n- Simple and understandable Fluent API\n- Different semantic items\n- Pre-defined controls\n- Validation\n- Localisation\n\n```Java\nForm loginForm = Form.of(\n        Group.of(\n                Field.ofStringType(model.usernameProperty())\n                        .label(\"Username\"),\n                Field.ofStringType(model.passwordProperty())\n                        .label(\"Password\")\n                        .required(\"This field can’t be empty\")\n        )\n).title(\"Login\");\n```\n\n## Semantics\n\nFormsFX offers different semantic layers. The largest entity is the form. It contains groups and sections, which in turn act as containers for fields. Fields are the end user's primary point of interaction as they handle data input and presentation.\n\n## Defining a form\n\nCreating a form is as simple as calling `Form.of()`.\n\n```Java\nForm.of(\n        Group.of(\n                Field.ofStringType(\"\")\n                        .label(\"Username\"),\n                Field.ofStringType(\"\")\n                        .label(\"Password\")\n                        .required(\"This field can’t be empty\")\n        ),\n        Group.of(…)\n).title(\"Login\");\n```\n\nFields have a range of options that define their semantics and change their functionality.\n\nOption | Description\n------ | -----------\n`label(String)` | Describes the field’s content in a concise manner. This description is always visible and usually placed next to the editable control.\n`tooltip(String)` | This contextual hint further describes the field. It is usually displayed on hover or focus.\n`placeholder(String)` | This hint describes the expected input as long as the field is empty.\n`required(boolean)` <br /> `required(String)` | Determines, whether entry in this field is required for the correctness of the form.\n`editable(boolean)` | Determines, whether end users can edit the contents of this field.\n`id(String)` | Describes the field with a unique ID. This is not visible directly, but can be used for styling purposes.\n`styleClass(List&lt;String&gt;)` | Adds styling hooks to the field. This can be used on the view layer.\n`span(int)` <br /> `span(ColSpan)` | Determines, how many columns the field should span on the view layer. Can be a number between 1 and 12 or a ColSpan fraction.\n`render(SimpleControl)` | Determines the control that is used to render this field on the view layer.\n\n\nThe following table shows how to create different fields and how they look by default:\n\nString Control\n\n<table>\n  <tbody>\n    <tr>\n      <td colspan=\"2\">String Control</td>\n    </tr>\n    <tr>\n      <td><img src=\"./docs/images/StringField.png\" /></td>\n    </tr>\n    <tr>\n      <td>\n        <pre lang=\"java\">Field.ofStringType(\"CHF\")\n     .label(\"Currency\")</pre>\n      </td>\n    </tr>\n  </tbody>\n</table>\n<table>\n  <tbody>\n    <tr>\n      <td colspan=\"2\">Integer Control</td>\n    </tr>\n    <tr>\n      <td><img src=\"./docs/images/IntegerField.png\" /></td>\n    </tr>\n    <tr>\n      <td>\n        <pre lang=\"java\">Field.ofIntegerType(8401120)\n     .label(\"Population\")</pre>\n      </td>\n    </tr>\n  </tbody>\n</table>\n<table>\n  <tbody>\n    <tr>\n      <td colspan=\"2\">Double Control</td>\n    </tr>\n    <tr>\n      <td><img src=\"./docs/images/DoubleField.png\" /></td>\n    </tr>\n    <tr>\n      <td>\n        <pre lang=\"java\">Field.ofDoubleType(41285.0)\n       .label(\"Area\")</pre>\n      </td>\n    </tr>\n  </tbody>\n</table>\n<table>\n  <tbody>\n    <tr>\n      <td colspan=\"2\">Boolean Control</td>\n    </tr>\n    <tr>\n      <td><img src=\"./docs/images/BooleanField.png\" /></td>\n    </tr>\n    <tr>\n      <td>\n        <pre lang=\"java\">Field.ofBooleanType(false)\n     .label(\"Independent\")</pre>\n      </td>\n    </tr>\n  </tbody>\n</table>\n<table>\n  <tbody>\n    <tr>\n      <td colspan=\"2\">ComboBox Control</td>\n    </tr>\n    <tr>\n      <td><img src=\"./docs/images/ComboBoxField.png\" /></td>\n    </tr>\n    <tr>\n      <td>\n        <pre lang=\"java\">Field.ofSingleSelectionType(Arrays.asList(\"Zürich (ZH)\", \"Bern (BE)\", …), 1)\n     .label(\"Capital\")</pre>\n      </td>\n    </tr>\n  </tbody>\n</table>\n<table>\n  <tbody>\n    <tr>\n      <td colspan=\"2\">RadioButton Control</td>\n    </tr>\n    <tr>\n      <td><img src=\"./docs/images/RadioButtonField.png\" /></td>\n    </tr>\n    <tr>\n      <td>\n        <pre lang=\"java\">Field.ofSingleSelectionType(Arrays.asList(\"Right\", \"Left\"), 0)\n     .label(\"Driving on the\")\n     .render(new SimpleRadioButtonControl<>())</pre>\n      </td>\n    </tr>\n  </tbody>\n</table>\n<table>\n  <tbody>\n    <tr>\n      <td colspan=\"2\">CheckBox Control</td>\n    </tr>\n    <tr>\n      <td><img src=\"./docs/images/CheckBoxField.png\" /></td>\n    </tr>\n    <tr>\n      <td>\n        <pre lang=\"java\">Field.ofMultiSelectionType(Arrays.asList(\"Africa\", \"Asia\", …), Collections.singletonList(2))\n     .label(\"Continent\")\n     .render(new SimpleCheckBoxControl<>())</pre>\n      </td>\n    </tr>\n  </tbody>\n</table>\n<table>\n  <tbody>\n    <tr>\n      <td colspan=\"2\">ListView Control</td>\n    </tr>\n    <tr>\n      <td><img src=\"./docs/images/ListField.png\" /></td>\n    </tr>\n    <tr>\n      <td>\n        <pre lang=\"java\">Field.ofMultiSelectionType(Arrays.asList(\"Zürich (ZH)\", \"Bern (BE)\", …), Arrays.asList(0, 1, …))\n     .label(\"Biggest Cities\")</pre>\n      </td>\n    </tr>\n  </tbody>\n</table>\n\n## Rendering a form\n\nThe only point of interaction is the `FormRenderer`. It delegates rendering of further components to other renderers.\n\n```java\nPane root = new Pane();\nroot.getChildren().add(new FormRenderer(form));\n```\n\nAll fields have a default control that is used for rendering. This can be changed to another compatible implementation using the `render()` method.\n\n```java\nField.ofMultiSelectionType(…)\n        .render(new SimpleCheckBoxControl<>())\n```\n\n## Model\n\nForms are used to create and manipulate data. In order to use this data in other parts of an application, model classes can be used. These classes contain properties, which are then bound to the persisted value of a field.\n\n```java\nStringProperty name = new SimpleStringProperty(\"Hans\");\nField.ofStringType(name);\n```\n\nThe `persist()` and `reset()` methods can be used to store and revert field values, which in turn updates the binding.\n\nFields in FormsFX store their values in multiple steps. For free-form fields, like `StringField` or `DoubleField`, the exact user input is stored, along with a type-transformed value and a persistent value. The persistence is, by default, handled manually, but this can be overridden by setting the `BindingMode` to `CONTINUOUS` on the form level.\n\n## Localisation\n\nAll displayed values are localisable. Methods like `label()`, `placeholder()` accept keys which are then used for translation. By default, FormsFX includes a `ResourceBundle`-based implementation, however, this can be exchanged for a custom implementation.\n\n```java\nprivate ResourceBundle rbDE = ResourceBundle.getBundle(\"demo.demo-locale\", new Locale(\"de\", \"CH\"));\nprivate ResourceBundle rbEN = ResourceBundle.getBundle(\"demo.demo-locale\", new Locale(\"en\", \"UK\"));\n\nprivate ResourceBundleService rbs = new ResourceBundleService(rbEN);\n\nForm.of(…)\n        .i18n(rbs);\n```\n\n## Validation\n\nAll fields are validated whenever end users edit the contained data. FormsFX offers a wide range of pre-defined validators, but also includes support for custom validators using the `CustomValidator.forPredicate()` method.\n\n| Validator | Description |\n| --------- | ----------- |\n| `CustomValidator` | Define a predicate that returns whether the field is valid or not. |\n| `DoubleRangeValidator` | Define a number range which is considered valid. This range can be limited in either one direction or in both directions. |\n| `IntegerRangeValidator` | Define a number range which is considered valid. This range can be limited in either one direction or in both directions. |\n| `RegexValidator` | Valiate text against a regular expression. This validator offers pre-defined expressions for common use cases, such as email addresses.\n| `SelectionLengthValidator` | Define a length interval which is considered valid. This range can be limited in either one direction or in both directions. |\n| `StringLengthValidator` | Define a length interval which is considered valid. This range can be limited in either one direction or in both directions. |\n\n## Advantages\n\n- Less error-prone\n- Less code needed\n- Easy to learn\n- Easy to understand\n- Easy to extend\n\n# Documentation\n\n- [Javadocs](http://dlsc.com/wp-content/html/formsfx/apidocs/)\n- [Report](./docs/Project%20Report.pdf)\n"
  },
  {
    "path": "docs/Project Report.pages/Metadata/BuildVersionHistory.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<array>\n\t<string>Template: Blank (2015-09-16 11:13)</string>\n\t<string>M5.6.2-2573-1</string>\n\t<string>M6.0-3507-1</string>\n\t<string>M5.6.2-2573-1</string>\n\t<string>M6.0.5-4052-1</string>\n\t<string>M5.6.2-2573-1</string>\n\t<string>M6.0.5-4052-1</string>\n\t<string>M5.6.2-2573-1</string>\n\t<string>M6.0.5-4052-1</string>\n\t<string>M5.6.2-2573-1</string>\n\t<string>M6.0.5-4052-1</string>\n\t<string>M6.1-4328-1</string>\n\t<string>M6.1.1-4338-1</string>\n\t<string>G-r13-2C85</string>\n\t<string>M6.2-4582-1</string>\n\t<string>G-r13-2C85</string>\n\t<string>M6.2-4582-1</string>\n</array>\n</plist>\n"
  },
  {
    "path": "docs/Project Report.pages/Metadata/DocumentIdentifier",
    "content": "A198DE43-9C62-4C8D-9A85-4BBB04C88780"
  },
  {
    "path": "docs/architecture.xml",
    "content": "<mxfile userAgent=\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30\" version=\"6.9.5\" editor=\"www.draw.io\" type=\"device\"><diagram id=\"5cf761be-5104-c127-7bda-1d032f754fbc\" name=\"Page-1\">3Zxfk6I4EMA/ja9bQPj7eOPM7F3VbtWWbt3tPWYkC9mJxIpx1Pv0FyUoIci4I0TAl4EmQtL969CdjjMB0+XuM4Or9CuNEZk4VrybgMeJ49guCMWfg2SfSyLLzwUJw3Euss6COf4PyW8W0g2O0VrKchGnlHC8UoULmmVowRUZZIxu1WY/KYkVwQomSBPMF5Do0n9wzFMptf3ofOFPhJO0eLTvufmVF7h4TRjdZPKBEwf8PH7yy0tY3EyOdJ3CmG5LIvA0AVNGKc+PlrspIgflqnp7vnD11HGGMn7NF4Aj+8H3xeBRLHQhTynjKU1oBsnTWfpwHB863MESZylfEnFoi0PxULb/UT7599Dok+OJ8zWHjP9xsI6QZTRDhewZEyLvhbK4aBFjuKRZ/D3FWX6h1OwX4nwvsYEbToXo3NMvlK5kF9ac0Vc0pYSy49iAdfyIK/moD0O9qDgpWtMNW8hWUlmi0wmSrZzwZDXhDogukRi1aMMQgRy/qbeHksvk1E5+VQwa7ksNVhRnfF2687eDQDSQLub6QX5H6WDA9lQzv9c+VLAQB3kPirPSUM6iIzr1GEnFvEGykSN+pmypoaWCs00xR/MVPGp3K2YSFSZIcCJM/7gQFkHsZLM3xDjaNVtNN0ehh0jVgxPmerO2JTcvfDMtebhvXbZgSYuNSgKgTV/rlTtdcnzvw64GunI13Tc8lQnX99Rb5H2Q3zqb+7d91qr4YPSOzza3v9lngeaznwVsqz46baQqwjfotE6rTtvkJ7/p0C+ELl5782Z0dXcFN3rrtRZyNY7nIibENOsfyb5/R5KBa4LkYCShntfB++daS3ntBuU7zH8UShfHJTudjJjPQGczDn06qrEe8NqIHm4N1N2oko+9095zm4MEEDS2vzlI8LTJ9RkjkU33bmqtBgmua3BqtY1MrYMPEnzdK291ymst5GscP0IOe8pyNUwwy7LXCcuVN8wHMth+sRzcj+VAY3mOSB7y9hTo6uTsOSYzOL+TaEpGUEVApbLujAv2UIc9MAR7qMOOs4Sg3iPvqIGZb5lEPugGeacB+WBcyEf3Qz7SkP+6IRz3nXivErUYJd4OO4laxhaBFwXWMta+IayLZ5encs7EZN5TnkO3krobjcKjTmZwZQmosgI0sgncLjYOtIj6h5aAAq8S/HrtLtEUAy3ntnTzQtBA/MpsMmBpCunar0bmVs4d3yCORvpfgssEsYGgbjYJsDWFdI36yNJeu2YTgjHW9fL8A6UEwb6G/xrrJrfWhPo7sE34FYrVamUDzBL3frBct4ZjqkJfs4jzncFsfXgMzeaIvWHRzd4RDezKBhyj1fpWA5VBT8N1azHGavE1qzEzlPfuQSj7sBA5EHyN5q9uXyuil/aa2HcEHNRsV3BNTc5AL/HPKOV/CyRjyMUIewi2o0YakUmw2y2PnpbWG1bWxxVUg5pdADfvzbkadn0fwHSz5nQ5INyDwCTu3VSSLieRY1txBzUbBYCpJBLUbBU4Lrl/QVnC0x5DX90xYBb6bopJo1tiB3U5pTG29aRyhhK0GxDUoWcS6nYrSqMNT2qSTXNM68lmXsqZwSxBAyI7AgbJ9tqt6Yx2OdutKf4bQ9vVq/+ydjM0tm3LNgl3u1WcFn+g3TO4a8r9N8P9oXq/61byNeed33lW26s/Ebl5f0Chmbr9woPLA8yu9eg1OKkqoboZWm8I75/GbBAY05g4Pf+rihzN8z8EAU//Aw==</diagram><diagram id=\"2878d1a8-c243-3bc8-e21e-b7c19a7684b3\" name=\"Page-2\">3VdNb9swDP01vg62lGTpcc3SbcB2WYatPaoWa2uVxUCRm2S/fpIl+QNOigILmnRGDuSjPsjHF8JO6KLafdJsXX5DDjIhKd8l9GNCyJyQxP1SvvfANJt7oNCCeyjtgJX4Ax7MIloLDpuAecggSiPWQzBHpSA3A4xpjdvhsgeUfACsWQEjYJUzOUZ/CW7KWNaswz+DKMp4cza78pF7lj8WGmsV7ksIfWgeH65YPCsUuikZx20PosuELjSi8Va1W4B01A5puzkSbfPWoMyLNkz8jicma4gpN4mZfSSjKQfchjSh19tSGFitWe6iW9t9i5WmktbLrMmkKJS1c5sAaAuE80Eb2B1NMmtLt4oCrMDovV0SNhAa2Apqyt4Hf9v1JpsHrOz1hUSQBT0U7dkdJ9YItByhiL4Biibzc1JERgx9B8uHttVdHFNkNmSqJaDPFDnA1OwUWhpLB7idPMFFbUosUDG57NDrIWU9emAnzG2AnX3n7HdT5ymb2G1c5pwutjFMmw9uSFqIC1ah4j9KoWLoRsj2AsXjQoUKPBLi7tLfYMw+TG9WG7RQV8JXxHU4Z2M0PsICJeqmaJo2T9tQx8Hz7bSUYa3zsCr8I222BUQdH266BsmMeBqe/i8dHA+DldF1bmoNl6f1aXZGrc9PKvUo6J6+78KyFwo6vWBBT8aCpq8k6PELwBcnQGVvcUWLTWNcvrazq9ec4+mIkFPMca/tOMmfkX32f8h+ej7ZT0ey/2lVyt+I2Omh17sTid263fdHE+t949HlXw==</diagram><diagram id=\"1a86d444-8032-98fa-018f-ab649ec67932\" name=\"Page-3\">3Zpdb5swFIZ/TS5XgQGTXi7pxyZ109RMW3vpgAtegIMc0yT79TPBBAihyqRCTLkJvPjz+DkHjsnEmsfbe07S8Bv4NJogw99OrJsJkge25U+u7Arl2sCFEHDmF5JRCQv2lxaiWaoZ8+laaYUkACLB0qboQZJQTzQ0wjlsmsVeIPIbQkoC2hIWHona6m/mi7BQpwhX+hfKgrDs2cTXxZ0l8VYBhyxR/U2Q9bI/itsxKdtSE12HxIdNTbJuJ9acA4jiLN7OaZTbtmm2u467h3FzmohzKqCiwiuJMjX1O+DxI018yilXYxS70i77mdG8rjGxZpuQCbpIiZff3UgSpBaKOJJXpjwlEQsSee7Jsci2rJnqi3JBt53jNQ9WkHRRiKngO1lEVfjkWspyiiwbqzXZVOtkltYNa2uElUYUGsGh7co88kRZ6LS1rJa17qVF0tJcM7KmIzCZYzvDmcxuGYT60tXUJXARQgAJiW4rddY0Wc08dMvEUy5fIUddPqtS0hh8V9xznfL6WdVbC8LF5zwuSCGBhJbaHcsHrhrwyxLLCLxVIdUK/KFC7FSoIpkAKVXDfwBID51xWNE5RMD3E7aM/XFYzHz+jaVcQ8Y9JamVkWMLqGhAd8aCcxoRwV6brZ9aPFX1BzDZYgUKtpugYMNptlEMS1WrGJB2I7tasTQvsP6fjtS8K6aKJivCDrM8Czqn5acL+ZRgkGgc2DA+bZNBvBT346XuG16KRuyl7iW91DUG8tJWR+/spe7bT1MNfXTaYZFBfHTaDmosTiPJsHQiiDQ0l2VZl3vxuO4jpJn1eHblNCJaPZyp8DaeiFYmXvWQNn3/kHbu4pXDacH+wNbiF6MbfaF3jCb0gyYoptmyyKDUjwx6pBf07WRcRfiQeqsZbMcD/aCR3rQuC707Muptvai3O6ifAUSUJOOBHqMhI73TB/QK9FrGNiKusV5c4w6uv2fxknJ9sT5+ax8Wa7cfrD/qfqE5bUNv4rPX/P2p78pSv0pCA52xR7iJvWsOiX0v2erH3YBDJ9LVS2KPuvLVG8iWOm/R2OWH2UtQj3rJVo3uF3dz1Hs0SK90tRxOi/mfdCv0Jd7Ex/szA6aqqJdU9XziRwa8Xpkq6spU5xAvQev9mWPoB92fQf2kqmdDP7L9GaRXHou68thH4jOYZUKAxns0x+D3+cVOXlZ/3Cq+h1b/jrNu/wE=</diagram></mxfile>"
  },
  {
    "path": "formsfx-core/.gitignore",
    "content": "/target\n*.iml"
  },
  {
    "path": "formsfx-core/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.dlsc.formsfx</groupId>\n    <artifactId>formsfx-core</artifactId>\n    <packaging>jar</packaging>\n    <version>11.6.0</version>\n\n    <name>FormsFX Core</name>\n    <description>A framework for quickly creating form-based UIs.</description>\n    <url>https://github.com/dlemmermann/formsfx</url>\n\n    <parent>\n        <groupId>com.dlsc.formsfx</groupId>\n        <artifactId>formsfx</artifactId>\n        <version>11.6.0</version>\n    </parent>\n\n    <licenses>\n        <license>\n            <name>Apache 2.0</name>\n        </license>\n    </licenses>\n    <scm>\n        <url>https://github.com/dlemmermann/formsfx</url>\n    </scm>\n\n    <developers>\n        <developer>\n            <name>Dirk Lemmermann</name>\n            <url>http://www.dlsc.com</url>\n            <organization>DLSC Software &amp; Consulting</organization>\n        </developer>\n    </developers>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.8.0</version>\n                <configuration>\n                    <source>11</source>\n                    <target>11</target>\n                </configuration>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-source-plugin</artifactId>\n                <version>3.2.1</version>\n                <executions>\n                    <execution>\n                        <id>attach-sources</id>\n                        <goals>\n                            <goal>jar</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-javadoc-plugin</artifactId>\n                <version>3.2.0</version>\n                <configuration>\n                    <failOnError>false</failOnError>\n                    <force>true</force>\n                    <windowtitle>FormsFX API</windowtitle>\n                    <additionalJOptions>\n                        <additionalJOption>-J-Djavafx.javadoc=true</additionalJOption>\n                        <additionalJOption>-html5</additionalJOption>\n                    </additionalJOptions>\n                    <docfilessubdirs>true</docfilessubdirs>\n                    <tags>\n                        <tag>\n                            <name>defaultValue</name>\n                            <placement>a</placement>\n                            <head>Default Value:</head>\n                        </tag>\n                        <tag>\n                            <name>apiNote</name>\n                            <placement>a</placement>\n                            <head>API Note:</head>\n                        </tag>\n                        <tag>\n                            <name>implSpec</name>\n                            <placement>a</placement>\n                            <head>Implementation Requirements:</head>\n                        </tag>\n                        <tag>\n                            <name>implNote</name>\n                            <placement>a</placement>\n                            <head>Implementation Note:</head>\n                        </tag>\n                    </tags>\n                    <sourceFileExcludes>\n                        <sourceFileExclude>**\\/\\module-info.java</sourceFileExclude>\n                    </sourceFileExcludes>\n                </configuration>\n                <dependencies>\n                    <dependency>\n                        <groupId>org.ow2.asm</groupId>\n                        <artifactId>asm</artifactId>\n                        <version>7.0</version>\n                    </dependency>\n                </dependencies>\n                <executions>\n                    <execution>\n                        <id>make-docs\n\n                        </id> <!-- this is used for inheritance merges -->\n                        <phase>package\n                        </phase> <!-- bind to the packaging phase -->\n                        <goals>\n                            <goal>aggregate</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>attach-javadocs</id>\n                        <goals>\n                            <goal>jar</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n\n\n            <plugin>\n                <groupId>org.codehaus.mojo</groupId>\n                <artifactId>license-maven-plugin</artifactId>\n                <version>1.16</version>\n                <configuration>\n                    <licenseName>apache20</licenseName>\n                    <licenseResolver>${project.baseUri}/src/license\n                    </licenseResolver>\n                    <organizationName>DLSC Software &amp; Consulting GmbH\n                    </organizationName>\n                    <inceptionYear>2019</inceptionYear>\n                    <projectName>FormsFX</projectName>\n                    <processStartTag>\n                        ========================LICENSE_START=================================\n                    </processStartTag>\n                    <processEndTag>\n                        =========================LICENSE_END==================================\n                    </processEndTag>\n                    <verbose>false</verbose>\n                    <includes>\n                        <includes>**/*.java</includes>\n                    </includes>\n\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>first</id>\n                        <goals>\n                            <goal>update-file-header</goal>\n                        </goals>\n                        <phase>process-sources</phase>\n                        <configuration>\n                            <roots>\n                                <root>src/main/java</root>\n                                <root>src/test/java</root>\n                            </roots>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n\n        </plugins>\n    </build>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.openjfx</groupId>\n            <artifactId>javafx-controls</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.openjfx</groupId>\n            <artifactId>javafx-fxml</artifactId>\n        </dependency>\n\n        <!-- Test Dependencies -->\n\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n</project>"
  },
  {
    "path": "formsfx-core/src/license/apache20/header.txt",
    "content": "Licensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License."
  },
  {
    "path": "formsfx-core/src/license/apache20/license.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "formsfx-core/src/license/licenses.properties",
    "content": "apache20=Apache License 2.0\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/event/FieldEvent.java",
    "content": "package com.dlsc.formsfx.model.event;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 - 2018 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.Field;\nimport javafx.event.Event;\nimport javafx.event.EventType;\n\n/**\n * Identifies events triggered by a {@code Field}.\n *\n * @author Andres Almiray\n */\npublic final class FieldEvent extends Event {\n    /**\n     * When a {@code Field} is persisted.\n     */\n    public static final EventType<FieldEvent> EVENT_FIELD_PERSISTED = new EventType<>(ANY, \"EVENT_FIELD_PERSISTED\");\n\n    /**\n     * When a {@code Field} is reset.\n     */\n    public static final EventType<FieldEvent> EVENT_FIELD_RESET = new EventType<>(ANY, \"EVENT_FIELD_RESET\");\n\n    /**\n     * Creates a new instance of {@code FieldEvent} with event type set to {@code EVENT_FIELD_PERSISTED}.\n     */\n    public static FieldEvent fieldPersistedEvent(Field field) {\n        return new FieldEvent(EVENT_FIELD_PERSISTED, field);\n    }\n\n    /**\n     * Creates a new instance of {@code FieldEvent} with event type set to {@code EVENT_FIELD_RESET}.\n     */\n    public static FieldEvent fieldResetEvent(Field field) {\n        return new FieldEvent(EVENT_FIELD_RESET, field);\n    }\n\n    private final Field field;\n\n    private FieldEvent(EventType<? extends Event> eventType, Field field) {\n        super(eventType);\n        this.field = field;\n    }\n\n    public final Field getField() {\n        return field;\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/event/FormEvent.java",
    "content": "package com.dlsc.formsfx.model.event;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 - 2018 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.Form;\nimport javafx.event.Event;\nimport javafx.event.EventType;\n\n/**\n * Identifies events triggered by a {@code Form}.\n *\n * @author Andres Almiray\n */\npublic final class FormEvent extends Event {\n    /**\n     * When a {@code Form} is persisted.\n     */\n    public static final EventType<FormEvent> EVENT_FORM_PERSISTED = new EventType<>(ANY, \"EVENT_FORM_PERSISTED\");\n\n    /**\n     * When a {@code Form} is reset.\n     */\n    public static final EventType<FormEvent> EVENT_FORM_RESET = new EventType<>(ANY, \"EVENT_FORM_RESET\");\n\n    /**\n     * Creates a new instance of {@code FormEvent} with event type set to {@code EVENT_FORM_PERSISTED}.\n     */\n    public static FormEvent formPersistedEvent(Form form) {\n        return new FormEvent(EVENT_FORM_PERSISTED, form);\n    }\n\n    /**\n     * Creates a new instance of {@code FormEvent} with event type set to {@code EVENT_FORM_RESET}.\n     */\n    public static FormEvent formResetEvent(Form form) {\n        return new FormEvent(EVENT_FORM_RESET, form);\n    }\n\n    private final Form form;\n\n    private FormEvent(EventType<? extends Event> eventType, Form form) {\n        super(eventType);\n        this.form = form;\n    }\n\n    public final Form getForm() {\n        return form;\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/event/GroupEvent.java",
    "content": "package com.dlsc.formsfx.model.event;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 - 2018 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.Group;\nimport javafx.event.Event;\nimport javafx.event.EventType;\n\n/**\n * Identifies events triggered by a {@code Group}.\n *\n * @author Andres Almiray\n */\npublic final class GroupEvent extends Event {\n    /**\n     * When a {@code Group} is persisted.\n     */\n    public static final EventType<GroupEvent> EVENT_GROUP_PERSISTED = new EventType<>(ANY, \"EVENT_GROUP_PERSISTED\");\n\n    /**\n     * When a {@code Group} is reset.\n     */\n    public static final EventType<GroupEvent> EVENT_GROUP_RESET = new EventType<>(ANY, \"EVENT_GROUP_RESET\");\n\n    /**\n     * Creates a new instance of {@code GroupEvent} with event type set to {@code EVENT_GROUP_PERSISTED}.\n     */\n    public static GroupEvent groupPersistedEvent(Group group) {\n        return new GroupEvent(EVENT_GROUP_PERSISTED, group);\n    }\n\n    /**\n     * Creates a new instance of {@code GroupEvent} with event type set to {@code EVENT_GROUP_RESET}.\n     */\n    public static GroupEvent groupResetEvent(Group group) {\n        return new GroupEvent(EVENT_GROUP_RESET, group);\n    }\n\n    private final Group group;\n\n    private GroupEvent(EventType<? extends Event> eventType, Group group) {\n        super(eventType);\n        this.group = group;\n    }\n\n    public final Group getGroup() {\n        return group;\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/BooleanField.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.view.controls.SimpleBooleanControl;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.util.StringConverter;\n\n/**\n * This class provides an implementation of a {@link Field} containing a\n * {@code boolean} value.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class BooleanField extends DataField<BooleanProperty, Boolean, BooleanField> {\n\n    /**\n     * The constructor of {@code BooleanField}.\n     *\n     * @param valueProperty\n     *              The property that is used to store the current valid value\n     *              of the field.\n     * @param persistentValueProperty\n     *              The property that is used to store the latest persisted\n     *              value of the field.\n     */\n    protected BooleanField(SimpleBooleanProperty valueProperty, SimpleBooleanProperty persistentValueProperty) {\n        super(valueProperty, persistentValueProperty);\n\n        stringConverter = new AbstractStringConverter<Boolean>() {\n            @Override\n            public Boolean fromString(String string) {\n                return Boolean.parseBoolean(string);\n            }\n        };\n        rendererSupplier = () -> new SimpleBooleanControl();\n\n        userInput.set(stringConverter.toString(value.getValue()));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    protected boolean validateRequired(String newValue) {\n        return !isRequired() || (isRequired() && newValue.equals(\"true\"));\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/DataField.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.event.FieldEvent;\nimport com.dlsc.formsfx.model.util.BindingMode;\nimport com.dlsc.formsfx.model.util.TranslationService;\nimport com.dlsc.formsfx.model.util.ValueTransformer;\nimport com.dlsc.formsfx.model.validators.ValidationResult;\nimport com.dlsc.formsfx.model.validators.Validator;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.Property;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.util.StringConverter;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * {@code DataField} holds a single value. This value can be represented and\n * manipulated as a {@code String}. It is stored as a concrete type.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic abstract class DataField<P extends Property, V, F extends Field<F>> extends Field<F> {\n  \n    /**\n     * Every field tracks its value in multiple ways.\n     *\n     * - The user input is bound to a specific control's input value and is a\n     *   1-to-1 representation of what the user enters.\n     * - The value is the last valid value entered by the user. This means that\n     *   the value passes the type transformation of the concrete field and all\n     *   user-defined validations.\n     * - The persistent value is the value that was last saved on the field. It\n     *   is the responsibility of the form creator to persist the field values\n     *   at the correct time.\n     */\n    protected final P value;\n    protected final P persistentValue;\n    protected final StringProperty userInput = new SimpleStringProperty(\"\");\n\n    /**\n     * Every field contains a list of validators. The validators are limited to\n     * the ones that correspond to the field's type.\n     */\n    protected final List<Validator<V>> validators = new ArrayList<>();\n\n    /**\n     * The value transformer is responsible for transforming the user input\n     * string to the specific type of the field's value.\n     * @deprecated Use DataField#stringConverter instead.\n     */\n    @Deprecated\n    ValueTransformer<V> valueTransformer;\n\n    protected StringConverter<V> stringConverter = new AbstractStringConverter<V>() {\n        @Override\n        public V fromString(String string) {\n            return null;\n        }\n    };\n\n    /**\n     * The format error is displayed when the value transformation fails.\n     *\n     * This property is translatable if a {@link TranslationService} is set on\n     * the containing form.\n     */\n    protected final StringProperty formatErrorKey = new SimpleStringProperty(\"\");\n    protected final StringProperty formatError = new SimpleStringProperty(\"\");\n\n    /**\n     * This listener updates the field when the external binding changes.\n     */\n    private final InvalidationListener externalBindingListener = (observable) -> userInput.setValue(stringConverter.toString((V) ((P) observable).getValue()));\n\n    /**\n     * Internal constructor for the {@code DataField} class. To create new\n     * elements, see the static factory methods in {@code Field}.\n     *\n     * @see Field::ofStringType\n     * @see Field::ofIntegerType\n     * @see Field::ofDoubleType\n     * @see Field::ofBooleanType\n     *\n     * @param valueProperty\n     *              The property that is used to store the current valid value\n     *              of the field.\n     * @param persistentValueProperty\n     *              The property that is used to store the latest persisted\n     *              value of the field.\n     */\n    protected DataField(P valueProperty, P persistentValueProperty) {\n        value = valueProperty;\n        persistentValue = persistentValueProperty;\n\n        // The changed property is a binding that compares the persistent value\n        // with the current value. This means that a field is marked as changed\n        // until Field::persist or Field::reset are called or the value is back\n        // to the persistent value.\n\n        changed.bind(Bindings.createBooleanBinding(() -> !stringConverter.toString((V) persistentValue.getValue()).equals(userInput.getValue()), userInput, persistentValue));\n\n        // Whenever one of the translatable elements' keys change, update the\n        // displayed value based on the new translation.\n\n        formatErrorKey.addListener((observable, oldValue, newValue) -> formatError.setValue(translationService.translate(newValue)));\n\n        // Changes to the user input are reflected in the value only if the new\n        // user input is valid.\n\n        userInput.addListener((observable, oldValue, newValue) -> {\n            if (validate()) {\n                value.setValue(stringConverter.fromString(newValue));\n            }\n        });\n    }\n\n    protected static abstract class AbstractStringConverter<V> extends StringConverter<V> {\n        @Override\n        public String toString(V object) {\n            return String.valueOf(object);\n        }\n    }\n\n    private static class StringConverterAdapter<V> extends AbstractStringConverter<V> {\n        private final ValueTransformer<V> valueTransformer;\n\n        public StringConverterAdapter(ValueTransformer<V> valueTransformer) {\n            this.valueTransformer = valueTransformer;\n        }\n\n        @Override\n        public V fromString(String string) {\n            return valueTransformer.transform(string);\n        }\n    }\n\n    /**\n     * Sets the string converter for the current field.\n     *\n     * @param newValue\n     *              The string converter that transforms the user input string to\n     *              the field's underlying value and back.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F format(StringConverter<V> newValue) {\n        stringConverter = newValue;\n        validate();\n        return (F) this;\n    }\n\n    /**\n     * Applies a new string converter that converts the entered string input\n     * to a concrete value.\n     *\n     * @param newValue\n     *              The string converter that transforms the user input string to\n     *              the field's underlying value and back.\n     * @param errorMessage\n     *              The error message to display if the transformation was\n     *              unsuccessful.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F format(StringConverter<V> newValue, String errorMessage) {\n        stringConverter = newValue;\n\n        if (isI18N()) {\n            formatErrorKey.set(errorMessage);\n        } else {\n            formatError.set(errorMessage);\n        }\n\n        validate();\n        return (F) this;\n    }\n\n    /**\n     * Sets the value transformer for the current field.\n     *\n     * @param newValue\n     *              The value transformer that parses the user input string to\n     *              the field's underlying value.\n     *\n     * @return Returns the current field to allow for chaining.\n     * @deprecated Use format(StringConverter) instead\n     */\n    @Deprecated\n    public F format(ValueTransformer<V> newValue) {\n        stringConverter = new StringConverterAdapter<>(newValue);\n        validate();\n        return (F) this;\n    }\n\n    /**\n     * Applies a new value transformer that converts the entered string input\n     * to a concrete value.\n     *\n     * @param newValue\n     *              The new value transformer. Takes a string as an input and\n     *              returns the concrete type.\n     * @param errorMessage\n     *              The error message to display if the transformation was\n     *              unsuccessful.\n     *\n     * @return Returns the current field to allow for chaining.\n     * @deprecated Use format(StringConverter, errorMessage) instead\n     */\n    @Deprecated\n    public F format(ValueTransformer<V> newValue, String errorMessage) {\n        stringConverter = new StringConverterAdapter<>(newValue);\n\n        if (isI18N()) {\n            formatErrorKey.set(errorMessage);\n        } else {\n            formatError.set(errorMessage);\n        }\n\n        validate();\n        return (F) this;\n    }\n\n    /**\n     * Adds an error message to handle formatting errors with the default\n     * value transformers.\n     *\n     * @param errorMessage\n     *              The error message to display if the transformation was\n     *              unsuccessful.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F format(String errorMessage) {\n        if (isI18N()) {\n            formatErrorKey.set(errorMessage);\n        } else {\n            formatError.set(errorMessage);\n        }\n\n        validate();\n        return (F) this;\n    }\n\n    /**\n     * Sets the list of validators for the current field. This overrides all\n     * validators that have previously been added.\n     *\n     * @param newValue\n     *              The validators that are to be used for validating this\n     *              field. Limited to validators that are able to handle the\n     *              field's underlying type.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    @SafeVarargs\n    public final F validate(Validator<V>... newValue) {\n        validators.clear();\n        Collections.addAll(validators, newValue);\n        validate();\n\n        return (F) this;\n    }\n\n    /**\n     * Binds the given property with the field.\n     *\n     * @param binding\n     *          The property to be bound with.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F bind(P binding) {\n        persistentValue.bindBidirectional(binding);\n        binding.addListener(externalBindingListener);\n\n        return (F) this;\n    }\n\n    /**\n     * Unbinds the given property with the field.\n     *\n     * @param binding\n     *          The property to be unbound with.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F unbind(P binding) {\n        persistentValue.unbindBidirectional(binding);\n        binding.removeListener(externalBindingListener);\n\n        return (F) this;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public void setBindingMode(BindingMode newValue) {\n        if (BindingMode.CONTINUOUS.equals(newValue)) {\n            value.addListener(bindingModeListener);\n        } else {\n            value.removeListener(bindingModeListener);\n        }\n    }\n\n    /**\n     * Stores the field's current value in its persistent value. This stores\n     * the user's changes in the model.\n     */\n    public void persist() {\n        if (!isValid()) {\n            return;\n        }\n\n        persistentValue.setValue(value.getValue());\n\n        fireEvent(FieldEvent.fieldPersistedEvent(this));\n    }\n\n    /**\n     * Sets the field's current value to its persistent value, thus resetting\n     * any changes made by the user.\n     */\n    public void reset() {\n        if (!hasChanged()) {\n            return;\n        }\n\n        userInput.setValue(stringConverter.toString((V) persistentValue.getValue()));\n    }\n\n    /**\n     * Validates that the new field input matches the required condition.\n     *\n     * @param newValue\n     *              The new value to check for the required state.\n     *\n     * @return Returns whether the input matches the required condition.\n     */\n    protected boolean validateRequired(String newValue) {\n        return !isRequired() || (isRequired() && !newValue.isEmpty());\n    }\n\n    /**\n     * Validates a user input based on the field's value transformer and its\n     * validation rules. Also considers the {@code required} flag. This method\n     * directly updates the {@code valid} property.\n     *\n     * @return Returns whether the user input is a valid value or not.\n     */\n    public boolean validate() {\n        String newValue = userInput.getValue();\n\n        if (!validateRequired(newValue)) {\n            if (isI18N() && !requiredErrorKey.get().isEmpty()) {\n                errorMessageKeys.setAll(requiredErrorKey.get());\n            } else if (!requiredError.get().isEmpty()) {\n                errorMessages.setAll(requiredError.get());\n            }\n\n            valid.set(false);\n            return false;\n        }\n\n        V transformedValue;\n\n        // Attempt a transformation from String to the field's underlying type.\n\n        try {\n            transformedValue = stringConverter.fromString(newValue);\n        } catch (Exception e) {\n            if (isI18N() && !formatErrorKey.get().isEmpty()) {\n                errorMessageKeys.setAll(formatErrorKey.get());\n            } else if (!formatError.get().isEmpty()) {\n                errorMessages.setAll(formatError.get());\n            }\n\n            valid.set(false);\n            return false;\n        }\n\n        // Check all validation rules and collect any error messages.\n\n        List<String> errorMessages = validators.stream()\n                .map(v -> v.validate(transformedValue))\n                .filter(r -> !r.getResult())\n                .map(ValidationResult::getErrorMessage)\n                .collect(Collectors.toList());\n\n        // Update the validation results with the current results. Listeners\n        // will handle the translation aspect.\n\n        if (isI18N()) {\n            errorMessageKeys.setAll(errorMessages);\n        } else {\n            this.errorMessages.setAll(errorMessages);\n        }\n\n        if (errorMessages.size() > 0) {\n            valid.set(false);\n            return false;\n        }\n\n        // If all above conditions have succeeded, the user input is\n        // considered valid.\n\n        valid.set(true);\n        return true;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public void translate(TranslationService service) {\n        super.translate(service);\n\n        updateElement(formatError, formatErrorKey);\n        validate();\n    }\n\n    public String getUserInput() {\n        return userInput.get();\n    }\n\n    public StringProperty userInputProperty() {\n        return userInput;\n    }\n\n    public V getValue() {\n        return (V) value.getValue();\n    }\n\n    public P valueProperty() {\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/DateField.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 - 2018 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.view.controls.SimpleDateControl;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.ObservableValue;\nimport javafx.util.converter.LocalDateStringConverter;\n\nimport java.time.LocalDate;\nimport java.time.chrono.Chronology;\nimport java.time.format.FormatStyle;\nimport java.util.Locale;\n\n/**\n * This class provides an implementation of a {@link Field} containing a {@code LocalDate} value.\n *\n * @author Tomasz Krzemiński\n */\npublic class DateField extends DataField<ObjectProperty<LocalDate>, LocalDate, DateField> {\n    /**\n     * Internal constructor for the {@code DataField} class. To create new\n     * elements, see the static factory methods in {@code Field}.\n     *\n     * @param valueProperty           The property that is used to store the current valid value\n     *                                of the field.\n     * @param persistentValueProperty The property that is used to store the latest persisted\n     * @see Field ::ofStringType\n     * @see Field ::ofIntegerType\n     * @see Field ::ofDoubleType\n     * @see Field ::ofBooleanType\n     */\n    public DateField(ObjectProperty<LocalDate> valueProperty, ObjectProperty<LocalDate> persistentValueProperty) {\n        super(valueProperty, persistentValueProperty);\n\n        Chronology chronology = Chronology.ofLocale(Locale.getDefault(Locale.Category.FORMAT));\n        stringConverter = new LocalDateStringConverter(FormatStyle.SHORT, null, chronology);\n        rendererSupplier = () -> new SimpleDateControl();\n        userInput.setValue(null);\n        userInput.setValue(stringConverter.toString((LocalDate) persistentValue.getValue()));\n    }\n\n    @Override\n    public DateField bind(ObjectProperty<LocalDate> binding) {\n        return super.bind(binding);\n\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/DoubleField.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.view.controls.SimpleDoubleControl;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\n\n/**\n * This class provides an implementation of a {@link Field} containing a\n * {@code double} value.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class DoubleField extends DataField<DoubleProperty, Double, DoubleField> {\n\n    /**\n     * The constructor of {@code DoubleField}.\n     *\n     * @param valueProperty\n     *              The property that is used to store the current valid value\n     *              of the field.\n     * @param persistentValueProperty\n     *              The property that is used to store the latest persisted\n     *              value of the field.\n     */\n    protected DoubleField(SimpleDoubleProperty valueProperty, SimpleDoubleProperty persistentValueProperty) {\n        super(valueProperty, persistentValueProperty);\n\n        stringConverter = new AbstractStringConverter<Double>() {\n            @Override\n            public Double fromString(String string) {\n                return Double.parseDouble(string);\n            }\n        };\n        rendererSupplier = () -> new SimpleDoubleControl();\n\n        userInput.set(stringConverter.toString(value.getValue()));\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/Element.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 - 2018 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.view.util.ColSpan;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ListProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleListProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\n\nimport java.util.UUID;\n\n/**\n * @author Andres Almiray\n */\npublic abstract class Element<E extends Element<E>> {\n    /**\n     * Fields can be styled using CSS through ID or class hooks.\n     */\n    protected final StringProperty id = new SimpleStringProperty(UUID.randomUUID().toString());\n    protected final ListProperty<String> styleClass = new SimpleListProperty<>(FXCollections.observableArrayList());\n    protected final IntegerProperty span = new SimpleIntegerProperty(12);\n\n    /**\n     * Sets the id property of the current field.\n     *\n     * @param newValue\n     *              The new value for the id property.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public E id(String newValue) {\n        id.set(newValue);\n        return (E) this;\n    }\n\n    /**\n     * Sets the style classes for the current field.\n     *\n     * @param newValue\n     *              The new style classes.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public E styleClass(String... newValue) {\n        styleClass.setAll(newValue);\n        return (E) this;\n    }\n\n    /**\n     * Sets the amount of columns the field takes up inside the section grid.\n     *\n     * @param newValue\n     *              The new number of columns.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public E span(int newValue) {\n        span.setValue(newValue);\n        return (E) this;\n    }\n\n    /**\n     * Sets the amount of columns the field takes up inside the section grid.\n     *\n     * @param newValue\n     *              The new span fraction.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public E span(ColSpan newValue) {\n        span.setValue(newValue.valueOf());\n        return (E) this;\n    }\n\n    public int getSpan() {\n        return span.get();\n    }\n\n    public IntegerProperty spanProperty() {\n        return span;\n    }\n\n    public String getID() {\n        return id.get();\n    }\n\n    public StringProperty idProperty() {\n        return id;\n    }\n\n    public ObservableList<String> getStyleClass() {\n        return styleClass.get();\n    }\n\n    public ListProperty<String> styleClassProperty() {\n        return styleClass;\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/Field.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.event.FieldEvent;\nimport com.dlsc.formsfx.model.util.BindingMode;\nimport com.dlsc.formsfx.model.util.TranslationService;\nimport com.dlsc.formsfx.view.controls.SimpleControl;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.property.*;\nimport javafx.collections.FXCollections;\n\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javafx.collections.ObservableList;\nimport javafx.event.EventHandler;\nimport javafx.event.EventType;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Labeled;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\n\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\n/**\n * This class provides the base implementation for a FormsFX field. It is not\n * meant to be used directly, but instead acts as a base for concrete\n * implementations.\n *\n * A field is the smallest unit of the form. It contains only the value and\n * relevant information.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic abstract class Field<F extends Field<F>> extends Element<F> implements FormElement {\n\n    /**\n     * The label acts as a description for the field. It is always visible to\n     * the user and tells them what should be entered into the field.\n     *\n     * This property is translatable if a {@link TranslationService} is set on\n     * the containing form.\n     */\n    protected final StringProperty label = new SimpleStringProperty(\"\");\n    protected final StringProperty labelKey = new SimpleStringProperty(\"\");\n\n    /**\n     * The tooltip is an extension of the label. It contains additional\n     * information about the contained data that is only contextually visible.\n     *\n     * This property is translatable if a {@link TranslationService} is set on\n     * the containing form.\n     */\n    protected final StringProperty tooltip = new SimpleStringProperty(\"\");\n    protected final StringProperty tooltipKey = new SimpleStringProperty(\"\");\n\n    /**\n     * The placeholder is only visible in an empty field. It provides a hint to\n     * the user about what should be entered into the field.\n     *\n     * This property is translatable if a {@link TranslationService} is set on\n     * the containing form.\n     */\n    protected final StringProperty placeholder = new SimpleStringProperty(\"\");\n    protected final StringProperty placeholderKey = new SimpleStringProperty(\"\");\n\n    /**\n     * Every field can be marked as {@code required} and {@code editable}. These\n     * properties can change the field's behaviour.\n     */\n    protected final StringProperty requiredErrorKey = new SimpleStringProperty(\"\");\n    protected final StringProperty requiredError = new SimpleStringProperty(\"\");\n    protected final BooleanProperty required = new SimpleBooleanProperty(false);\n    protected final BooleanProperty editable = new SimpleBooleanProperty(true);\n\n    /**\n     * The field's current state is represented by the value properties, as\n     * well as by the {@code valid} and {@code changed} flags.\n     */\n    protected final BooleanProperty valid = new SimpleBooleanProperty(true);\n    protected final BooleanProperty changed = new SimpleBooleanProperty(false);\n\n    /**\n     * Fields can be styled using CSS through ID or class hooks.\n     */\n    protected final StringProperty id = new SimpleStringProperty(UUID.randomUUID().toString());\n    protected final ListProperty<String> styleClass = new SimpleListProperty<>(FXCollections.observableArrayList());\n    protected final IntegerProperty span = new SimpleIntegerProperty(12);\n\n    /**\n     * The results of the field's validation is stored in this property. After\n     * every validation, the results are updated and reflected in this property.\n     *\n     * This property is translatable if a {@link TranslationService} is set on\n     * the containing form.\n     */\n    protected final ListProperty<String> errorMessages = new SimpleListProperty<>(FXCollections.observableArrayList());\n    protected final ListProperty<String> errorMessageKeys = new SimpleListProperty<>(FXCollections.observableArrayList());\n\n    /**\n     * Additional descriptions for the field's label and value are stored in these properties.\n     *\n     * These properties are translatable if a {@link TranslationService} is set on\n     * the containing form.\n     */\n    private Node labelDescription;\n    private Node valueDescription;\n    private final StringProperty labelDescriptionKey = new SimpleStringProperty(\"\");\n    private final StringProperty valueDescriptionKey = new SimpleStringProperty(\"\");\n\n    private static final String LABEL_DESCRIPTION_STYLE_CLASS = \"field-label-description\";\n    private static final String VALUE_DESCRIPTION_STYLE_CLASS = \"field-value-description\";\n\n    /**\n     * The translation service is passed down from the containing section. It\n     * is used to translate all translatable values of the field.\n     */\n    protected TranslationService translationService;\n\n    protected SimpleControl<F> renderer;\n    protected Supplier<SimpleControl<F>> rendererSupplier;\n\n    protected final Map<EventType<FieldEvent>,List<EventHandler<? super FieldEvent>>> eventHandlers = new ConcurrentHashMap<>();\n\n    /**\n     * With the continuous binding mode, values are always directly persisted\n     * upon any changes.\n     */\n    protected final InvalidationListener bindingModeListener = (observable) -> {\n        if (validate()) {\n            persist();\n        }\n    };\n\n    /**\n     * Internal constructor for the {@code Field} class. To create new elements,\n     * see the static factory methods in this class.\n     *\n     * @see Field::ofStringType\n     * @see Field::ofIntegerType\n     * @see Field::ofDoubleType\n     * @see Field::ofBooleanType\n     * @see Field::ofMultiSelectionType\n     * @see Field::ofSingleSelectionType\n     */\n    protected Field() {\n\n        // Whenever one of the translatable elements' keys change, update the\n        // displayed value based on the new translation.\n\n        labelKey.addListener((observable, oldValue, newValue) -> label.setValue(translationService.translate(newValue)));\n\n        tooltipKey.addListener((observable, oldValue, newValue) -> tooltip.setValue(translationService.translate(newValue)));\n\n        placeholderKey.addListener((observable, oldValue, newValue) -> placeholder.setValue(translationService.translate(newValue)));\n\n        labelDescriptionKey.addListener((observable, oldValue, newValue) -> labelDescription = asLabel(translationService.translate(newValue), LABEL_DESCRIPTION_STYLE_CLASS));\n\n        valueDescriptionKey.addListener((observable, oldValue, newValue) -> valueDescription = asLabel(translationService.translate(newValue), VALUE_DESCRIPTION_STYLE_CLASS));\n\n        requiredErrorKey.addListener((observable, oldValue, newValue) -> validate());\n\n        // Whenever the errorMessageKeys change, update the displayed\n        // label to the new translation. This maps the keys to their translated\n        // representation.\n\n        errorMessageKeys.addListener((observable, oldValue, newValue) -> errorMessages.setAll(errorMessageKeys.stream()\n                .map(s -> translationService.translate(s))\n                .collect(Collectors.toList())));\n    }\n\n    /**\n     * Creates a new {@link PasswordField} with the given default value.\n     *\n     * @param defaultValue\n     *              The initial value and persistent value of the field.\n     *\n     * @return Returns a new {@link PasswordField}.\n     */\n    public static PasswordField ofPasswordType(String defaultValue) {\n        return new PasswordField(new SimpleStringProperty(defaultValue), new SimpleStringProperty(defaultValue));\n    }\n\n    /**\n     * Creates a new {@link PasswordField} with the given property.\n     *\n     * @param binding\n     *          The property from the model to be bound with.\n     *\n     * @return Returns a new {@link PasswordField}.\n     */\n    public static PasswordField ofPasswordType(StringProperty binding) {\n        return new PasswordField(new SimpleStringProperty(binding.getValue()), new SimpleStringProperty(binding.getValue())).bind(binding);\n    }\n\n    /**\n     * Creates a new {@link StringField} with the given default value.\n     *\n     * @param defaultValue\n     *              The initial value and persistent value of the field.\n     *\n     * @return Returns a new {@link StringField}.\n     */\n    public static StringField ofStringType(String defaultValue) {\n        return new StringField(new SimpleStringProperty(defaultValue), new SimpleStringProperty(defaultValue));\n    }\n\n    /**\n     * Creates a new {@link StringField} with the given property.\n     *\n     * @param binding\n     *          The property from the model to be bound with.\n     *\n     * @return Returns a new {@link StringField}.\n     */\n    public static StringField ofStringType(StringProperty binding) {\n        return new StringField(new SimpleStringProperty(binding.getValue()), new SimpleStringProperty(binding.getValue())).bind(binding);\n    }\n\n    /**\n     * Creates a new {@link DoubleField} with the given default value.\n     *\n     * @param defaultValue\n     *              The initial value and persistent value of the field.\n     *\n     * @return Returns a new {@link DoubleField}.\n     */\n    public static DoubleField ofDoubleType(double defaultValue) {\n        return new DoubleField(new SimpleDoubleProperty(defaultValue), new SimpleDoubleProperty(defaultValue));\n    }\n\n    /**\n     * Creates a new {@link DoubleField} with the given property.\n     *\n     * @param binding\n     *          The property from the model to be bound with.\n     *\n     * @return Returns a new {@link DoubleField}.\n     */\n    public static DoubleField ofDoubleType(DoubleProperty binding) {\n        return new DoubleField(new SimpleDoubleProperty(binding.getValue()), new SimpleDoubleProperty(binding.getValue())).bind(binding);\n    }\n\n    /**\n     * Creates a new {@link IntegerField} with the given default value.\n     *\n     * @param defaultValue\n     *              The initial value and persistent value of the field.\n     *\n     * @return Returns a new {@link IntegerField}.\n     */\n    public static IntegerField ofIntegerType(int defaultValue) {\n        return new IntegerField(new SimpleIntegerProperty(defaultValue), new SimpleIntegerProperty(defaultValue));\n    }\n\n    /**\n     * Creates a new {@link IntegerField} with the given property.\n     *\n     * @param binding\n     *          The property from the model to be bound with.\n     *\n     * @return Returns a new {@link IntegerField}.\n     */\n    public static IntegerField ofIntegerType(IntegerProperty binding) {\n        return new IntegerField(new SimpleIntegerProperty(binding.getValue()), new SimpleIntegerProperty(binding.getValue())).bind(binding);\n    }\n\n    /**\n     * Creates a new {@link BooleanField} with the given default value.\n     *\n     * @param defaultValue\n     *              The initial value and persistent value of the field.\n     *\n     * @return Returns a new {@link BooleanField}.\n     */\n    public static BooleanField ofBooleanType(boolean defaultValue) {\n        return new BooleanField(new SimpleBooleanProperty(defaultValue), new SimpleBooleanProperty(defaultValue));\n    }\n\n    /**\n     * Creates a new {@link BooleanField} with the given property.\n     *\n     * @param binding\n     *          The property from the model to be bound with.\n     *\n     * @return Returns a new {@link BooleanField}.\n     */\n    public static BooleanField ofBooleanType(BooleanProperty binding) {\n        return new BooleanField(new SimpleBooleanProperty(binding.getValue()), new SimpleBooleanProperty(binding.getValue())).bind(binding);\n    }\n\n    /**\n     * Creates a new {@link MultiSelectionField} with the given items and a\n     * pre-defined selection.\n     *\n     * @param items\n     *              The list of available items on the field.\n     * @param selection\n     *              The pre-defined indices of the selected items.\n     *\n     * @return Returns a new {@link MultiSelectionField}.\n     */\n    public static <T> MultiSelectionField<T> ofMultiSelectionType(List<T> items, List<Integer> selection) {\n        return new MultiSelectionField<>(new SimpleListProperty<>(FXCollections.observableArrayList(items)), selection);\n    }\n\n    /**\n     * Creates a new {@link MultiSelectionField} with the given items and no\n     * pre-defined selection.\n     *\n     * @param items\n     *              The list of available items on the field.\n     *\n     * @return Returns a new {@link MultiSelectionField}.\n     */\n    public static <T> MultiSelectionField<T> ofMultiSelectionType(List<T> items) {\n        return new MultiSelectionField<>(new SimpleListProperty<>(FXCollections.observableArrayList(items)), new ArrayList<>());\n    }\n\n    /**\n     * Creates a new {@link MultiSelectionField} with the given properties for\n     * items and selection.\n     *\n     * @param itemsBinding\n     *          The items property to be bound with.\n     *\n     * @param selectionBinding\n     *          The selection property to be bound with.\n     *\n     * @return Returns a new {@link MultiSelectionField}.\n     */\n    public static <T> MultiSelectionField<T> ofMultiSelectionType(ListProperty<T> itemsBinding, ListProperty<T> selectionBinding) {\n        return new MultiSelectionField<>(new SimpleListProperty<>(itemsBinding.getValue()), new ArrayList<>(selectionBinding.getValue().stream().map(t -> itemsBinding.getValue().indexOf(t)).collect(Collectors.toList()))).bind(itemsBinding, selectionBinding);\n    }\n\n    /**\n     * Creates a new {@link SingleSelectionField} with the given items and a\n     * pre-defined selection.\n     *\n     * @param items\n     *              The list of available items on the field.\n     * @param selection\n     *              The pre-defined index of the selected item.\n     *\n     * @return Returns a new {@link SingleSelectionField}.\n     */\n    public static <T> SingleSelectionField<T> ofSingleSelectionType(List<T> items, int selection) {\n        return new SingleSelectionField<>(new SimpleListProperty<>(FXCollections.observableArrayList(items)), selection);\n    }\n\n    /**\n     * Creates a new {@link SingleSelectionField} with the given items and no\n     * pre-defined selection.\n     *\n     * @param items\n     *              The list of available items on the field.\n     *\n     * @return Returns a new {@link SingleSelectionField}.\n     */\n    public static <T> SingleSelectionField<T> ofSingleSelectionType(List<T> items) {\n        return new SingleSelectionField<>(new SimpleListProperty<>(FXCollections.observableArrayList(items)), -1);\n    }\n\n    /**\n     * Creates a new {@link SingleSelectionField} with the given properties for\n     * items and selection.\n     *\n     * @param itemsBinding\n     *          The items property to be bound with.\n     *\n     * @param selectionBinding\n     *          The selection property to be bound with.\n     *\n     * @return Returns a new {@link SingleSelectionField}.\n     */\n    public static <T> SingleSelectionField<T> ofSingleSelectionType(ListProperty<T> itemsBinding, ObjectProperty<T> selectionBinding) {\n        return new SingleSelectionField<>(new SimpleListProperty<>(itemsBinding.getValue()), itemsBinding.indexOf(selectionBinding.getValue())).bind(itemsBinding, selectionBinding);\n    }\n\n    /**\n     * Creates a new {@link DateField} with given default value\n     *\n     * @param defaultValue The initial value and persistent value of the field.\n     * @return Returns a new {@link DateField}.\n     */\n    public static DateField ofDate(LocalDate defaultValue) {\n        return new DateField(new SimpleObjectProperty<>(defaultValue), new SimpleObjectProperty<>(defaultValue));\n    }\n\n    /**\n     * Creates a new {@link DateField} with given property\n     *\n     * @param binding The property from the model to be bound with.\n     * @return Returns a new {@link DateField}.\n     */\n    public static DateField ofDate(ObjectProperty<LocalDate> binding) {\n        return new DateField(new SimpleObjectProperty<>(binding.getValue()), new SimpleObjectProperty<>(binding.getValue())).bind(binding);\n    }\n\n    /**\n     * Sets the required property to for the current field without providing an\n     * error message.\n     *\n     * @param newValue\n     *              The new state of the required property.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F required(boolean newValue) {\n        required.set(newValue);\n        validate();\n\n        return (F) this;\n    }\n\n    /**\n     * Sets the required property to true for the current field.\n     *\n     * @param errorMessage\n     *              The error message if the field is not filled in.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F required(String errorMessage) {\n        required.set(true);\n\n        if (isI18N()) {\n            requiredErrorKey.set(errorMessage);\n        } else {\n            requiredError.set(errorMessage);\n        }\n\n        validate();\n\n        return (F) this;\n    }\n\n    /**\n     * Sets the editable property of the current field.\n     *\n     * @param newValue\n     *              The new value for the editable property.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F editable(boolean newValue) {\n        editable.set(newValue);\n        return (F) this;\n    }\n\n    /**\n     * Sets the label property of the current field.\n     *\n     * @param newValue\n     *              The new value for the label property. This can be the label\n     *              itself or a key that is then used for translation.\n     *\n     * @see TranslationService\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F label(String newValue) {\n        if (isI18N()) {\n            labelKey.set(newValue);\n        } else {\n            label.set(newValue);\n        }\n\n        return (F) this;\n    }\n\n    /**\n     * Sets the label description property of the current field.\n     *\n     * @param newValue\n     *              The new value for the label description property.\n     *\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F labelDescription(Node newValue) {\n        labelDescription = newValue;\n        if (labelDescription != null) {\n            labelDescription.getStyleClass().add(LABEL_DESCRIPTION_STYLE_CLASS);\n        }\n\n        return (F) this;\n    }\n\n    /**\n     * Sets the label description property of the current field.\n     *\n     * @param newValue\n     *              The new value for the label description property,\n     *              wrapped with a {@code Text}.\n     *\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F labelDescription(String newValue) {\n        if(isI18N()) {\n            labelDescriptionKey.set(newValue);\n        } else if (newValue != null) {\n            labelDescription = asLabel(newValue, LABEL_DESCRIPTION_STYLE_CLASS);\n        }\n\n        return (F) this;\n    }\n\n    /**\n     * Sets the value description property of the current field.\n     *\n     * @param newValue\n     *              The new value for the field description property.\n     *\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F valueDescription(Node newValue) {\n        valueDescription = newValue;\n        if (valueDescription != null) {\n            valueDescription.getStyleClass().add(VALUE_DESCRIPTION_STYLE_CLASS);\n        }\n\n        return (F) this;\n    }\n\n    /**\n     * Sets the value description property of the current field.\n     *\n     * @param newValue\n     *              The new value for the field description property,\n     *              wrapped with a {@code Text}.\n     *\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F valueDescription(String newValue) {\n        if(isI18N()) {\n            valueDescriptionKey.set(newValue);\n        } else if (newValue != null) {\n            valueDescription = asLabel(newValue, VALUE_DESCRIPTION_STYLE_CLASS);\n        }\n\n        return (F) this;\n    }\n\n    private Label asLabel(String text, String styleClass) {\n        Label label = new Label(text);\n        label.setWrapText(true);\n        label.getStyleClass().add(styleClass);\n        return label;\n    }\n\n    /**\n     * Sets the tooltip property of the current field.\n     *\n     * @param newValue\n     *              The new value for the tooltip property. This can be the\n     *              label itself or a key that is then used for translation.\n     *\n     * @see TranslationService\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F tooltip(String newValue) {\n        if (isI18N()) {\n            tooltipKey.set(newValue);\n        } else {\n            tooltip.set(newValue);\n        }\n\n        return (F) this;\n    }\n\n    /**\n     * Sets the placeholder property of the current field.\n     *\n     * @param newValue\n     *              The new value for the placeholder property. This can be the\n     *              label itself or a key that is then used for translation.\n     *\n     * @see TranslationService\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F placeholder(String newValue) {\n        if (isI18N()) {\n            placeholderKey.set(newValue);\n        } else {\n            placeholder.set(newValue);\n        }\n\n        return (F) this;\n    }\n\n    /**\n     * Sets the control that renders this field.\n     *\n     * @param newValue\n     *              The new control to render the field.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F render(SimpleControl<F> newValue) {\n        renderer = newValue;\n        return (F) this;\n    }\n\n    /**\n     * Sets the control supplier that renders this field.\n     * The supplier is only called when required, i.e., when the GUI is created.\n     *\n     * @param newValue\n     *              The new control supplier to render the field.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public F render(Supplier<SimpleControl<F>> newValue) {\n        rendererSupplier = newValue;\n        return (F) this;\n    }\n\n    /**\n     * Activates or deactivates the {@code bindingModeListener} based on the\n     * given {@code BindingMode}.\n     *\n     * @param newValue\n     *              The new binding mode for the current field.\n     */\n    public abstract void setBindingMode(BindingMode newValue);\n\n    // abstract void persist();\n\n    // abstract void reset();\n\n    /**\n     * This internal method is called by the containing section when a new\n     * translation has been added to the form.\n     *\n     * @param newValue\n     *              The new service to use for translating translatable values.\n     */\n    public void translate(TranslationService newValue) {\n        translationService = newValue;\n\n        if (!isI18N()) {\n            return;\n        }\n\n        updateElement(label, labelKey);\n        updateElement(tooltip, tooltipKey);\n        updateElement(placeholder, placeholderKey);\n        updateElement(requiredError, requiredErrorKey);\n        updateElement(labelDescription, labelDescriptionKey);\n        updateElement(valueDescription, valueDescriptionKey);\n\n        // Validation results are handled separately as they use a somewhat\n        // more complex structure.\n\n        validate();\n    }\n\n    /**\n     * Updates a displayable field property to include translated text.\n     *\n     * @param displayProperty\n     *              The property that is displayed to the user.\n     * @param keyProperty\n     *              The internal property that holds the translation key.\n     */\n    protected void updateElement(StringProperty displayProperty, StringProperty keyProperty) {\n\n        // If the key has not yet been set that means that the translation\n        // service was added for the first time. We can simply set the key\n        // to the value stored in the display property, the listener will\n        // then take care of the translation.\n\n        if ((keyProperty.get() == null || keyProperty.get().isEmpty()) && !displayProperty.get().isEmpty()) {\n            keyProperty.setValue(displayProperty.get());\n        } else if (!keyProperty.get().isEmpty()) {\n            displayProperty.setValue(translationService.translate(keyProperty.get()));\n        }\n    }\n\n    /**\n     * Updates a displayable field property to include translated text.\n     *\n     * @param node\n     *              The property that is displayed to the user.\n     * @param keyProperty\n     *              The internal property that holds the translation key.\n     */\n    void updateElement(Node node, StringProperty keyProperty) {\n\n        // If the key has not yet been set that means that the translation\n        // service was added for the first time. We can simply set the key\n        // to the value stored in the display property, the listener will\n        // then take care of the translation.\n\n        if (!(node instanceof Labeled)) {\n            // no automatic update\n            return;\n        }\n\n        Labeled labeled = (Labeled) node;\n\n        if ((keyProperty.get() == null || keyProperty.get().isEmpty()) && !labeled.getText().isEmpty()) {\n            keyProperty.setValue(labeled.getText());\n        } else if (!keyProperty.get().isEmpty()) {\n            labeled.setText(translationService.translate(keyProperty.get()));\n        }\n    }\n\n    /**\n     * Validates a user input based on the field's value transformer and its\n     * validation rules. Also considers the {@code required} flag. This method\n     * directly updates the {@code valid} property.\n     *\n     * @return Returns whether the user input is a valid value or not.\n     */\n    protected abstract boolean validate();\n\n    public String getPlaceholder() {\n        return placeholder.get();\n    }\n\n    public StringProperty placeholderProperty() {\n        return placeholder;\n    }\n\n    public String getLabel() {\n        return label.get();\n    }\n\n    public StringProperty labelProperty() {\n        return label;\n    }\n\n    public String getTooltip() {\n        return tooltip.get();\n    }\n\n    public StringProperty tooltipProperty() {\n        return tooltip;\n    }\n\n    public boolean isValid() {\n        return valid.get();\n    }\n\n    public BooleanProperty validProperty() {\n        return valid;\n    }\n\n    public boolean hasChanged() {\n        return changed.get();\n    }\n\n    public BooleanProperty changedProperty() {\n        return changed;\n    }\n\n    public boolean isRequired() {\n        return required.get();\n    }\n\n    public BooleanProperty requiredProperty() {\n        return required;\n    }\n\n    public boolean isEditable() {\n        return editable.get();\n    }\n\n    public BooleanProperty editableProperty() {\n        return editable;\n    }\n\n    public boolean isI18N() {\n        return translationService != null;\n    }\n\n    public SimpleControl<F> getRenderer() {\n        if (renderer == null) {\n            renderer = rendererSupplier.get();\n        }\n\n        return renderer;\n    }\n\n    public List<String> getErrorMessages() {\n        return errorMessages.get();\n    }\n\n    public ListProperty<String> errorMessagesProperty() {\n        return errorMessages;\n    }\n\n    /**\n     * Registers an event handler to this field. The handler is called when the\n     * field receives an {@code Event} of the specified type during the bubbling\n     * phase of event delivery.\n     *\n     * @param eventType    the type of the events to receive by the handler\n     * @param eventHandler the handler to register\n     *\n     * @throws NullPointerException if either event type or handler are {@code null}.\n     */\n    public Field addEventHandler(EventType<FieldEvent> eventType, EventHandler<? super FieldEvent> eventHandler) {\n        if (eventType == null) {\n            throw new NullPointerException(\"Argument eventType must not be null\");\n        }\n        if (eventHandler == null) {\n            throw new NullPointerException(\"Argument eventHandler must not be null\");\n        }\n\n        this.eventHandlers.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>()).add(eventHandler);\n\n        return this;\n    }\n\n    /**\n     * Unregisters a previously registered event handler from this field. One\n     * handler might have been registered for different event types, so the\n     * caller needs to specify the particular event type from which to\n     * unregister the handler.\n     *\n     * @param eventType    the event type from which to unregister\n     * @param eventHandler the handler to unregister\n     *\n     * @throws NullPointerException if either event type or handler are {@code null}.\n     */\n    public Field removeEventHandler(EventType<FieldEvent> eventType, EventHandler<? super FieldEvent> eventHandler) {\n        if (eventType == null) {\n            throw new NullPointerException(\"Argument eventType must not be null\");\n        }\n        if (eventHandler == null) {\n            throw new NullPointerException(\"Argument eventHandler must not be null\");\n        }\n\n        List<EventHandler<? super FieldEvent>> list = this.eventHandlers.get(eventType);\n        if (list != null) {\n            list.remove(eventHandler);\n        }\n\n        return this;\n    }\n\n    protected void fireEvent(FieldEvent event) {\n        List<EventHandler<? super FieldEvent>> list = this.eventHandlers.get(event.getEventType());\n        if (list == null) {\n            return;\n        }\n        for (EventHandler<? super FieldEvent> eventHandler : list) {\n            if (!event.isConsumed()) {\n                eventHandler.handle(event);\n            }\n        }\n    }\n  \n    public Node getLabelDescription() {\n        return labelDescription;\n    }\n\n    public Node getValueDescription() {\n        return valueDescription;\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/Form.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.event.FormEvent;\nimport com.dlsc.formsfx.model.util.BindingMode;\nimport com.dlsc.formsfx.model.util.TranslationService;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.event.EventHandler;\nimport javafx.event.EventType;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.stream.Collectors;\n\n/**\n * A form is the containing unit for sections and elements and is used to bring\n * structure to form data. It also acts as a proxy to some properties of the\n * contained data, such as validity or changes.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class Form {\n\n    protected final List<Group> groups = new ArrayList<>();\n\n    /**\n     * The title acts as a description for the form.\n     *\n     * This property is translatable if a {@link TranslationService} is set.\n     */\n    protected final StringProperty title = new SimpleStringProperty(\"\");\n    protected final StringProperty titleKey = new SimpleStringProperty(\"\");\n\n    /**\n     * The form acts as a proxy for its contained sections' {@code changed}\n     * and {@code valid} properties.\n     */\n    protected final BooleanProperty valid = new SimpleBooleanProperty(true);\n    protected final BooleanProperty changed = new SimpleBooleanProperty(false);\n    protected final BooleanProperty persistable = new SimpleBooleanProperty(false);\n\n    /**\n     * A form can optionally have a translation service. This service is used to\n     * translate displayed values into multiple locales. The form registers\n     * itself as a listener on the translation service to handle locale changes.\n     *\n     * @see TranslationService\n     */\n    protected final ObjectProperty<TranslationService> translationService = new SimpleObjectProperty<>();\n    protected final Runnable localeChangeListener = this::translate;\n\n    private final Map<EventType<FormEvent>,List<EventHandler<? super FormEvent>>> eventHandlers = new ConcurrentHashMap<>();\n\n    /**\n     * Internal constructor for the {@code Form} class. To create new\n     * forms, see the static factory method in this class.\n     *\n     * @see Form::of\n     *\n     * @param groups\n     *              A varargs list of groups that are contained in this\n     *              form.\n     */\n    private Form(Group... groups) {\n        Collections.addAll(this.groups, groups);\n\n        // If any of the groups are marked as changed, the section is updated\n        // accordingly.\n\n        this.groups.forEach(s -> s.changedProperty().addListener((observable, oldValue, newValue) -> setChangedProperty()));\n\n        // If any of the groups are marked as invalid, the section is updated\n        // accordingly.\n\n        this.groups.forEach(s -> s.validProperty().addListener((observable, oldValue, newValue) -> setValidProperty()));\n\n        setValidProperty();\n        setChangedProperty();\n        setPersistableProperty();\n\n        // Whenever the title's key changes, update the displayed value based\n        // on the new translation.\n\n        titleKey.addListener((observable, oldValue, newValue) -> title.setValue(translationService.get().translate(newValue)));\n\n        // Whenever the underlying translation service changes, update all\n        // translation on the form and its contained elements.\n\n        translationService.addListener((observable, oldValue, newValue) -> translate());\n    }\n\n    /**\n     * Creates a new form containing the given sections.\n     *\n     * @param sections\n     *              The sections to be included in the form.\n     *\n     * @return Returns a new {@code Form}.\n     */\n    public static Form of(Group... sections) {\n        return new Form(sections);\n    }\n\n    /**\n     * Sets the title property of the current form.\n     *\n     * @param newValue\n     *              The new value for the title property. This can be the title\n     *              itself or a key that is then used for translation.\n     *\n     * @see TranslationService\n     *\n     * @return Returns the current form to allow for chaining.\n     */\n    public Form title(String newValue) {\n        if (isI18N()) {\n            titleKey.set(newValue);\n        } else {\n            title.set(newValue);\n        }\n\n        return this;\n    }\n\n    /**\n     * Sets the translation service property of the current form.\n     *\n     * @param newValue\n     *              The new value for the translation service property.\n     *\n     * @return Returns the current form to allow for chaining.\n     */\n    public Form i18n(TranslationService newValue) {\n        if (translationService.get() != null) {\n            translationService.get().removeListener(localeChangeListener);\n        }\n\n        translationService.setValue(newValue);\n        translationService.get().addListener(localeChangeListener);\n\n        return this;\n    }\n\n    /**\n     * Changes the way field values are bound to external properties.\n     *\n     * @see BindingMode\n     *\n     * @param newValue\n     *              The new mode for handling external bindings.\n     *\n     * @return Returns the current form to allow for chaining.\n     */\n    public Form binding(BindingMode newValue) {\n        getFields().forEach(f -> f.setBindingMode(newValue));\n        return this;\n    }\n\n    /**\n     * This internal method is used as a callback for when the translation\n     * service or its locale changes. Also applies the translation to all\n     * contained sections.\n     *\n     * @see Group::translate\n     */\n    protected void translate() {\n        TranslationService tr = translationService.get();\n\n        if (!isI18N()) {\n            return;\n        }\n\n        if (titleKey.get() == null || titleKey.get().isEmpty()) {\n            titleKey.setValue(title.get());\n        } else {\n            title.setValue(tr.translate(titleKey.get()));\n        }\n\n        groups.forEach(s -> s.translate(tr));\n    }\n\n    /**\n     * Persists the values for all elements contained in this form's groups.\n     *\n     * @see Field::reset\n     */\n    public void persist() {\n        if (!isPersistable()) {\n            return;\n        }\n\n        groups.forEach(Group::persist);\n\n        fireEvent(FormEvent.formPersistedEvent(this));\n    }\n\n    /**\n     * Resets the values for all elements contained in this form's groups.\n     *\n     * @see Field::reset\n     */\n    public void reset() {\n        if (!hasChanged()) {\n            return;\n        }\n\n        groups.forEach(Group::reset);\n\n        fireEvent(FormEvent.formResetEvent(this));\n    }\n\n    /**\n     * Sets this form's {@code changed} property based on its contained\n     * groups' changed properties.\n     */\n    protected void setChangedProperty() {\n        changed.setValue(groups.stream().anyMatch(Group::hasChanged));\n        setPersistableProperty();\n    }\n\n    /**\n     * Sets this form's {@code valid} property based on its contained groups'\n     * changed properties.\n     */\n    protected void setValidProperty() {\n        valid.setValue(groups.stream().allMatch(Group::isValid));\n        setPersistableProperty();\n    }\n\n    /**\n     * Sets this form's {@code persistable} property based on its contained\n     * groups' persistable properties.\n     */\n    protected void setPersistableProperty() {\n        persistable.setValue(groups.stream().anyMatch(Group::hasChanged) && groups.stream().allMatch(Group::isValid));\n    }\n\n    public List<Group> getGroups() {\n        return groups;\n    }\n\n    public List<Element> getElements() {\n        return groups.stream()\n                .map(Group::getElements)\n                .flatMap(List::stream)\n                .collect(Collectors.toList());\n    }\n\n    public List<Field> getFields() {\n        return groups.stream()\n            .map(Group::getElements)\n            .flatMap(List::stream)\n            .filter(e -> e instanceof Field)\n            .map(e -> (Field) e)\n            .collect(Collectors.toList());\n    }\n\n    public boolean hasChanged() {\n        return changed.get();\n    }\n\n    public BooleanProperty changedProperty() {\n        return changed;\n    }\n\n    public boolean isValid() {\n        return valid.get();\n    }\n\n    public BooleanProperty validProperty() {\n        return valid;\n    }\n\n    public boolean isPersistable() {\n        return persistable.get();\n    }\n\n    public BooleanProperty persistableProperty() {\n        return persistable;\n    }\n\n    public String getTitle() {\n        return title.get();\n    }\n\n    public StringProperty titleProperty() {\n        return title;\n    }\n\n    public boolean isI18N() {\n        return translationService.get() != null;\n    }\n\n    /**\n     * Registers an event handler to this form. The handler is called when the\n     * form receives an {@code Event} of the specified type during the bubbling\n     * phase of event delivery.\n     *\n     * @param eventType    the type of the events to receive by the handler\n     * @param eventHandler the handler to register\n     *\n     * @throws NullPointerException if either event type or handler are {@code null}.\n     */\n    public Form addEventHandler(EventType<FormEvent> eventType, EventHandler<? super FormEvent> eventHandler) {\n        if (eventType == null) {\n            throw new NullPointerException(\"Argument eventType must not be null\");\n        }\n        if (eventHandler == null) {\n            throw new NullPointerException(\"Argument eventHandler must not be null\");\n        }\n\n        this.eventHandlers.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>()).add(eventHandler);\n\n        return this;\n    }\n\n    /**\n     * Unregisters a previously registered event handler from this form. One\n     * handler might have been registered for different event types, so the\n     * caller needs to specify the particular event type from which to\n     * unregister the handler.\n     *\n     * @param eventType    the event type from which to unregister\n     * @param eventHandler the handler to unregister\n     *\n     * @throws NullPointerException if either event type or handler are {@code null}.\n     */\n    public Form removeEventHandler(EventType<FormEvent> eventType, EventHandler<? super FormEvent> eventHandler) {\n        if (eventType == null) {\n            throw new NullPointerException(\"Argument eventType must not be null\");\n        }\n        if (eventHandler == null) {\n            throw new NullPointerException(\"Argument eventHandler must not be null\");\n        }\n\n        List<EventHandler<? super FormEvent>> list = this.eventHandlers.get(eventType);\n        if (list != null) {\n            list.remove(eventHandler);\n        }\n\n        return this;\n    }\n\n    protected void fireEvent(FormEvent event) {\n        List<EventHandler<? super FormEvent>> list = this.eventHandlers.get(event.getEventType());\n        if (list == null) {\n            return;\n        }\n        for (EventHandler<? super FormEvent> eventHandler : list) {\n            if (!event.isConsumed()) {\n                eventHandler.handle(event);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/FormElement.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 - 2018 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\n/**\n * @author Andres Almiray\n */\npublic interface FormElement {\n    void persist();\n\n    void reset();\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/Group.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\n\nimport com.dlsc.formsfx.model.event.GroupEvent;\nimport com.dlsc.formsfx.model.util.TranslationService;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.event.EventHandler;\nimport javafx.event.EventType;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\n\n/**\n * A group is the intermediate unit in a form. It is used to group form\n * elements to a larger unit. It also acts as a proxy to some properties of\n * the contained data, such as validity or changes.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class Group {\n\n    protected final List<Element> elements = new ArrayList<>();\n\n    /**\n     * The group acts as a proxy for its contained elements' {@code changed}\n     * and {@code valid} properties.\n     */\n    protected final BooleanProperty valid = new SimpleBooleanProperty(true);\n    protected final BooleanProperty changed = new SimpleBooleanProperty(false);\n\n    /**\n     * The translation service is passed down from the containing form. It\n     * is used to translate all translatable values of the field.\n     */\n    protected TranslationService translationService;\n\n    private final Map<EventType<GroupEvent>,List<EventHandler<? super GroupEvent>>> eventHandlers = new ConcurrentHashMap<>();\n\n    /**\n     * Internal constructor for the {@code Group} class. To create new\n     * groups, see the static factory method in this class.\n     *\n     * @see Group::of\n     *\n     * @param elements\n     *              A varargs list of elements that are contained in this\n     *              group.\n     */\n    protected Group(Element... elements) {\n        Collections.addAll(this.elements, elements);\n\n        // If any of the elements are marked as changed, the group is updated\n        // accordingly.\n\n        this.elements.stream()\n            .filter(e -> e instanceof Field)\n            .map(e -> (Field) e)\n            .forEach(f -> f.changedProperty().addListener((observable, oldValue, newValue) -> setChangedProperty()));\n\n        // If any of the elements are marked as invalid, the group is updated\n        // accordingly.\n\n        this.elements.stream()\n            .filter(e -> e instanceof Field)\n            .map(e -> (Field) e)\n            .forEach(f -> f.validProperty().addListener((observable, oldValue, newValue) -> setValidProperty()));\n\n        setValidProperty();\n        setChangedProperty();\n    }\n\n    /**\n     * Creates a new group containing the given elements.\n     *\n     * @param elements\n     *              The elements to be included in the group.\n     *\n     * @return Returns a new {@code Group}.\n     */\n    public static Group of(Element... elements) {\n        return new Group(elements);\n    }\n\n    /**\n     * This internal method is called by the containing form when a new\n     * translation has been added to the form. Also applies the translation\n     * to all contained elements.\n     *\n     * @see Field::translate\n     *\n     * @param newValue\n     *              The new service to use for translating translatable values.\n     */\n    protected void translate(TranslationService newValue) {\n        translationService = newValue;\n\n        if (!isI18N()) {\n            return;\n        }\n\n        elements.stream()\n            .filter(e -> e instanceof Field)\n            .map(e -> (Field) e)\n            .forEach(f -> f.translate(translationService));\n    }\n\n    /**\n     * Persists the values for all contained elements.\n     * @see Field::persist\n     */\n    public void persist() {\n        if (!isValid()) {\n            return;\n        }\n\n        elements.stream()\n            .filter(e -> e instanceof FormElement)\n            .map(e -> (FormElement) e)\n            .forEach(FormElement::persist);\n\n        fireEvent(GroupEvent.groupPersistedEvent(this));\n    }\n\n    /**\n     * Resets the values for all contained elements.\n     * @see Field::reset\n     */\n    public void reset() {\n        if (!hasChanged()) {\n            return;\n        }\n\n        elements.stream()\n            .filter(e -> e instanceof FormElement)\n            .map(e -> (FormElement) e)\n            .forEach(FormElement::reset);\n    }\n\n    /**\n     * Sets this group's {@code changed} property based on its contained\n     * elements' changed properties.\n     */\n    private void setChangedProperty() {\n        changed.setValue(elements.stream()\n            .filter(e -> e instanceof Field)\n            .map(e -> (Field) e)\n            .anyMatch(Field::hasChanged));\n    }\n\n    /**\n     * Sets this group's {@code valid} property based on its contained elements'\n     * changed properties.\n     */\n    private void setValidProperty() {\n        valid.setValue(elements.stream()\n            .filter(e -> e instanceof Field)\n            .map(e -> (Field) e)\n            .allMatch(Field::isValid));\n    }\n\n    public List<Element> getElements() {\n        return elements;\n    }\n\n    public boolean hasChanged() {\n        return changed.get();\n    }\n\n    public BooleanProperty changedProperty() {\n        return changed;\n    }\n\n    public boolean isValid() {\n        return valid.get();\n    }\n\n    public BooleanProperty validProperty() {\n        return valid;\n    }\n\n    public boolean isI18N() {\n        return translationService != null;\n    }\n\n    /**\n     * Registers an event handler to this group. The handler is called when the\n     * group receives an {@code Event} of the specified type during the bubbling\n     * phase of event delivery.\n     *\n     * @param eventType    the type of the events to receive by the handler\n     * @param eventHandler the handler to register\n     *\n     * @throws NullPointerException if either event type or handler are {@code null}.\n     */\n    public Group addEventHandler(EventType<GroupEvent> eventType, EventHandler<? super GroupEvent> eventHandler) {\n        if (eventType == null) {\n            throw new NullPointerException(\"Argument eventType must not be null\");\n        }\n        if (eventHandler == null) {\n            throw new NullPointerException(\"Argument eventHandler must not be null\");\n        }\n\n        this.eventHandlers.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>()).add(eventHandler);\n\n        return this;\n    }\n\n    /**\n     * Unregisters a previously registered event handler from this group. One\n     * handler might have been registered for different event types, so the\n     * caller needs to specify the particular event type from which to\n     * unregister the handler.\n     *\n     * @param eventType    the event type from which to unregister\n     * @param eventHandler the handler to unregister\n     *\n     * @throws NullPointerException if either event type or handler are {@code null}.\n     */\n    public Group removeEventHandler(EventType<GroupEvent> eventType, EventHandler<? super GroupEvent> eventHandler) {\n        if (eventType == null) {\n            throw new NullPointerException(\"Argument eventType must not be null\");\n        }\n        if (eventHandler == null) {\n            throw new NullPointerException(\"Argument eventHandler must not be null\");\n        }\n\n        List<EventHandler<? super GroupEvent>> list = this.eventHandlers.get(eventType);\n        if (list != null) {\n            list.remove(eventHandler);\n        }\n\n        return this;\n    }\n\n    protected void fireEvent(GroupEvent event) {\n        List<EventHandler<? super GroupEvent>> list = this.eventHandlers.get(event.getEventType());\n        if (list == null) {\n            return;\n        }\n        for (EventHandler<? super GroupEvent> eventHandler : list) {\n            if (!event.isConsumed()) {\n                eventHandler.handle(event);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/IntegerField.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\n\nimport com.dlsc.formsfx.view.controls.SimpleIntegerControl;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\n\n/**\n * This class provides an implementation of a {@link Field} containing a\n * {@code integer} value.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class IntegerField extends DataField<IntegerProperty, Integer, IntegerField> {\n\n    /**\n     * The constructor of {@code IntegerField}.\n     *\n     * @param valueProperty\n     *              The property that is used to store the current valid value\n     *              of the field.\n     * @param persistentValueProperty\n     *              The property that is used to store the latest persisted\n     *              value of the field.\n     */\n    protected IntegerField(SimpleIntegerProperty valueProperty, SimpleIntegerProperty persistentValueProperty) {\n        super(valueProperty, persistentValueProperty);\n\n        stringConverter = new AbstractStringConverter<Integer>() {\n            @Override\n            public Integer fromString(String string) {\n                return Integer.parseInt(string);\n            }\n        };\n        rendererSupplier = () -> new SimpleIntegerControl();\n\n        userInput.set(stringConverter.toString(value.getValue()));\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/MultiSelectionField.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.event.FieldEvent;\nimport com.dlsc.formsfx.model.util.BindingMode;\nimport com.dlsc.formsfx.model.validators.ValidationResult;\nimport com.dlsc.formsfx.model.validators.Validator;\nimport com.dlsc.formsfx.view.controls.SimpleListViewControl;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ListProperty;\nimport javafx.beans.property.SimpleListProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\n\n/**\n * This class provides an implementation of a {@link MultiSelectionField}\n * allowing for multi-selection.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class MultiSelectionField<V> extends SelectionField<V, MultiSelectionField<V>> {\n\n    /**\n     * A {@code MultiSelectionField} can have multiple items selected. These\n     * items are stored in a {@code ListProperty}.\n     */\n    protected final ListProperty<V> persistentSelection = new SimpleListProperty<>(FXCollections.observableArrayList());\n    protected final ListProperty<V> selection = new SimpleListProperty<>(FXCollections.observableArrayList());\n\n    /**\n     * Every field contains a list of validators. The validators are limited to\n     * the ones that correspond to the field's type.\n     */\n    protected final List<Validator<ObservableList<V>>> validators = new ArrayList<>();\n\n    /**\n     * The constructor of {@code MultiSelectionField}.\n     *\n     * @param items\n     *              The property that is used to store the items of the field.\n     * @param selection\n     *              The list of indices of items that are to be selected.\n     */\n    protected MultiSelectionField(ListProperty<V> items, List<Integer> selection) {\n        super(items);\n\n        // Add items to the selection, based on their indices. This also\n        // determines the persistent selection.\n\n        selection.forEach(i -> {\n            if (i < this.items.size() && i >= 0) {\n                this.selection.add(this.items.get(i));\n            }\n        });\n\n        persistentSelection.addAll(this.selection.getValue());\n\n        // The changed property is a binding that compares the persistent\n        // selection with the current selection. This means that a field is\n        // marked as changed until Field::persist or Field::reset are called\n        // or the selection is back to the persistent selection.\n\n        changed.bind(Bindings.createBooleanBinding(() -> !persistentSelection.equals(this.selection), this.selection, persistentSelection));\n\n        // Changes to the user input are reflected in the value only if the new\n        // user input is valid.\n\n        this.selection.addListener((observable, oldValue, newValue) -> validate());\n\n        // Clear the current selection and persistent selection whenever new\n        // items are added. The selection is built back up if it is passed along\n        // with the new list of items.\n\n        items.addListener((observable, oldValue, newValue) -> {\n            this.selection.clear();\n            persistentSelection.clear();\n        });\n\n        rendererSupplier = () -> new SimpleListViewControl<>();\n    }\n\n    /**\n     * Updates the list of available items to a new list, along with a\n     * pre-defined selection.\n     *\n     * @param newValue\n     *              The new list of items.\n     * @param newSelection\n     *              The new pre-defined selection.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public MultiSelectionField<V> items(List<V> newValue, List<Integer> newSelection) {\n        items.setAll(newValue);\n\n        newSelection.forEach(i -> selection.add(items.get(i)));\n        persistentSelection.setAll(selection.getValue());\n\n        return this;\n    }\n\n    /**\n     * Updates the list of available items to a new list, without a\n     * pre-defined selection.\n     *\n     * @param newValue\n     *              The new list of items.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public MultiSelectionField<V> items(List<V> newValue) {\n        return this.items(newValue, new ArrayList<>());\n    }\n\n    /**\n     * Sets the list of validators for the current field. This overrides all\n     * validators that have previously been added.\n     *\n     * @param newValue\n     *              The validators that are to be used for validating this\n     *              field.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    @SafeVarargs\n    public final MultiSelectionField<V> validate(Validator<ObservableList<V>>... newValue) {\n        validators.clear();\n        Collections.addAll(validators, newValue);\n        validate();\n\n        return this;\n    }\n\n    /**\n     * Adds the element at the given index to the current selection.\n     *\n     * @param index\n     *              The index of the element to be selected.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public MultiSelectionField<V> select(int index) {\n        if (index < items.size() && index > -1 && !selection.contains(items.get(index))) {\n            selection.add(items.get(index));\n        }\n\n        return this;\n    }\n\n    /**\n     * Removes the element at the given index from the current selection.\n     *\n     * @param index\n     *              The index of the element to be removed.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public MultiSelectionField<V> deselect(int index) {\n        if (index < items.size() && selection.contains(items.get(index))) {\n            selection.remove(items.get(index));\n        }\n\n        return this;\n    }\n\n    /**\n     * Binds the given items and selection property with the corresponding\n     * elements.\n     *\n     * @param itemsBinding\n     *          The items property to be bound with.\n     *\n     * @param selectionBinding\n     *          The selection property to be bound with.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public MultiSelectionField<V> bind(ListProperty<V> itemsBinding, ListProperty<V> selectionBinding) {\n        items.bindBidirectional(itemsBinding);\n        selection.bindBidirectional(selectionBinding);\n\n        return this;\n    }\n\n    /**\n     * Unbinds the given items and selection property with the corresponding\n     * elements.\n     *\n     * @param itemsBinding\n     *          The items property to be unbound with.\n     *\n     * @param selectionBinding\n     *          The selection property to be unbound with.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public MultiSelectionField<V> unbind(ListProperty<V> itemsBinding, ListProperty<V> selectionBinding) {\n        items.unbindBidirectional(itemsBinding);\n        selection.unbindBidirectional(selectionBinding);\n\n        return this;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public void setBindingMode(BindingMode newValue) {\n        if (BindingMode.CONTINUOUS.equals(newValue)) {\n            selection.addListener(bindingModeListener);\n        } else {\n            selection.removeListener(bindingModeListener);\n        }\n    }\n\n    /**\n     * Stores the field's current selection in its persistent selection. This\n     * stores the user's changes in the model.\n     */\n    public void persist() {\n        if (!isValid()) {\n            return;\n        }\n\n        persistentSelection.setAll(selection.getValue());\n\n        fireEvent(FieldEvent.fieldPersistedEvent(this));\n    }\n\n    /**\n     * Sets the field's current selection to its persistent selection, thus\n     * resetting any changes made by the user.\n     */\n    public void reset() {\n        if (!hasChanged()) {\n            return;\n        }\n\n        selection.setAll(persistentSelection.getValue());\n\n        fireEvent(FieldEvent.fieldResetEvent(this));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected boolean validateRequired() {\n        return !isRequired() || (isRequired() && selection.size() > 0);\n    }\n\n    /**\n     * Validates a user input based on the field's selection and its validation\n     * rules. Also considers the {@code required} flag. This method directly\n     * updates the {@code valid} property.\n     *\n     * @return Returns whether the user selection is a valid value or not.\n     */\n    public boolean validate() {\n\n        // Check all validation rules and collect any error messages.\n\n        List<String> errorMessages = validators.stream()\n                .map(v -> v.validate(selection.getValue()))\n                .filter(r -> !r.getResult())\n                .map(ValidationResult::getErrorMessage)\n                .collect(Collectors.toList());\n\n        return super.validate(errorMessages);\n    }\n\n    public ObservableList<V> getSelection() {\n        return selection.get();\n    }\n\n    public ListProperty<V> selectionProperty() {\n        return selection;\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/NodeElement.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 - 2018 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport javafx.scene.Node;\n\n/**\n * @author Andres Almiray\n */\npublic class NodeElement<N extends Node> extends Element {\n    protected N node;\n\n    public static <T extends Node> NodeElement<T> of(T node) {\n        return new NodeElement(node);\n    }\n\n    protected NodeElement(N node) {\n        if (node == null) {\n            throw new NullPointerException(\"Node argument must not be null\");\n        }\n        this.node = node;\n    }\n\n    public N getNode() {\n        return node;\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/PasswordField.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.view.controls.SimplePasswordControl;\nimport com.dlsc.formsfx.view.controls.SimpleTextControl;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\n\n/**\n * This class provides an implementation of a {@link Field} containing a\n * {@code string} value intended for passwords.\n *\n * @author Andres Almiray\n */\npublic class PasswordField extends DataField<StringProperty, String, PasswordField> {\n    /**\n     * The constructor of {@code PasswordField}.\n     *\n     * @param valueProperty\n     *              The property that is used to store the current valid value\n     *              of the field.\n     * @param persistentValueProperty\n     *              The property that is used to store the latest persisted\n     *              value of the field.\n     */\n    protected PasswordField(SimpleStringProperty valueProperty, SimpleStringProperty persistentValueProperty) {\n        super(valueProperty, persistentValueProperty);\n\n        stringConverter = new AbstractStringConverter<String>() {\n            @Override\n            public String fromString(String string) {\n                return string;\n            }\n        };\n        rendererSupplier = () -> new SimplePasswordControl();\n\n        userInput.set(stringConverter.toString(value.getValue()));\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/Section.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.util.TranslationService;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\n\n/**\n * A section is a kind of group with more options. It can have a title and can\n * be collapsed by the user. Sections represent a more semantically heavy\n * grouping of elements, compared to groups.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class Section extends Group {\n\n    /**\n     * The title acts as a description for the group. It is always visible to\n     * the user and tells them how the contained elements are grouped.\n     *\n     * This property is translatable if a {@link TranslationService} is set on\n     * the containing form.\n     */\n    protected final StringProperty titleKey = new SimpleStringProperty(\"\");\n    protected final StringProperty title = new SimpleStringProperty(\"\");\n\n    /**\n     * A group can optionally be collapsed.\n     */\n    protected final BooleanProperty collapsed = new SimpleBooleanProperty(false);\n    /**\n     * Section is collapsible by default\n     */\n    protected final BooleanProperty collapsible = new SimpleBooleanProperty(true);\n\n    /**\n     * {@inheritDoc}\n     */\n    private Section(Element... elements) {\n        super(elements);\n\n        // Whenever the title's key changes, update the displayed value based\n        // on the new translation.\n\n        titleKey.addListener((observable, oldValue, newValue) -> title.setValue(translationService.translate(newValue)));\n    }\n\n    /**\n     * Creates a new section containing the given elements.\n     *\n     * @param elements\n     *              The elements to be included in the section.\n     *\n     * @return Returns a new {@code Section}.\n     */\n    public static Section of(Element... elements) {\n        return new Section(elements);\n    }\n\n    /**\n     * Sets the title property of the current group.\n     *\n     * @param newValue\n     *              The new value for the title property. This can be the title\n     *              itself or a key that is then used for translation.\n     *\n     * @see TranslationService\n     *\n     * @return Returns the current group to allow for chaining.\n     */\n    public Section title(String newValue) {\n        if (isI18N()) {\n            titleKey.set(newValue);\n        } else {\n            title.set(newValue);\n        }\n\n        return this;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected void translate(TranslationService newValue) {\n        translationService = newValue;\n\n        if (!isI18N()) {\n            return;\n        }\n\n        if (titleKey.get() == null || titleKey.get().isEmpty()) {\n            titleKey.setValue(title.get());\n        } else {\n            title.setValue(translationService.translate(titleKey.get()));\n        }\n\n        elements.stream()\n            .filter(e -> e instanceof Field)\n            .map(e -> (Field) e)\n            .forEach(f -> f.translate(translationService));\n    }\n\n    /**\n     * Changes the collapsed state on a section.\n     *\n     * @param newValue\n     *              The new value for the collapsed state.\n     */\n    public Section collapse(boolean newValue) {\n        collapsed.setValue(newValue);\n        return this;\n    }\n\n    public BooleanProperty collapsedProperty() {\n        return collapsed;\n    }\n\n    public boolean isCollapsed() {\n        return collapsed.get();\n    }\n\n    public String getTitle() {\n        return title.get();\n    }\n\n    public StringProperty titleProperty() {\n        return title;\n    }\n\n    /**\n     * Changes the collapsible state on a section.\n     *\n     * @param newValue\n     *              The new value for the collapsible state.\n     */\n    public Section collapsible(boolean newValue) {\n        this.collapsible.set(newValue);\n        return this;\n    }\n\n    public boolean isCollapsible() {\n        return collapsible.get();\n    }\n\n    public BooleanProperty collapsibleProperty() {\n        return collapsible;\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/SelectionField.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport javafx.beans.property.ListProperty;\nimport javafx.collections.ObservableList;\n\nimport java.util.List;\n\n/**\n * {@code SelectionField} holds a list of values. Users can select one or more\n * of these values, depending on the concrete type of the field.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic abstract class SelectionField<V, F extends SelectionField<V, F>> extends Field<F> {\n\n    /**\n     * Stores a typed list of available items on this field.\n     */\n    protected final ListProperty<V> items;\n\n    /**\n     * Internal constructor for the {@code SelectionField} class. To create new\n     * elements, see the static factory methods in {@code Field}.\n     *\n     * @see Field::ofMultiSelectionType\n     * @see Field::ofSingleSelectionType\n     *\n     * @param items\n     *              The list of available items on the field.\n     */\n    protected SelectionField(ListProperty<V> items) {\n        this.items = items;\n    }\n\n    /**\n     * Validates that the new field input matches the required condition.\n     *\n     * @return Returns whether the input matches the required condition.\n     */\n    protected abstract boolean validateRequired();\n\n    /**\n     * Validates a user input based on the field's selection and its validation\n     * rules. Also considers the {@code required} flag. This method directly\n     * updates the {@code valid} property.\n     *\n     * This method should not be called directly but instead only be used in\n     * concrete subclasses.\n     *\n     * @param errorMessages\n     *              A list of error messages based on the field's validators.\n     *\n     * @return Returns whether the user selection is a valid value or not.\n     */\n    protected boolean validate(List<String> errorMessages) {\n        if (!validateRequired()) {\n            if (isI18N() && requiredErrorKey.get() != null) {\n                this.errorMessageKeys.setAll(requiredErrorKey.get());\n            } else if (requiredError.get() != null) {\n                this.errorMessages.setAll(requiredError.get());\n            }\n\n            valid.set(false);\n            return false;\n        }\n\n        // Update the validation results with the current results. Listeners\n        // will handle the translation aspect.\n\n        if (isI18N()) {\n            errorMessageKeys.setAll(errorMessages);\n        } else {\n            this.errorMessages.setAll(errorMessages);\n        }\n\n        if (errorMessages.size() > 0) {\n            valid.set(false);\n            return false;\n        }\n\n        // If, and only if all above conditions have succeeded, the user input\n        // is considered valid.\n\n        valid.set(true);\n        return true;\n    }\n\n    public ObservableList getItems() {\n        return items.get();\n    }\n\n    public ListProperty<V> itemsProperty() {\n        return items;\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/SingleSelectionField.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.event.FieldEvent;\nimport com.dlsc.formsfx.model.util.BindingMode;\nimport com.dlsc.formsfx.model.validators.ValidationResult;\nimport com.dlsc.formsfx.model.validators.Validator;\nimport com.dlsc.formsfx.view.controls.SimpleComboBoxControl;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ListProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\n\n/**\n * This class provides an implementation of a {@link SelectionField} allowing\n * only for single selection.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class SingleSelectionField<V> extends SelectionField<V, SingleSelectionField<V>> {\n\n    /**\n     * A {@code SingleSelectionField} can only ever have one item selected.\n     * This item is stored in an {@code ObjectProperty}.\n     */\n    protected final ObjectProperty<V> persistentSelection = new SimpleObjectProperty<>();\n    protected final ObjectProperty<V> selection = new SimpleObjectProperty<>();\n\n    /**\n     * Every field contains a list of validators. The validators are limited to\n     * the ones that correspond to the field's type.\n     */\n    protected final List<Validator<V>> validators = new ArrayList<>();\n\n    /**\n     * The constructor of {@code SingleSelectionField}.\n     *\n     * @param items\n     *              The property that is used to store the items of the field.\n     * @param selection\n     *              The index of the item that is to be selected.\n     */\n    protected SingleSelectionField(ListProperty<V> items, int selection) {\n        super(items);\n\n        // Sets the initial selection, based on an index. This also determines\n        // the persistent selection.\n\n        if (selection < items.size() && selection >= 0) {\n            this.selection.set(this.items.get(selection));\n            persistentSelection.setValue(this.selection.getValue());\n        }\n\n        // The changed property is a binding that compares the persistent\n        // selection with the current selection. This means that a field is\n        // marked as changed until Field::persist or Field::reset are called\n        // or the selection is back to the persistent selection.\n\n        changed.bind(Bindings.createBooleanBinding(() -> persistentSelection.get() == null ? this.selection.get() != null : !persistentSelection.get().equals(this.selection.get()), this.selection, persistentSelection));\n\n        // Changes to the user input are reflected in the value only if the new\n        // user input is valid.\n\n        this.selection.addListener((observable, oldValue, newValue) -> validate());\n\n        // Clear the current selection and persistent selection whenever new\n        // items are added. The selection is built back up if it is passed along\n        // with the new list of items.\n\n        items.addListener((observable, oldValue, newValue) -> {\n            this.selection.setValue(null);\n            persistentSelection.setValue(null);\n        });\n\n        rendererSupplier = () -> new SimpleComboBoxControl<>();\n    }\n\n    /**\n     * Updates the list of available items to a new list, along with a\n     * pre-defined selection.\n     *\n     * @param newValue\n     *              The new list of items.\n     * @param newSelection\n     *              The new pre-defined selection.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public SingleSelectionField<V> items(List<V> newValue, int newSelection) {\n        items.setAll(newValue);\n\n        if (newSelection != -1) {\n            this.selection.setValue(items.get(newSelection));\n            this.persistentSelection.setValue(this.selection.getValue());\n        }\n\n        return this;\n    }\n\n    /**\n     * Updates the list of available items to a new list, without a\n     * pre-defined selection.\n     *\n     * @param newValue\n     *              The new list of items.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public SingleSelectionField<V> items(List<V> newValue) {\n        return this.items(newValue, -1);\n    }\n\n    /**\n     * Sets the list of validators for the current field. This overrides all\n     * validators that have previously been added.\n     *\n     * @param newValue\n     *              The validators that are to be used for validating this\n     *              field.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    @SafeVarargs\n    public final SingleSelectionField<V> validate(Validator<V>... newValue) {\n        validators.clear();\n        Collections.addAll(validators, newValue);\n        validate();\n\n        return this;\n    }\n\n    /**\n     * Sets the selection to the element at the given index.\n     *\n     * @param index\n     *              The index of the element to be selected.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public SingleSelectionField<V> select(int index) {\n        if (index == -1) {\n            selection.setValue(null);\n        } else if (index < items.size() && index > -1 && (selection.get() == null || (selection.get() != null && !selection.get().equals(items.get(index))))) {\n            selection.setValue(items.get(index));\n        }\n\n        return this;\n    }\n\n    /**\n     * Removes the selection on the current field.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public SingleSelectionField<V> deselect() {\n        if (selection.get() != null) {\n            selection.setValue(null);\n        }\n\n        return this;\n    }\n\n    /**\n     * Binds the given items and selection property with the corresponding\n     * elements.\n     *\n     * @param itemsBinding\n     *          The items property to be bound with.\n     *\n     * @param selectionBinding\n     *          The selection property to be bound with.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public SingleSelectionField<V> bind(ListProperty<V> itemsBinding, ObjectProperty<V> selectionBinding) {\n        items.bindBidirectional(itemsBinding);\n        selection.bindBidirectional(selectionBinding);\n\n        return this;\n    }\n\n    /**\n     * Unbinds the given items and selection property with the corresponding\n     * elements.\n     *\n     * @param itemsBinding\n     *          The items property to be unbound with.\n     *\n     * @param selectionBinding\n     *          The selection property to be unbound with.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public SingleSelectionField<V> unbind(ListProperty<V> itemsBinding, ObjectProperty<V> selectionBinding) {\n        items.unbindBidirectional(itemsBinding);\n        selection.unbindBidirectional(selectionBinding);\n\n        return this;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public void setBindingMode(BindingMode newValue) {\n        if (BindingMode.CONTINUOUS.equals(newValue)) {\n            selection.addListener(bindingModeListener);\n        } else {\n            selection.removeListener(bindingModeListener);\n        }\n    }\n\n    /**\n     * Stores the field's current value in its persistent value. This stores\n     * the user's changes in the model.\n     */\n    public void persist() {\n        if (!isValid()) {\n            return;\n        }\n\n        persistentSelection.setValue(selection.getValue());\n\n        fireEvent(FieldEvent.fieldPersistedEvent(this));\n    }\n\n    /**\n     * Sets the field's current value to its persistent value, thus resetting\n     * any changes made by the user.\n     */\n    public void reset() {\n        if (!hasChanged()) {\n            return;\n        }\n\n        selection.setValue(persistentSelection.getValue());\n\n        fireEvent(FieldEvent.fieldResetEvent(this));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    protected boolean validateRequired() {\n        return !isRequired() || (isRequired() && selection.get() != null);\n    }\n\n    /**\n     * Validates a user input based on the field's selection and its validation\n     * rules. Also considers the {@code required} flag. This method directly\n     * updates the {@code valid} property.\n     *\n     * @return Returns whether the user selection is a valid value or not.\n     */\n    public boolean validate() {\n\n        // Check all validation rules and collect any error messages.\n\n        List<String> errorMessages = validators.stream()\n                .map(v -> v.validate(selection.getValue()))\n                .filter(r -> !r.getResult())\n                .map(ValidationResult::getErrorMessage)\n                .collect(Collectors.toList());\n\n        return super.validate(errorMessages);\n    }\n\n    public V getSelection() {\n        return selection.get();\n    }\n\n    public ObjectProperty<V> selectionProperty() {\n        return selection;\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/StringField.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.view.controls.SimpleTextControl;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\n\n/**\n * This class provides an implementation of a {@link Field} containing a\n * {@code string} value.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class StringField extends DataField<StringProperty, String, StringField> {\n\n    protected final BooleanProperty multiline = new SimpleBooleanProperty(false);\n\n    /**\n     * The constructor of {@code StringField}.\n     *\n     * @param valueProperty\n     *              The property that is used to store the current valid value\n     *              of the field.\n     * @param persistentValueProperty\n     *              The property that is used to store the latest persisted\n     *              value of the field.\n     */\n    protected StringField(SimpleStringProperty valueProperty, SimpleStringProperty persistentValueProperty) {\n        super(valueProperty, persistentValueProperty);\n\n        stringConverter = new AbstractStringConverter<String>() {\n            @Override\n            public String fromString(String string) {\n                return string;\n            }\n        };\n        rendererSupplier = () -> new SimpleTextControl();\n\n        userInput.set(stringConverter.toString(value.getValue()));\n    }\n\n    /**\n     * Sets whether the field is considered to be multiline or not.\n     *\n     * @param newValue\n     *              The new value for the multiline property.\n     *\n     * @return Returns the current field to allow for chaining.\n     */\n    public StringField multiline(boolean newValue) {\n        multiline.setValue(newValue);\n        return this;\n    }\n\n    public boolean isMultiline() {\n        return multiline.get();\n    }\n\n    public BooleanProperty multilineProperty() {\n        return multiline;\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/util/BindingMode.java",
    "content": "package com.dlsc.formsfx.model.util;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\n/**\n * Contains constants for the different ways to handle value bindings.\n * {@code CONTINUOUS} persists field values upon any change, while\n * {@code PERSISTENT} only persists values when explicitly requested.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic enum BindingMode {\n\n    CONTINUOUS,\n    PERSISTENT\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/util/ResourceBundleService.java",
    "content": "package com.dlsc.formsfx.model.util;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport java.util.ResourceBundle;\n\n/**\n * The ResourceBundleService is a concrete implementation of a\n * {@link TranslationService} and uses ResourceBundles to perform translations.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class ResourceBundleService extends TranslationService {\n\n    private ResourceBundle rb;\n\n    public ResourceBundleService(ResourceBundle rb) {\n        this.rb = rb;\n    }\n\n    /**\n     * Change the resource bundle to use for this service. Notifies all\n     * listeners of the locale change.\n     *\n     * @param newValue\n     *              The new resource bundle to use for translations.\n     */\n    public void changeLocale(ResourceBundle newValue) {\n        if (newValue.equals(rb)) {\n            return;\n        }\n\n        rb = newValue;\n        notifyListeners();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public String translate(String key) {\n        return rb.getString(key);\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/util/TranslationService.java",
    "content": "package com.dlsc.formsfx.model.util;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * A general purpose translation service that is used to translate values into\n * multiple locales based on keys. A concrete sample implementation is provided\n * in the {@link ResourceBundleService}.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic abstract class TranslationService {\n\n    private List<Runnable> listeners = new ArrayList<>();\n\n    /**\n     * Looks up a key in the translation service and returns the translate string.\n     *\n     * @param key\n     *              The key to use for the lookup.\n     *\n     * @return The translated string.\n     */\n    public abstract String translate(String key);\n\n    public void addListener(Runnable listener) {\n        listeners.add(listener);\n    }\n\n    public void removeListener(Runnable listener) {\n        listeners.remove(listener);\n    }\n\n    /**\n     * Notifies all listeners of a locale change. Concrete implementations must\n     * call this method after every locale change.\n     */\n    protected void notifyListeners() {\n        listeners.forEach(Runnable::run);\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/util/ValueTransformer.java",
    "content": "package com.dlsc.formsfx.model.util;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\n/**\n * A value transformer takes a string as an input and returns a parsed type.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n * @deprecated Use {@code StringConverter} instead.\n * @see javafx.util.StringConverter\n */\n@Deprecated\npublic interface ValueTransformer<T> {\n\n    T transform(String input);\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/CustomValidator.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport java.util.function.Predicate;\n\n/**\n * A custom validator implementation of the root validator. This validator\n * takes a generic object as an input parameter and allows users to perform\n * any kind of validation.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class CustomValidator<T> extends RootValidator<T> {\n\n    private Predicate<T> callback;\n\n    protected CustomValidator(Predicate<T> callback, String errorMessage) {\n        super(errorMessage);\n        this.callback = callback;\n    }\n\n    public static <E> CustomValidator<E> forPredicate(Predicate<E> callback, String errorMessage) {\n        return new CustomValidator<>(callback, errorMessage);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public ValidationResult validate(T input) {\n        return createResult(callback.test(input));\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/DoubleRangeValidator.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\n/**\n * A DoubleRangeValidator checks if a double value is between a minimum and a\n * maximum value.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class DoubleRangeValidator extends CustomValidator<Double> {\n\n    private DoubleRangeValidator(double min, double max, String errorMessage) {\n        super(input -> input >= min && input <= max, errorMessage);\n    }\n\n    /**\n     * Creates a DoubleRangeValidator with given lower and upper bounds.\n     *\n     * @param min\n     *              The lower bound for the validation.\n     * @param max\n     *              The upper bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @throws IllegalArgumentException\n     *              Thrown if the maximum is not larger than or equal to the\n     *              minimum.\n     *\n     * @return Returns a new DoubleRangeValidator.\n     */\n    public static DoubleRangeValidator between(double min, double max, String errorMessage) {\n        if (min > max) {\n            throw new IllegalArgumentException(\"Minimum must not be larger than maximum.\");\n        }\n\n        return new DoubleRangeValidator(min, max, errorMessage);\n    }\n\n    /**\n     * Creates a DoubleRangeValidator with a given lower bound.\n     *\n     * @param min\n     *              The lower bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @return Returns a new DoubleRangeValidator.\n     */\n    public static DoubleRangeValidator atLeast(double min, String errorMessage) {\n        return new DoubleRangeValidator(min, Double.MAX_VALUE, errorMessage);\n    }\n\n    /**\n     * Creates a DoubleRangeValidator with a given upper bound.\n     *\n     * @param max\n     *              The upper bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @return Returns a new DoubleRangeValidator.\n     */\n    public static DoubleRangeValidator upTo(double max, String errorMessage) {\n        return new DoubleRangeValidator(Double.MIN_VALUE, max, errorMessage);\n    }\n\n    /**\n     * Creates a DoubleRangeValidator with a given lower and upper bound, which\n     * are equal.\n     *\n     * @param value\n     *              The lower and upper bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @return Returns a new DoubleRangeValidator.\n     */\n    public static DoubleRangeValidator exactly(double value, String errorMessage) {\n        return new DoubleRangeValidator(value, value, errorMessage);\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/IntegerRangeValidator.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\n/**\n * A IntegerRangeValidator checks if an integer value is between a minimum and a\n * maximum value.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class IntegerRangeValidator extends CustomValidator<Integer> {\n\n    private IntegerRangeValidator(int min, int max, String errorMessage) {\n        super(input -> input >= min && input <= max, errorMessage);\n    }\n\n    /**\n     * Creates an IntegerRangeValidator with given lower and upper bounds.\n     *\n     * @param min\n     *              The lower bound for the validation.\n     * @param max\n     *              The upper bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @throws IllegalArgumentException\n     *              Thrown if the maximum is not larger than or equal to the\n     *              minimum.\n     *\n     * @return Returns a new IntegerRangeValidator.\n     */\n    public static IntegerRangeValidator between(int min, int max, String errorMessage) {\n        if (min > max) {\n            throw new IllegalArgumentException(\"Minimum must not be larger than maximum.\");\n        }\n\n        return new IntegerRangeValidator(min, max, errorMessage);\n    }\n\n    /**\n     * Creates an IntegerRangeValidator with a given lower bound.\n     *\n     * @param min\n     *              The lower bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @return Returns a new IntegerRangeValidator.\n     */\n    public static IntegerRangeValidator atLeast(int min, String errorMessage) {\n        return new IntegerRangeValidator(min, Integer.MAX_VALUE, errorMessage);\n    }\n\n    /**\n     * Creates an IntegerRangeValidator with a given upper bound.\n     *\n     * @param max\n     *              The upper bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @return Returns a new IntegerRangeValidator.\n     */\n    public static IntegerRangeValidator upTo(int max, String errorMessage) {\n        return new IntegerRangeValidator(Integer.MIN_VALUE, max, errorMessage);\n    }\n\n    /**\n     * Creates a IntegerRangeValidator with a given lower and upper bound,\n     * which are equal.\n     *\n     * @param value\n     *              The lower and upper bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @return Returns a new IntegerRangeValidator.\n     */\n    public static IntegerRangeValidator exactly(int value, String errorMessage) {\n        return new IntegerRangeValidator(value, value, errorMessage);\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/RegexValidator.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport java.util.regex.Pattern;\nimport java.util.regex.PatternSyntaxException;\n\n/**\n * A RegexValidator checks if a given input matches a regular expression.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class RegexValidator extends RootValidator<String> {\n\n    private Pattern pattern;\n\n    private RegexValidator(String pattern, String errorMessage) throws PatternSyntaxException {\n        super(errorMessage);\n        this.pattern = Pattern.compile(pattern);\n    }\n\n    /**\n     * Creates a RegexValidator with a custom pattern.\n     *\n     * @param pattern\n     *              The pattern to use for the validation. Must be a valid\n     *              RegEx.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @throws PatternSyntaxException\n     *              Thrown if the given pattern is not a valid RegEx.\n     *\n     * @return Returns a new RegexValidator.\n     */\n    public static RegexValidator forPattern(String pattern, String errorMessage) {\n        return new RegexValidator(pattern, errorMessage);\n    }\n\n    /**\n     * Creates a RegexValidator for email addresses.\n     *\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @throws PatternSyntaxException\n     *              Thrown if the given pattern is not a valid RegEx.\n     *\n     * @return Returns a new RegexValidator.\n     */\n    public static RegexValidator forEmail(String errorMessage) {\n        return new RegexValidator(\"^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\\\"(?:[\\\\x01-\\\\x08\\\\x0b\\\\x0c\\\\x0e-\\\\x1f\\\\x21\\\\x23-\\\\x5b\\\\x5d-\\\\x7f]|\\\\\\\\[\\\\x01-\\\\x09\\\\x0b\\\\x0c\\\\x0e-\\\\x7f])*\\\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\\\x01-\\\\x08\\\\x0b\\\\x0c\\\\x0e-\\\\x1f\\\\x21-\\\\x5a\\\\x53-\\\\x7f]|\\\\\\\\[\\\\x01-\\\\x09\\\\x0b\\\\x0c\\\\x0e-\\\\x7f])+)\\\\])$\", errorMessage);\n    }\n\n    /**\n     * Creates a RegexValidator for URLs.\n     *\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @throws PatternSyntaxException\n     *              Thrown if the given pattern is not a valid RegEx.\n     *\n     * @return Returns a new RegexValidator.\n     */\n    public static RegexValidator forURL(String errorMessage) {\n        return new RegexValidator(\"(https?:\\\\/\\\\/(?:www\\\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\\\.[^\\\\s]{2,}|www\\\\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\\\.[^\\\\s]{2,}|https?:\\\\/\\\\/(?:www\\\\.|(?!www))[a-zA-Z0-9]\\\\.[^\\\\s]{2,}|www\\\\.[a-zA-Z0-9]\\\\.[^\\\\s]{2,})\", errorMessage);\n    }\n\n    /**\n     * Creates a RegexValidator for alphanumeric inputs.\n     *\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @throws PatternSyntaxException\n     *              Thrown if the given pattern is not a valid RegEx.\n     *\n     * @return Returns a new RegexValidator.\n     */\n    public static RegexValidator forAlphaNumeric(String errorMessage) {\n        return new RegexValidator(\"^[a-zA-Z0-9]*$\", errorMessage);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public ValidationResult validate(String input) {\n        return createResult(pattern.matcher(input).matches());\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/RootValidator.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\n/**\n * The RootValidator contains helper methods for concrete validator implementations.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\nabstract class RootValidator<T> implements Validator<T> {\n\n    private String errorMessage;\n\n    protected RootValidator(String errorMessage) {\n        this.errorMessage = errorMessage;\n    }\n\n    /**\n     * Creates a {@link ValidationResult} based on the validation result.\n     *\n     * @param result\n     *              The result of the validation.\n     *\n     * @return Returns a new ValidationResult containing result and message.\n     */\n    protected ValidationResult createResult(boolean result) {\n        return new ValidationResult(result, errorMessage);\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/SelectionLengthValidator.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport javafx.collections.ObservableList;\n\n/**\n * A SelectionLengthValidator checks if a selection list's length is between a\n * minimum and a maximum value.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class SelectionLengthValidator<E> extends CustomValidator<ObservableList<E>> {\n\n    private SelectionLengthValidator(int min, int max, String errorMessage) {\n        super(input -> input.size() >= min && input.size() <= max, errorMessage);\n    }\n\n    /**\n     * Creates an SelectionLengthValidator with given lower and upper bounds.\n     *\n     * @param min\n     *              The lower bound for the validation.\n     * @param max\n     *              The upper bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @throws IllegalArgumentException\n     *              Thrown if the minimum is a negative number.\n     *\n     * @return Returns a new SelectionLengthValidator.\n     */\n    public static <T> SelectionLengthValidator<T> between(int min, int max, String errorMessage) {\n        if (min < 0) {\n            throw new IllegalArgumentException(\"Minimum string length cannot be negative.\");\n        } else if (min > max) {\n            throw new IllegalArgumentException(\"Minimum must not be larger than maximum.\");\n        }\n\n        return new SelectionLengthValidator<>(min, max, errorMessage);\n    }\n\n    /**\n     * Creates an SelectionLengthValidator with a given lower bound.\n     *\n     * @param min\n     *              The lower bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @throws IllegalArgumentException\n     *              Thrown if the minimum is a negative number.\n     *\n     * @return Returns a new SelectionLengthValidator.\n     */\n    public static <T> SelectionLengthValidator<T> atLeast(int min, String errorMessage) {\n        if (min < 0) {\n            throw new IllegalArgumentException(\"Minimum string length cannot be negative.\");\n        }\n\n        return new SelectionLengthValidator<>(min, Integer.MAX_VALUE, errorMessage);\n    }\n\n    /**\n     * Creates an SelectionLengthValidator with a given upper bound.\n     *\n     * @param max\n     *              The upper bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @return Returns a new SelectionLengthValidator.\n     */\n    public static <T> SelectionLengthValidator<T> upTo(int max, String errorMessage) {\n        return new SelectionLengthValidator<>(0, max, errorMessage);\n    }\n\n    /**\n     * Creates a SelectionLengthValidator with a given lower and upper bound,\n     * which are equal.\n     *\n     * @param value\n     *              The lower and upper bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @return Returns a new SelectionLengthValidator.\n     */\n    public static <T> SelectionLengthValidator<T> exactly(int value, String errorMessage) {\n        return new SelectionLengthValidator<>(value, value, errorMessage);\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/StringLengthValidator.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\n/**\n * A StringLengthValidator checks if a string value's length is between a\n * minimum and a maximum value.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class StringLengthValidator extends CustomValidator<String> {\n\n    private StringLengthValidator(int min, int max, String errorMessage) {\n        super(input -> input.length() >= min && input.length() <= max, errorMessage);\n    }\n\n    /**\n     * Creates an StringLengthValidator with given lower and upper bounds.\n     *\n     * @param min\n     *              The lower bound for the validation.\n     * @param max\n     *              The upper bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @throws IllegalArgumentException\n     *              Thrown if the minimum is a negative number.\n     *\n     * @return Returns a new StringLengthValidator.\n     */\n    public static StringLengthValidator between(int min, int max, String errorMessage) {\n        if (min < 0) {\n            throw new IllegalArgumentException(\"Minimum string length cannot be negative.\");\n        } else if (min > max) {\n            throw new IllegalArgumentException(\"Minimum must not be larger than maximum.\");\n        }\n\n        return new StringLengthValidator(min, max, errorMessage);\n    }\n\n    /**\n     * Creates an StringLengthValidator with a given lower bound.\n     *\n     * @param min\n     *              The lower bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @throws IllegalArgumentException\n     *              Thrown if the minimum is a negative number.\n     *\n     * @return Returns a new StringLengthValidator.\n     */\n    public static StringLengthValidator atLeast(int min, String errorMessage) {\n        if (min < 0) {\n            throw new IllegalArgumentException(\"Minimum string length cannot be negative.\");\n        }\n\n        return new StringLengthValidator(min, Integer.MAX_VALUE, errorMessage);\n    }\n\n    /**\n     * Creates an StringLengthValidator with a given upper bound.\n     *\n     * @param max\n     *              The upper bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @return Returns a new StringLengthValidator.\n     */\n    public static StringLengthValidator upTo(int max, String errorMessage) {\n        return new StringLengthValidator(0, max, errorMessage);\n    }\n\n    /**\n     * Creates a StringLengthValidator with a given lower and upper bound, which\n     * are equal.\n     *\n     * @param value\n     *              The lower and upper bound for the validation.\n     * @param errorMessage\n     *              The error message that is returned if the validation fails.\n     *\n     * @return Returns a new StringLengthValidator.\n     */\n    public static StringLengthValidator exactly(int value, String errorMessage) {\n        return new StringLengthValidator(value, value, errorMessage);\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/ValidationResult.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\n/**\n * A ValidationResult is the description of the result of a validation. It\n * contains the actual result, as well as possibly an error message.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class ValidationResult {\n\n    private boolean result;\n    private String errorMessage;\n\n    ValidationResult(boolean result, String errorMessage) {\n        this.result = result;\n\n        // The error message is only included in the result if the validation\n        // failed.\n\n        this.errorMessage = !result ? errorMessage : null;\n    }\n\n    public String getErrorMessage() {\n        return errorMessage;\n    }\n\n    public boolean getResult() {\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/Validator.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\n/**\n * A validator is used to validate a generic input for a specific syntax or\n * semantic.\n * \n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic interface Validator<T> {\n\n    ValidationResult validate(T input);\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleBooleanControl.java",
    "content": "package com.dlsc.formsfx.view.controls;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.BooleanField;\nimport javafx.geometry.VPos;\nimport javafx.scene.Node;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.VBox;\n\n/**\n * This class provides the base implementation for a simple control to edit\n * boolean values.\n *\n * @author Rinesch Murugathas\n * @author Sacha Schmid\n */\npublic class SimpleBooleanControl extends SimpleControl<BooleanField> {\n\n    /**\n     * - fieldLabel is the container that displays the label property of the\n     *   field.\n     * - checkBox is the editable checkbox to set user input.\n     * - container holds the checkbox so that it can be styled properly.\n     */\n    protected Label fieldLabel;\n    protected CheckBox checkBox;\n    protected VBox container;\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void initializeParts() {\n        super.initializeParts();\n\n        getStyleClass().add(\"simple-boolean-control\");\n\n        fieldLabel = new Label(field.labelProperty().getValue());\n        checkBox = new CheckBox();\n        container = new VBox();\n        checkBox.setSelected(field.getValue());\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void layoutParts() {\n        super.layoutParts();\n\n        int columns = field.getSpan();\n        container.getChildren().add(checkBox);\n\n        Node labelDescription = field.getLabelDescription();\n        Node valueDescription = field.getValueDescription();\n\n        add(fieldLabel, 0, 0, 2, 1);\n        if (labelDescription != null) {\n            GridPane.setValignment(labelDescription, VPos.TOP);\n            add(labelDescription, 0, 1, 2, 1);\n        }\n        add(container, 2, 0, columns - 2, 1);\n        if (valueDescription != null) {\n            GridPane.setValignment(valueDescription, VPos.TOP);\n            add(valueDescription, 2, 1, columns - 2, 1);\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupBindings() {\n        super.setupBindings();\n\n        checkBox.disableProperty().bind(field.editableProperty().not());\n        fieldLabel.textProperty().bind(field.labelProperty());\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupValueChangedListeners() {\n        super.setupValueChangedListeners();\n        field.userInputProperty().addListener((observable, oldValue, newValue) -> checkBox.setSelected(Boolean.parseBoolean(field.getUserInput())));\n\n        field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(checkBox));\n        field.tooltipProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(checkBox));\n\n        checkBox.focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(checkBox));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupEventHandlers() {\n        setOnMouseEntered(event -> toggleTooltip(checkBox));\n        setOnMouseExited(event -> toggleTooltip(checkBox));\n\n        checkBox.selectedProperty().addListener((observable, oldValue, newValue) -> field.userInputProperty().setValue(String.valueOf(newValue)));\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleCheckBoxControl.java",
    "content": "package com.dlsc.formsfx.view.controls;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.MultiSelectionField;\nimport javafx.geometry.VPos;\nimport javafx.scene.Node;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.VBox;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * This class provides the base implementation for a simple control to edit\n * checkbox values.\n *\n * @author Rinesch Murugathas\n * @author Sacha Schmid\n */\npublic class SimpleCheckBoxControl<V> extends SimpleControl<MultiSelectionField<V>> {\n\n    /**\n     * - The fieldLabel is the container that displays the label property of\n     *   the field.\n     * - The checkboxes list contains all the checkboxes to display.\n     * - The box is a VBox holding all box.\n     */\n    protected Label fieldLabel;\n    protected final List<CheckBox> checkboxes = new ArrayList<>();\n    protected VBox box;\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void initializeParts() {\n        super.initializeParts();\n\n        getStyleClass().add(\"simple-checkbox-control\");\n\n        fieldLabel = new Label(field.labelProperty().getValue());\n        box = new VBox();\n\n        createCheckboxes();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void layoutParts() {\n        super.layoutParts();\n\n        int columns = field.getSpan();\n\n        box.setSpacing(5);\n\n        Node labelDescription = field.getLabelDescription();\n        Node valueDescription = field.getValueDescription();\n\n        add(fieldLabel, 0, 0, 2, 1);\n        if (labelDescription != null) {\n            GridPane.setValignment(labelDescription, VPos.TOP);\n            add(labelDescription, 0, 1, 2, 1);\n        }\n        add(box, 2, 0, columns - 2, 1);\n        if (valueDescription != null) {\n            GridPane.setValignment(valueDescription, VPos.TOP);\n            add(valueDescription, 2, 1, columns - 2, 1);\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupBindings() {\n        super.setupBindings();\n\n        fieldLabel.textProperty().bind(field.labelProperty());\n        setupCheckboxBindings();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupValueChangedListeners() {\n        super.setupValueChangedListeners();\n\n        field.itemsProperty().addListener((observable, oldValue, newValue) -> {\n            createCheckboxes();\n            setupCheckboxBindings();\n            setupCheckboxEventHandlers();\n        });\n\n        field.selectionProperty().addListener((observable, oldValue, newValue) -> {\n            for (int i = 0; i < checkboxes.size(); i++) {\n                checkboxes.get(i).setSelected(field.getSelection().contains(field.getItems().get(i)));\n            }\n        });\n\n        field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(box, checkboxes.get(checkboxes.size() - 1)));\n        field.tooltipProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(box, checkboxes.get(checkboxes.size() - 1)));\n\n        for (int i = 0; i < checkboxes.size(); i++) {\n            checkboxes.get(i).focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(box, checkboxes.get(checkboxes.size() - 1)));\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupEventHandlers() {\n        box.setOnMouseEntered(event -> toggleTooltip(box, checkboxes.get(checkboxes.size() - 1)));\n        box.setOnMouseExited(event -> toggleTooltip(box, checkboxes.get(checkboxes.size() - 1)));\n        setupCheckboxEventHandlers();\n    }\n\n    /**\n     * This method creates box and adds them to checkboxes and is\n     * used when the itemsProperty on the field changes.\n     */\n    protected void createCheckboxes() {\n        box.getChildren().clear();\n        checkboxes.clear();\n\n        for (int i = 0; i < field.getItems().size(); i++) {\n            CheckBox cb = new CheckBox();\n\n            cb.setText(field.getItems().get(i).toString());\n            cb.setSelected(field.getSelection().contains(field.getItems().get(i)));\n\n            checkboxes.add(cb);\n        }\n\n        box.getChildren().addAll(checkboxes);\n    }\n\n    /**\n     * Sets up bindings for all checkboxes.\n     */\n    protected void setupCheckboxBindings() {\n        for (CheckBox checkbox : checkboxes) {\n            checkbox.disableProperty().bind(field.editableProperty().not());\n        }\n    }\n\n    /**\n     * Sets up event handlers for all checkboxes.\n     */\n    protected void setupCheckboxEventHandlers() {\n        for (int i = 0; i < checkboxes.size(); i++) {\n            final int j = i;\n\n            checkboxes.get(i).setOnAction(event -> {\n                if (checkboxes.get(j).isSelected()) {\n                    field.select(j);\n                } else {\n                    field.deselect(j);\n                }\n            });\n        }\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleComboBoxControl.java",
    "content": "package com.dlsc.formsfx.view.controls;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.SingleSelectionField;\nimport javafx.geometry.Pos;\nimport javafx.geometry.VPos;\nimport javafx.scene.Node;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.StackPane;\n\n/**\n * This class provides the base implementation for a simple control to edit\n * combobox values.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class SimpleComboBoxControl<V> extends SimpleControl<SingleSelectionField<V>> {\n\n    /**\n     * - The fieldLabel is the container that displays the label property of\n     *   the field.\n     * - The comboBox is the container that displays the values in the\n     *   ComboBox.\n     * - The readOnlyLabel is used to show the current selection in read only.\n     * - The stack is a StackPane to hold the field and read only label.\n     */\n    protected Label fieldLabel;\n    protected ComboBox<V> comboBox;\n    protected Label readOnlyLabel;\n    protected StackPane stack;\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void initializeParts() {\n        super.initializeParts();\n\n        getStyleClass().add(\"simple-select-control\");\n\n        fieldLabel = new Label(field.labelProperty().getValue());\n        readOnlyLabel = new Label();\n        stack = new StackPane();\n\n        comboBox = new ComboBox<>(field.getItems());\n\n        comboBox.getSelectionModel().select(field.getItems().indexOf(field.getSelection()));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void layoutParts() {\n        super.layoutParts();\n\n        int columns = field.getSpan();\n        readOnlyLabel.getStyleClass().add(\"read-only-label\");\n\n        comboBox.setMaxWidth(Double.MAX_VALUE);\n        comboBox.setVisibleRowCount(4);\n\n        stack.setAlignment(Pos.CENTER_LEFT);\n        stack.getChildren().addAll(comboBox, readOnlyLabel);\n\n        Node labelDescription = field.getLabelDescription();\n        Node valueDescription = field.getValueDescription();\n\n        add(fieldLabel, 0, 0, 2, 1);\n        if (labelDescription != null) {\n            GridPane.setValignment(labelDescription, VPos.TOP);\n            add(labelDescription, 0, 1, 2, 1);\n        }\n        add(stack, 2, 0, columns - 2, 1);\n        if (valueDescription != null) {\n            GridPane.setValignment(valueDescription, VPos.TOP);\n            add(valueDescription, 2, 1, columns - 2, 1);\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupBindings() {\n        super.setupBindings();\n\n        fieldLabel.textProperty().bind(field.labelProperty());\n        comboBox.visibleProperty().bind(field.editableProperty());\n        readOnlyLabel.visibleProperty().bind(field.editableProperty().not());\n        readOnlyLabel.textProperty().bind(comboBox.valueProperty().asString());\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupValueChangedListeners() {\n        super.setupValueChangedListeners();\n\n        field.itemsProperty().addListener((observable, oldValue, newValue) -> comboBox.setItems(field.getItems()));\n\n        field.selectionProperty().addListener((observable, oldValue, newValue) -> {\n            if (field.getSelection() != null) {\n                comboBox.getSelectionModel().select(field.getItems().indexOf(field.getSelection()));\n            } else {\n                comboBox.getSelectionModel().clearSelection();\n            }\n        });\n\n        field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(comboBox));\n        field.tooltipProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(comboBox));\n        comboBox.focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(comboBox));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupEventHandlers() {\n        comboBox.setOnMouseEntered(event -> toggleTooltip(comboBox));\n        comboBox.setOnMouseExited(event -> toggleTooltip(comboBox));\n\n        comboBox.valueProperty().addListener((observable, oldValue, newValue) -> field.select(comboBox.getSelectionModel().getSelectedIndex()));\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleControl.java",
    "content": "package com.dlsc.formsfx.view.controls;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.Field;\nimport com.dlsc.formsfx.view.util.ViewMixin;\nimport javafx.collections.ListChangeListener;\nimport javafx.css.PseudoClass;\nimport javafx.geometry.Point2D;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Control;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\n\n/**\n * This class provides a base for general purpose FormsFX controls.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic abstract class SimpleControl<F extends Field> extends GridPane implements ViewMixin {\n\n    /**\n     * This is the Field that is used for binding and update styling changes.\n     */\n    protected F field;\n\n    /**\n     * Tooltip to hold the error message.\n     */\n    protected Tooltip tooltip;\n\n    /**\n     * Pseudo classes for state changes.\n     */\n    protected static final PseudoClass REQUIRED_CLASS = PseudoClass.getPseudoClass(\"required\");\n    protected static final PseudoClass INVALID_CLASS = PseudoClass.getPseudoClass(\"invalid\");\n    protected static final PseudoClass CHANGED_CLASS = PseudoClass.getPseudoClass(\"changed\");\n    protected static final PseudoClass DISABLED_CLASS = PseudoClass.getPseudoClass(\"disabled\");\n\n    public void setField(F field) {\n        if (this.field != null) {\n            throw new IllegalStateException(\"Cannot change a control's field once set.\");\n        }\n\n        this.field = field;\n        init();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void initializeParts() {\n        getStyleClass().add(\"simple-control\");\n\n        tooltip = new Tooltip();\n        tooltip.getStyleClass().add(\"simple-tooltip\");\n\n        getStyleClass().addAll(field.getStyleClass());\n\n        updateStyle(INVALID_CLASS, !field.isValid());\n        updateStyle(REQUIRED_CLASS, field.isRequired());\n        updateStyle(CHANGED_CLASS, field.hasChanged());\n        updateStyle(DISABLED_CLASS, !field.isEditable());\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void layoutParts() {\n        setAlignment(Pos.CENTER_LEFT);\n\n        int columns = field.getSpan();\n\n        for (int i = 0; i < columns; i++) {\n            ColumnConstraints colConst = new ColumnConstraints();\n            colConst.setPercentWidth(100.0 / columns);\n            getColumnConstraints().add(colConst);\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public void setupBindings() {\n        idProperty().bind(field.idProperty());\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupValueChangedListeners() {\n        field.validProperty().addListener((observable, oldValue, newValue) -> updateStyle(INVALID_CLASS, !newValue));\n        field.requiredProperty().addListener((observable, oldValue, newValue) -> updateStyle(REQUIRED_CLASS, newValue));\n        field.changedProperty().addListener((observable, oldValue, newValue) -> updateStyle(CHANGED_CLASS, newValue));\n        field.editableProperty().addListener((observable, oldValue, newValue) -> updateStyle(DISABLED_CLASS, !newValue));\n\n        field.getStyleClass().addListener((ListChangeListener<String>) c -> {\n            while (c.next()) {\n                if (c.wasRemoved()) {\n                    getStyleClass().removeAll(c.getRemoved());\n                }\n\n                if (c.wasAdded()) {\n                    getStyleClass().addAll(c.getAddedSubList());\n                }\n            }\n        });\n    }\n\n    /**\n     * Sets the error message as tooltip for the matching control and shows\n     * them below the same control.\n     *\n     * @param reference\n     *          The control which gets the tooltip.\n     */\n    protected void toggleTooltip(Node reference) {\n        this.toggleTooltip(reference, (Control) reference);\n    }\n\n    /**\n     * Sets the error message as tooltip for the matching control.\n     *\n     * @param below\n     *          The control needed for positioning the tooltip.\n     * @param reference\n     *          The control which gets the tooltip.\n     */\n    protected void toggleTooltip(Node reference, Control below) {\n        String fieldTooltip = field.getTooltip();\n\n        if ((reference.isFocused() || reference.isHover()) && (!fieldTooltip.equals(\"\") || field.getErrorMessages().size() > 0)) {\n            tooltip.setText((!fieldTooltip.equals(\"\") ? fieldTooltip + \"\\n\" : \"\") + String.join(\"\\n\", field.getErrorMessages()));\n\n            if (tooltip.isShowing()) {\n                return;\n            }\n\n            Point2D p = below.localToScene(0.0, 0.0);\n\n            tooltip.show(\n                    getScene().getWindow(),\n                    p.getX() + getScene().getX() + getScene().getWindow().getX(),\n                    p.getY() + getScene().getY() + getScene().getWindow().getY() + below.getHeight() + 5\n            );\n        } else {\n            tooltip.hide();\n        }\n    }\n\n    /**\n     * Sets the css style for the defined properties.\n     *\n     * @param pseudo\n     *              The CSS pseudo class to toggle.\n     * @param newValue\n     *              Determines whether the CSS class should be applied.\n     */\n    protected void updateStyle(PseudoClass pseudo, boolean newValue) {\n        pseudoClassStateChanged(pseudo, newValue);\n    }\n\n    /**\n     * Adds a style class to the control.\n     * @param name of the style class to be added to the control\n     */\n    public void addStyleClass(String name) {\n        getStyleClass().add(name);\n    }\n\n    /**\n     * Removes a style class from the control.\n     * @param name of the class to be removed from the control\n     */\n    public void removeStyleClass(String name) {\n        getStyleClass().remove(name);\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleDateControl.java",
    "content": "package com.dlsc.formsfx.view.controls;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 - 2018 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *        http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.DateField;\nimport javafx.geometry.Pos;\nimport javafx.geometry.VPos;\nimport javafx.scene.Node;\nimport javafx.scene.control.DatePicker;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.StackPane;\n\n/**\n * This class provides a specific implementation to edit date values.\n *\n * @author Tomasz Krzemiński\n */\npublic class SimpleDateControl extends SimpleControl<DateField> {\n\n    protected Label fieldLabel;\n    protected DatePicker picker;\n    protected Label readOnlyLabel;\n    protected StackPane stack;\n\n    @Override\n    public void initializeParts() {\n        super.initializeParts();\n\n        stack = new StackPane();\n\n        fieldLabel = new Label();\n        readOnlyLabel = new Label();\n        picker = new DatePicker();\n        picker.setEditable(true);\n    }\n\n    @Override\n    public void layoutParts() {\n        super.layoutParts();\n\n        int columns = field.getSpan();\n        readOnlyLabel.getStyleClass().add(\"read-only-label\");\n\n        picker.setMaxWidth(Double.MAX_VALUE);\n\n        stack.setAlignment(Pos.CENTER_LEFT);\n        stack.getChildren().addAll(picker, readOnlyLabel);\n\n        Node labelDescription = field.getLabelDescription();\n        Node valueDescription = field.getValueDescription();\n\n        add(fieldLabel, 0, 0, 2, 1);\n        if (labelDescription != null) {\n            GridPane.setValignment(labelDescription, VPos.TOP);\n            add(labelDescription, 0, 1, 2, 1);\n        }\n        add(stack, 2, 0, columns - 2, 1);\n        if (valueDescription != null) {\n            GridPane.setValignment(valueDescription, VPos.TOP);\n            add(valueDescription, 2, 1, columns - 2, 1);\n        }\n    }\n\n    @Override\n    public void setupBindings() {\n        super.setupBindings();\n\n        picker.disableProperty().bind(field.editableProperty().not());\n        readOnlyLabel.visibleProperty().bind(field.editableProperty().not());\n\n        picker.getEditor().textProperty().bindBidirectional(field.userInputProperty());\n        fieldLabel.textProperty().bind(field.labelProperty());\n        picker.promptTextProperty().bind(field.placeholderProperty());\n        picker.managedProperty().bind(picker.visibleProperty());\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupEventHandlers() {\n        picker.getEditor().textProperty().addListener((observable, oldValue, newValue) -> field.userInputProperty().setValue(String.valueOf(newValue)));\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleDoubleControl.java",
    "content": "package com.dlsc.formsfx.view.controls;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.DoubleField;\nimport javafx.scene.control.SpinnerValueFactory;\n\n/**\n * This class provides a specific implementation to edit double values.\n *\n * @author Rinesch Murugathas\n * @author Sacha Schmid\n */\npublic class SimpleDoubleControl extends SimpleNumberControl<DoubleField, Double> {\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void initializeParts() {\n        super.initializeParts();\n\n        getStyleClass().add(\"simple-double-control\");\n        editableSpinner.setValueFactory(new SpinnerValueFactory.DoubleSpinnerValueFactory(-Double.MAX_VALUE, Double.MAX_VALUE, field.getValue()));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupValueChangedListeners() {\n        super.setupValueChangedListeners();\n\n        field.tooltipProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableSpinner));\n        field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableSpinner));\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleIntegerControl.java",
    "content": "package com.dlsc.formsfx.view.controls;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.IntegerField;\nimport javafx.scene.control.SpinnerValueFactory;\n\n/**\n * This class provides a specific implementation to edit integer values.\n *\n * @author Rinesch Murugathas\n * @author Sacha Schmid\n */\npublic class SimpleIntegerControl extends SimpleNumberControl<IntegerField, Integer> {\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void initializeParts() {\n        super.initializeParts();\n\n        getStyleClass().addAll(\"simple-integer-control\");\n        editableSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(Integer.MIN_VALUE, Integer.MAX_VALUE, field.getValue()));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupValueChangedListeners() {\n        super.setupValueChangedListeners();\n\n        field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableSpinner));\n        field.tooltipProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableSpinner));\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleListViewControl.java",
    "content": "package com.dlsc.formsfx.view.controls;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.MultiSelectionField;\nimport javafx.collections.ListChangeListener;\nimport javafx.geometry.VPos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.SelectionMode;\nimport javafx.scene.layout.GridPane;\n\n/**\n * This class provides the base implementation for a simple control to edit\n * listview values.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class SimpleListViewControl<V> extends SimpleControl<MultiSelectionField<V>> {\n\n    /**\n     * - The fieldLabel is the container that displays the label property of\n     *   the field.\n     * - The listView is the container that displays list values.\n     */\n    protected Label fieldLabel;\n    protected ListView<String> listView = new ListView<>();\n\n    /**\n     * The flag used for setting the selection properly.\n     */\n    protected boolean preventUpdate;\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void initializeParts() {\n        super.initializeParts();\n\n        getStyleClass().add(\"simple-listview-control\");\n\n        fieldLabel = new Label(field.labelProperty().getValue());\n\n        listView.setItems(field.getItems());\n        listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n\n        for (int i = 0; i < field.getItems().size(); i++) {\n            if (field.getSelection().contains(field.getItems().get(i))) {\n                listView.getSelectionModel().select(i);\n            } else {\n                listView.getSelectionModel().clearSelection(i);\n            }\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void layoutParts() {\n        super.layoutParts();\n\n        int columns = field.getSpan();\n\n        listView.setPrefHeight(200);\n\n        Node labelDescription = field.getLabelDescription();\n        Node valueDescription = field.getValueDescription();\n\n        add(fieldLabel, 0, 0, 2, 1);\n        if (labelDescription != null) {\n            GridPane.setValignment(labelDescription, VPos.TOP);\n            add(labelDescription, 0, 1, 2, 1);\n        }\n        add(listView, 2, 0, columns - 2, 1);\n        if (valueDescription != null) {\n            GridPane.setValignment(valueDescription, VPos.TOP);\n            add(valueDescription, 2, 1, columns - 2, 1);\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupBindings() {\n        super.setupBindings();\n\n        fieldLabel.textProperty().bind(field.labelProperty());\n        listView.disableProperty().bind(field.editableProperty().not());\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupValueChangedListeners() {\n        super.setupValueChangedListeners();\n\n        field.itemsProperty().addListener((observable, oldValue, newValue) -> listView.setItems(field.getItems()));\n\n        field.selectionProperty().addListener((observable, oldValue, newValue) -> {\n            if (preventUpdate) {\n                return;\n            }\n\n            preventUpdate = true;\n\n            for (int i = 0; i < field.getItems().size(); i++) {\n                if (field.getSelection().contains(field.getItems().get(i))) {\n                    listView.getSelectionModel().select(i);\n                } else {\n                    listView.getSelectionModel().clearSelection(i);\n                }\n            }\n\n            preventUpdate = false;\n        });\n\n        field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(listView));\n        field.tooltipProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(listView));\n        listView.focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(listView));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupEventHandlers() {\n        listView.setOnMouseEntered(event -> toggleTooltip(listView));\n        listView.setOnMouseExited(event -> toggleTooltip(listView));\n\n        listView.getSelectionModel().getSelectedIndices().addListener((ListChangeListener<Integer>) c -> {\n            if (preventUpdate) {\n                return;\n            }\n\n            preventUpdate = true;\n\n            for (int i = 0; i < listView.getItems().size(); i++) {\n                if (listView.getSelectionModel().getSelectedIndices().contains(i)) {\n                    field.select(i);\n                } else {\n                    field.deselect(i);\n                }\n            }\n\n            preventUpdate = false;\n        });\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleNumberControl.java",
    "content": "package com.dlsc.formsfx.view.controls;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.DataField;\nimport javafx.geometry.Pos;\nimport javafx.geometry.VPos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Spinner;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.StackPane;\n\n/**\n * This class provides the base implementation for a simple control to edit\n * numerical elements.\n *\n * @author Rinesch Murugathas\n * @author Sacha Schmid\n */\npublic abstract class SimpleNumberControl<F extends DataField, D extends Number> extends SimpleControl<F> {\n\n    /**\n     * This StackPane is needed for achieving the readonly effect by putting\n     * the {@code readOnlyLabel} over the {@code editableSpinner} on the change\n     * of the {@code visibleProperty}.\n     */\n    protected StackPane stack;\n\n    /**\n     * - The fieldLabel is the container that displays the label property of\n     *   the field.\n     * - The editableSpinner is a Spinner for setting numerical values.\n     * - The readOnlyLabel is the label to put over editableSpinner.\n     */\n    protected Label fieldLabel;\n    protected Spinner<D> editableSpinner;\n    protected Label readOnlyLabel;\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void initializeParts() {\n        super.initializeParts();\n\n        stack = new StackPane();\n\n        fieldLabel = new Label();\n        readOnlyLabel = new Label();\n        editableSpinner = new Spinner<>();\n        editableSpinner.setEditable(true);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void layoutParts() {\n        super.layoutParts();\n\n        readOnlyLabel.getStyleClass().add(\"read-only-label\");\n        stack.getChildren().addAll(editableSpinner, readOnlyLabel);\n        stack.setAlignment(Pos.CENTER_LEFT);\n\n        editableSpinner.setMaxWidth(Double.MAX_VALUE);\n\n        Node labelDescription = field.getLabelDescription();\n        Node valueDescription = field.getValueDescription();\n\n        int columns = field.getSpan();\n\n        if (columns < 3) {\n            int rowIndex = 0;\n            add(fieldLabel, 0, rowIndex++, columns, 1);\n            if (labelDescription != null) {\n                GridPane.setValignment(labelDescription, VPos.TOP);\n                add(labelDescription, 0, rowIndex++, columns, 1);\n            }\n            add(stack, 0, rowIndex++, columns, 1);\n            if (valueDescription != null) {\n                GridPane.setValignment(valueDescription, VPos.TOP);\n                add(valueDescription, 0, rowIndex, columns, 1);\n            }\n        } else {\n            add(fieldLabel, 0, 0, 2, 1);\n            if (labelDescription != null) {\n                GridPane.setValignment(labelDescription, VPos.TOP);\n                add(labelDescription, 0, 1, 2, 1);\n            }\n            add(stack, 2, 0, columns - 2, 1);\n            if (valueDescription != null) {\n                GridPane.setValignment(valueDescription, VPos.TOP);\n                add(valueDescription, 2, 1, columns - 2, 1);\n            }\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupBindings() {\n        super.setupBindings();\n\n        editableSpinner.visibleProperty().bind(field.editableProperty());\n        readOnlyLabel.visibleProperty().bind(field.editableProperty().not());\n\n        editableSpinner.getEditor().textProperty().bindBidirectional(field.userInputProperty());\n        readOnlyLabel.textProperty().bind(field.userInputProperty());\n        fieldLabel.textProperty().bind(field.labelProperty());\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupEventHandlers() {\n        editableSpinner.getEditor().setOnKeyPressed(event -> {\n            switch (event.getCode()) {\n                case UP:\n                    editableSpinner.increment(1);\n                    break;\n                case DOWN:\n                    editableSpinner.decrement(1);\n                    break;\n            }\n        });\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupValueChangedListeners() {\n        super.setupValueChangedListeners();\n        editableSpinner.focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableSpinner));\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimplePasswordControl.java",
    "content": "package com.dlsc.formsfx.view.controls;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.PasswordField;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.binding.StringBinding;\nimport javafx.geometry.Pos;\nimport javafx.geometry.VPos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.StackPane;\n\n/**\n * This class provides the base implementation for a simple control to edit\n * password values.\n *\n * @author Rinesch Murugathas\n * @author Sacha Schmid\n * @author Andres Almiray\n */\npublic class SimplePasswordControl extends SimpleControl<PasswordField> {\n\n    /**\n     * This StackPane is needed for achieving the readonly effect by putting\n     * the readOnlyLabel over the editableField on the change of the\n     * visibleProperty.\n     */\n    protected StackPane stack;\n\n    /**\n     * - The fieldLabel is the container that displays the label property of\n     *   the field.\n     * - The editableField allows users to modify the field's value.\n     * - The readOnlyLabel displays the field's value if it is not editable.\n     */\n    protected javafx.scene.control.PasswordField editableField;\n    protected Label readOnlyLabel;\n    protected Label fieldLabel;\n\n    /*\n     * Translates characters found in user input into '*'\n     */\n    protected StringBinding obfuscatedUserInputBinding;\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void initializeParts() {\n        super.initializeParts();\n\n        getStyleClass().add(\"simple-password-control\");\n\n        stack = new StackPane();\n\n        editableField = new javafx.scene.control.PasswordField();\n        editableField.setText(field.getValue());\n\n        readOnlyLabel = new Label(obfuscate(field.getValue()));\n        fieldLabel = new Label(field.labelProperty().getValue());\n        editableField.setPromptText(field.placeholderProperty().getValue());\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void layoutParts() {\n        super.layoutParts();\n\n        readOnlyLabel.getStyleClass().add(\"read-only-label\");\n\n        readOnlyLabel.setPrefHeight(26);\n\n        stack.getChildren().addAll(editableField, readOnlyLabel);\n\n        stack.setAlignment(Pos.CENTER_LEFT);\n\n        Node labelDescription = field.getLabelDescription();\n        Node valueDescription = field.getValueDescription();\n\n        int columns = field.getSpan();\n\n        if (columns < 3) {\n            int rowIndex = 0;\n            add(fieldLabel, 0, rowIndex++, columns, 1);\n            if (labelDescription != null) {\n                GridPane.setValignment(labelDescription, VPos.TOP);\n                add(labelDescription, 0, rowIndex++, columns, 1);\n            }\n            add(stack, 0, rowIndex++, columns, 1);\n            if (valueDescription != null) {\n                GridPane.setValignment(valueDescription, VPos.TOP);\n                add(valueDescription, 0, rowIndex, columns, 1);\n            }\n        } else {\n            add(fieldLabel, 0, 0, 2, 1);\n            if (labelDescription != null) {\n                GridPane.setValignment(labelDescription, VPos.TOP);\n                add(labelDescription, 0, 1, 2, 1);\n            }\n            add(stack, 2, 0, columns - 2, 1);\n            if (valueDescription != null) {\n                GridPane.setValignment(valueDescription, VPos.TOP);\n                add(valueDescription, 2, 1, columns - 2, 1);\n            }\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupBindings() {\n        super.setupBindings();\n\n        editableField.visibleProperty().bind(field.editableProperty());\n        readOnlyLabel.visibleProperty().bind(field.editableProperty().not());\n\n        editableField.textProperty().bindBidirectional(field.userInputProperty());\n        obfuscatedUserInputBinding = Bindings.createStringBinding(() -> obfuscate(field.getUserInput()), field.userInputProperty());\n        readOnlyLabel.textProperty().bind(obfuscatedUserInputBinding);\n        fieldLabel.textProperty().bind(field.labelProperty());\n        editableField.promptTextProperty().bind(field.placeholderProperty());\n        editableField.managedProperty().bind(editableField.visibleProperty());\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupValueChangedListeners() {\n        super.setupValueChangedListeners();\n\n        field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableField));\n\n        editableField.focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableField));\n    }\n\n    protected String obfuscate(String input) {\n        if (input == null) { return \"\"; }\n        int length = input.length();\n        StringBuilder b = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            b.append('*');\n        }\n        return b.toString();\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleRadioButtonControl.java",
    "content": "package com.dlsc.formsfx.view.controls;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.SingleSelectionField;\nimport javafx.geometry.VPos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.RadioButton;\nimport javafx.scene.control.ToggleGroup;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.VBox;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * This class provides the base implementation for a simple control to edit\n * radio button values.\n *\n * @author Rinesch Murugathas\n * @author Sacha Schmid\n */\npublic class SimpleRadioButtonControl<V> extends SimpleControl<SingleSelectionField<V>> {\n\n    /**\n     * - The fieldLabel is the container that displays the label property of\n     *   the field.\n     * - The radioButtons is the list of radio buttons to display.\n     * - The toggleGroup defines the group for the radio buttons.\n     * - The box is a VBox holding all radio buttons.\n     */\n    protected Label fieldLabel;\n    protected final List<RadioButton> radioButtons = new ArrayList<>();\n    protected ToggleGroup toggleGroup;\n    protected VBox box;\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void initializeParts() {\n        super.initializeParts();\n\n        getStyleClass().add(\"simple-radio-control\");\n\n        fieldLabel = new Label(field.labelProperty().getValue());\n        toggleGroup = new ToggleGroup();\n        box = new VBox();\n\n        createRadioButtons();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void layoutParts() {\n        super.layoutParts();\n\n        int columns = field.getSpan();\n\n        box.setSpacing(5);\n\n        Node labelDescription = field.getLabelDescription();\n        Node valueDescription = field.getValueDescription();\n\n        add(fieldLabel, 0, 0, 2, 1);\n        if (labelDescription != null) {\n            GridPane.setValignment(labelDescription, VPos.TOP);\n            add(labelDescription, 0, 1, 2, 1);\n        }\n        add(box, 2, 0, columns - 2, 1);\n        if (valueDescription != null) {\n            GridPane.setValignment(valueDescription, VPos.TOP);\n            add(valueDescription, 2, 1, columns - 2, 1);\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupBindings() {\n        super.setupBindings();\n\n        fieldLabel.textProperty().bind(field.labelProperty());\n        setupRadioButtonBindings();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupValueChangedListeners() {\n        super.setupValueChangedListeners();\n\n        field.itemsProperty().addListener((observable, oldValue, newValue) -> {\n            createRadioButtons();\n            setupRadioButtonBindings();\n            setupRadioButtonEventHandlers();\n        });\n\n        field.selectionProperty().addListener((observable, oldValue, newValue) -> {\n            if (field.getSelection() != null) {\n                radioButtons.get(field.getItems().indexOf(field.getSelection())).setSelected(true);\n            } else {\n                toggleGroup.getSelectedToggle().setSelected(false);\n            }\n        });\n\n        field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(box, radioButtons.get(radioButtons.size() - 1)));\n        field.tooltipProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(box, radioButtons.get(radioButtons.size() - 1)));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupEventHandlers() {\n        setOnMouseEntered(event -> toggleTooltip(box, radioButtons.get(radioButtons.size() - 1)));\n        setOnMouseExited(event -> toggleTooltip(box, radioButtons.get(radioButtons.size() - 1)));\n        setupRadioButtonEventHandlers();\n    }\n\n    /**\n     * This method creates radio buttons and adds them to radioButtons\n     * and is used when the itemsProperty on the field changes.\n     */\n    protected void createRadioButtons() {\n        box.getChildren().clear();\n        radioButtons.clear();\n\n        for (int i = 0; i < field.getItems().size(); i++) {\n            RadioButton rb = new RadioButton();\n\n            rb.setText(field.getItems().get(i).toString());\n            rb.setToggleGroup(toggleGroup);\n\n            radioButtons.add(rb);\n        }\n\n        if (field.getSelection() != null) {\n            radioButtons.get(field.getItems().indexOf(field.getSelection())).setSelected(true);\n        }\n\n        box.getChildren().addAll(radioButtons);\n    }\n\n    /**\n     * Sets up bindings for all radio buttons.\n     */\n    protected void setupRadioButtonBindings() {\n        for (RadioButton radio : radioButtons) {\n            radio.disableProperty().bind(field.editableProperty().not());\n        }\n    }\n\n    /**\n     * Sets up bindings for all radio buttons.\n     */\n    protected void setupRadioButtonEventHandlers() {\n        for (int i = 0; i < radioButtons.size(); i++) {\n            final int j = i;\n            radioButtons.get(j).setOnAction(event -> field.select(j));\n        }\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleTextControl.java",
    "content": "package com.dlsc.formsfx.view.controls;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.StringField;\nimport javafx.beans.binding.Bindings;\nimport javafx.geometry.Pos;\nimport javafx.geometry.VPos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextArea;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.StackPane;\n\n/**\n * This class provides the base implementation for a simple control to edit\n * string values.\n *\n * @author Rinesch Murugathas\n * @author Sacha Schmid\n */\npublic class SimpleTextControl extends SimpleControl<StringField> {\n\n    /**\n     * This StackPane is needed for achieving the readonly effect by putting\n     * the readOnlyLabel over the editableField on the change of the\n     * visibleProperty.\n     */\n    protected StackPane stack;\n\n    /**\n     * - The fieldLabel is the container that displays the label property of\n     *   the field.\n     * - The editableField allows users to modify the field's value.\n     * - The readOnlyLabel displays the field's value if it is not editable.\n     */\n    protected TextField editableField;\n    protected TextArea editableArea;\n    protected Label readOnlyLabel;\n    protected Label fieldLabel;\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void initializeParts() {\n        super.initializeParts();\n\n        getStyleClass().add(\"simple-text-control\");\n\n        stack = new StackPane();\n\n        editableField = new TextField(field.getValue());\n        editableArea = new TextArea(field.getValue());\n\n        readOnlyLabel = new Label(field.getValue());\n        fieldLabel = new Label(field.labelProperty().getValue());\n        editableField.setPromptText(field.placeholderProperty().getValue());\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void layoutParts() {\n        super.layoutParts();\n\n        readOnlyLabel.getStyleClass().add(\"read-only-label\");\n\n        readOnlyLabel.setPrefHeight(26);\n\n        editableArea.getStyleClass().add(\"simple-textarea\");\n        editableArea.setPrefRowCount(5);\n        editableArea.setPrefHeight(80);\n        editableArea.setWrapText(true);\n\n        if (field.isMultiline()) {\n            stack.setPrefHeight(80);\n            readOnlyLabel.setPrefHeight(80);\n        }\n\n        stack.getChildren().addAll(editableField, editableArea, readOnlyLabel);\n\n        stack.setAlignment(Pos.CENTER_LEFT);\n\n        Node labelDescription = field.getLabelDescription();\n        Node valueDescription = field.getValueDescription();\n\n        int columns = field.getSpan();\n\n        if (columns < 3) {\n            int rowIndex = 0;\n            add(fieldLabel, 0, rowIndex++, columns, 1);\n            if (labelDescription != null) {\n                GridPane.setValignment(labelDescription, VPos.TOP);\n                add(labelDescription, 0, rowIndex++, columns, 1);\n            }\n            add(stack, 0, rowIndex++, columns, 1);\n            if (valueDescription != null) {\n                GridPane.setValignment(valueDescription, VPos.TOP);\n                add(valueDescription, 0, rowIndex, columns, 1);\n            }\n        } else {\n            add(fieldLabel, 0, 0, 2, 1);\n            if (labelDescription != null) {\n                GridPane.setValignment(labelDescription, VPos.TOP);\n                add(labelDescription, 0, 1, 2, 1);\n            }\n            add(stack, 2, 0, columns - 2, 1);\n            if (valueDescription != null) {\n                GridPane.setValignment(valueDescription, VPos.TOP);\n                add(valueDescription, 2, 1, columns - 2, 1);\n            }\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupBindings() {\n        super.setupBindings();\n\n        editableArea.visibleProperty().bind(Bindings.and(field.editableProperty(),\n                                                        field.multilineProperty()));\n        editableField.visibleProperty().bind(Bindings.and(field.editableProperty(),\n                                                        field.multilineProperty().not()));\n        readOnlyLabel.visibleProperty().bind(field.editableProperty().not());\n\n        editableField.textProperty().bindBidirectional(field.userInputProperty());\n        editableArea.textProperty().bindBidirectional(field.userInputProperty());\n        readOnlyLabel.textProperty().bind(field.userInputProperty());\n        fieldLabel.textProperty().bind(field.labelProperty());\n        editableField.promptTextProperty().bind(field.placeholderProperty());\n        editableArea.promptTextProperty().bind(field.placeholderProperty());\n\n        editableArea.managedProperty().bind(editableArea.visibleProperty());\n        editableField.managedProperty().bind(editableField.visibleProperty());\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupValueChangedListeners() {\n        super.setupValueChangedListeners();\n\n        field.multilineProperty().addListener((observable, oldValue, newValue) -> {\n            stack.setPrefHeight(newValue ? 80 : 0);\n            readOnlyLabel.setPrefHeight(newValue ? 80 : 26);\n        });\n\n        field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(field.isMultiline() ? editableArea : editableField));\n\n        editableField.focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableField));\n        editableArea.focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableArea));\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/renderer/FormRenderer.java",
    "content": "package com.dlsc.formsfx.view.renderer;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.Form;\nimport com.dlsc.formsfx.model.structure.Section;\nimport com.dlsc.formsfx.view.util.ViewMixin;\nimport javafx.geometry.Insets;\nimport javafx.scene.layout.VBox;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * This class is used to render a form.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class FormRenderer extends VBox implements ViewMixin {\n\n    protected Form form;\n    protected List<GroupRendererBase> sections = new ArrayList<>();\n\n    /**\n     * This is the constructor to pass over data.\n     * @param form\n     *              The form which gets rendered.\n     */\n    public FormRenderer(Form form) {\n        this.form = form;\n\n        init();\n    }\n\n    @Override\n    public String getUserAgentStylesheet() {\n        return FormRenderer.class.getResource(\"style.css\").toExternalForm();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void initializeParts() {\n        sections = form.getGroups().stream()\n                .map(s -> {\n                    if (s instanceof Section) {\n                        return new SectionRenderer((Section) s);\n                    } else {\n                        return new GroupRenderer(s);\n                    }\n                })\n                .collect(Collectors.toList());\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void layoutParts() {\n        getStyleClass().add(\"formsfx-form\");\n\n        setPadding(new Insets(10));\n        getChildren().addAll(sections);\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/renderer/GroupRenderer.java",
    "content": "package com.dlsc.formsfx.view.renderer;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.Group;\nimport javafx.geometry.Insets;\n\n/**\n * This class renders a group for a form.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class GroupRenderer extends GroupRendererBase {\n\n    /**\n     * This is the constructor to pass over data.\n     *\n     * @param group\n     *              The section which gets rendered.\n     */\n    protected GroupRenderer(Group group) {\n        this.element = group;\n        init();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void initializeParts() {\n        super.initializeParts();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void layoutParts() {\n        super.layoutParts();\n\n        getStyleClass().add(\"formsfx-group\");\n\n        setFocusTraversable(false);\n        setPadding(new Insets(SPACING * 2));\n        getChildren().add(grid);\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/renderer/GroupRendererBase.java",
    "content": "package com.dlsc.formsfx.view.renderer;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.Element;\nimport com.dlsc.formsfx.model.structure.Field;\nimport com.dlsc.formsfx.model.structure.Group;\nimport com.dlsc.formsfx.model.structure.NodeElement;\nimport com.dlsc.formsfx.view.controls.SimpleControl;\nimport com.dlsc.formsfx.view.util.ViewMixin;\nimport javafx.geometry.Insets;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.StackPane;\n\n/**\n * This class handles shared aspects of groups and sections during rendering.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic abstract class GroupRendererBase<V extends Group> extends StackPane implements ViewMixin {\n\n    /**\n     * - SPACING is used to set the spacing of the section as well as the\n     *   spacing for vertical/horizontal gaps between controls.\n     */\n    protected final int SPACING = 10;\n\n    protected GridPane grid;\n\n    protected V element;\n\n    /**\n     *\n     */\n    @Override\n    public void initializeParts() {\n        grid = new GridPane();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void layoutParts() {\n        int COLUMN_COUNT = 12;\n\n        for (int i = 0; i < COLUMN_COUNT; i++) {\n            ColumnConstraints colConst = new ColumnConstraints();\n            colConst.setPercentWidth(100.0 / COLUMN_COUNT);\n            grid.getColumnConstraints().add(colConst);\n        }\n\n        grid.setHgap(SPACING);\n        grid.setVgap(SPACING);\n        setPadding(new Insets(SPACING));\n\n        int currentRow = 0;\n        int currentColumnCount = 0;\n\n        // Add the controls in the GridPane in a 12-column layout. If a control\n        // takes up too much horizontal space, wrap it to the next row.\n\n        for (Element e : element.getElements()) {\n            int span = e.getSpan();\n\n            if (currentColumnCount + span > COLUMN_COUNT) {\n                currentRow += 1;\n                currentColumnCount = 0;\n            }\n\n            if (e instanceof Field) {\n                Field f = (Field) e;\n                SimpleControl c = f.getRenderer();\n                c.setField(f);\n\n                grid.add(c, currentColumnCount, currentRow, span, 1);\n            } else if (e instanceof NodeElement){\n                grid.add(((NodeElement)e).getNode(), currentColumnCount, currentRow, span, 1);\n            }\n\n            currentColumnCount += span;\n        }\n    }\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/renderer/SectionRenderer.java",
    "content": "package com.dlsc.formsfx.view.renderer;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.Section;\nimport javafx.scene.control.TitledPane;\n\n/**\n * This class renders a section for a form.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class SectionRenderer extends GroupRendererBase<Section> {\n\n    private TitledPane titledPane;\n\n    /**\n     * This is the constructor to pass over data.\n     *\n     * @param section\n     *              The section which gets rendered.\n     */\n    protected SectionRenderer(Section section) {\n        this.element = section;\n        init();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void initializeParts() {\n        super.initializeParts();\n        titledPane = new TitledPane();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void layoutParts() {\n        super.layoutParts();\n\n        getStyleClass().add(\"formsfx-section\");\n\n        titledPane.setContent(grid);\n        getChildren().add(titledPane);\n\n        titledPane.setFocusTraversable(false);\n        titledPane.setExpanded(true);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupValueChangedListeners() {\n        titledPane.collapsibleProperty().bind(element.collapsibleProperty());\n        titledPane.expandedProperty().addListener((observable, oldValue, newValue) -> element.collapsedProperty().setValue(!newValue));\n        element.collapsedProperty().addListener((observable, oldValue, newValue) -> titledPane.expandedProperty().setValue(!newValue));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void setupBindings() {\n        titledPane.textProperty().bind(element.titleProperty());\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/util/ColSpan.java",
    "content": "package com.dlsc.formsfx.view.util;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\n/**\n * An enum to set the span of columns in the form more easily.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic enum ColSpan {\n\n    FIVE_SIXTH(10),\n    TWO_THIRD(8),\n    HALF(6),\n    THIRD(4),\n    QUARTER(3),\n    SIXTH(2),\n    TWELFTH(1);\n\n    private int span;\n\n    ColSpan(int span) {\n        this.span = span;\n    }\n\n    public int valueOf() {\n        return span;\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/com/dlsc/formsfx/view/util/ViewMixin.java",
    "content": "package com.dlsc.formsfx.view.util;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport java.util.List;\n\n/**\n * This interface defines lifecycle of a FormsFX view.\n *\n * @author Dieter Holz\n */\npublic interface ViewMixin {\n\n    /**\n     * This method holds a list of stylesheets.\n     *\n     * @return List of stylesheets.\n     */\n    List<String> getStylesheets();\n\n    /**\n     * This method calls all the other methods, so that it can be initialized\n     * easier.\n     */\n    default void init() {\n        initializeSelf();\n        initializeParts();\n        layoutParts();\n        setupEventHandlers();\n        setupValueChangedListeners();\n        setupBindings();\n    }\n\n    /**\n     * This method can be used to initialize the parts of the same class.\n     */\n    default void initializeSelf() {}\n\n    /**\n     * This method is used to initializes all the properties of a class.\n     */\n    void initializeParts();\n\n    /**\n     * This method is used to align the parts of a class.\n     */\n    void layoutParts();\n\n    /**\n     * This method is used to set up event handlers.\n     */\n    default void setupEventHandlers() {}\n\n    /**\n     * This method is used to set up value change listeners.\n     */\n    default void setupValueChangedListeners() {}\n\n    /**\n     * This method is used to configure the bindings of the properties.\n     */\n    default void setupBindings() {}\n\n    /**\n     * This method adds the stylesheet files to the getStylesheets method.\n     *\n     * @param stylesheetFile\n     *              List of stylesheet files\n     */\n    default void addStylesheetFiles(String... stylesheetFile) {\n        for (String file : stylesheetFile) {\n            String stylesheet = getClass().getResource(file).toExternalForm();\n            getStylesheets().add(stylesheet);\n        }\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/main/java/module-info.java",
    "content": "/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 - 2018 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\nmodule com.dlsc.formsfx {\n\n    requires javafx.controls;\n\n    exports com.dlsc.formsfx.model.event;\n    exports com.dlsc.formsfx.model.structure;\n    exports com.dlsc.formsfx.model.util;\n    exports com.dlsc.formsfx.model.validators;\n\n    exports com.dlsc.formsfx.view.controls;\n    exports com.dlsc.formsfx.view.renderer;\n    exports com.dlsc.formsfx.view.util;\n\n    opens com.dlsc.formsfx.view.renderer;\n}\n"
  },
  {
    "path": "formsfx-core/src/main/resources/com/dlsc/formsfx/view/renderer/style.css",
    "content": "/*******************\n * GENERAL STYLING *\n *******************/\n\n.formsfx-group {\n    -fx-border-width: 1px;\n    -fx-border-color: rgb(200, 200, 200);\n    -fx-border-insets: 10px;\n}\n\n.simple-control .text-field,\n.simple-control .password-field,\n.simple-control .text-area {\n    -fx-padding: 5px;\n}\n\n.simple-control .read-only-label {\n    -fx-padding: 0 6px;\n}\n\n/*********************\n * TEXT AREA STYLING *\n *********************/\n\n.simple-textarea {\n    -fx-text-fill: -fx-text-inner-color;\n    -fx-highlight-fill: derive(-fx-control-inner-background,-20%);\n    -fx-highlight-text-fill: -fx-text-inner-color;\n    -fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);\n    /*noinspection CssInvalidFunction*/\n    -fx-background-color:\n            linear-gradient(to bottom, derive(-fx-text-box-border, -10%), -fx-text-box-border),\n            linear-gradient(from 0px 0px to 0px 5px, derive(-fx-control-inner-background, -9%), -fx-control-inner-background);\n    -fx-background-insets: 0, 1;\n    -fx-background-radius: 3, 2;\n    -fx-cursor: text;\n    -fx-padding: 0.333333em;\n}\n\n.simple-textarea:focused {\n    -fx-highlight-fill: -fx-accent;\n    -fx-highlight-text-fill: white;\n    /*noinspection CssInvalidFunction*/\n    -fx-background-color:\n            -fx-focus-color,\n            -fx-control-inner-background,\n            -fx-faint-focus-color,\n            linear-gradient(from 0px 0px to 0px 5px, derive(-fx-control-inner-background, -9%), -fx-control-inner-background);\n    -fx-background-insets: -0.2, 1, -1.4, 3;\n    -fx-background-radius: 3, 2, 4, 0;\n    -fx-prompt-text-fill: transparent;\n}\n\n.simple-textarea .content {\n    -fx-padding: 0;\n    -fx-cursor: text;\n    -fx-background-color: -fx-control-inner-background;\n    -fx-background-radius: 0;\n}\n\n/********************\n * LISTVIEW STYLING *\n ********************/\n\n.simple-listview-control .list-view {\n    -fx-padding: 0 0 0 5px;\n    -fx-border-color: transparent;\n    -fx-border-width: 0 4px 0 0;\n    -fx-border-insets: 5px 5px 5px 0;\n}\n\n/***********************\n * RADIOBUTTON STYLING *\n ***********************/\n\n.simple-radio-control > VBox {\n    -fx-border-width: 0 4px 0 0;\n    -fx-border-insets: 5px 5px 5px 0;\n    -fx-border-color: transparent;\n}\n\n/*********************\n * COMBOBOX STYLING *\n *********************/\n\n.simple-select-control .read-only-label {\n    -fx-padding: 0 9px;\n}\n\n/*****************\n * STATE STYLING *\n *****************/\n\n/*noinspection CssInvalidPseudoSelector*/\n.simple-control:changed .text-field,\n.simple-control:changed .password-field,\n.simple-control:changed .text-area,\n.simple-control:required .text-field,\n.simple-control:required .password-field,\n.simple-control:required .text-area,\n.simple-control:invalid .text-field,\n.simple-control:invalid .password-field,\n.simple-control:invalid .text-area,\n.simple-select-control:changed .arrow-button,\n.simple-select-control:required .arrow-button,\n.simple-select-control:invalid .arrow-button,\n.simple-listview-control:changed .list-view,\n.simple-listview-control:required .list-view,\n.simple-listview-control:invalid .list-view {\n    -fx-padding: 0 0 0 5px;\n    -fx-border-width: 0 4px 0 0;\n    -fx-border-insets: 5px 5px 5px 0;\n}\n\n/*noinspection CssInvalidPseudoSelector*/\n.simple-boolean-control:invalid > VBox,\n.simple-boolean-control:required > VBox,\n.simple-boolean-control:changed > VBox,\n.simple-checkbox-control:changed > VBox,\n.simple-checkbox-control:required > VBox,\n.simple-checkbox-control:invalid > VBox,\n.simple-radio-control:changed > VBox,\n.simple-radio-control:required > VBox,\n.simple-radio-control:invalid > VBox {\n    -fx-border-width: 0 4px 0 0;\n    -fx-border-insets: 0 5px 0 0;\n}\n\n/*noinspection CssInvalidPseudoSelector*/\n.simple-control:required .text-field,\n.simple-control:required .password-field,\n.simple-control:required .text-area,\n.simple-boolean-control:required > VBox,\n.simple-select-control:required .arrow-button,\n.simple-radio-control:required > VBox,\n.simple-checkbox-control:required > VBox,\n.simple-listview-control:required .list-view {\n    -fx-border-color: transparent #EBDE4C transparent transparent;\n}\n\n/*noinspection CssInvalidPseudoSelector*/\n.simple-control:changed .text-field,\n.simple-control:changed .password-field,\n.simple-control:changed .text-area,\n.simple-boolean-control:changed > VBox,\n.simple-select-control:changed .arrow-button,\n.simple-radio-control:changed > VBox,\n.simple-checkbox-control:changed > VBox,\n.simple-listview-control:changed .list-view {\n    -fx-border-color: transparent #4cadeb transparent transparent;\n}\n\n/*noinspection CssInvalidPseudoSelector*/\n.simple-control:invalid .text-field,\n.simple-control:invalid .password-field,\n.simple-control:invalid .text-area,\n.simple-boolean-control:invalid > VBox,\n.simple-select-control:invalid .arrow-button,\n.simple-radio-control:invalid > VBox,\n.simple-checkbox-control:invalid > VBox,\n.simple-listview-control:invalid .list-view {\n    -fx-control-inner-background: #ffe5e5;\n    -fx-border-color: transparent #EB4C4C transparent transparent;\n}\n\n/*noinspection CssInvalidPseudoSelector*/\n.simple-select-control:invalid .combo-box-base {\n    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, #ffe5e5;\n    -fx-background-insets: 0 0 -1 0, 0, 1, 2;\n    -fx-background-radius: 3px, 3px, 2px, 1px;\n}\n\n.simple-checkbox-control:disabled > VBox,\n.simple-radio-control:disabled > VBox,\n.simple-boolean-control:disabled > VBox,\n.simple-listview-control:disabled .list-view {\n    -fx-border-color: transparent;\n}\n\n/*noinspection CssInvalidPseudoSelector*/\n.simple-select-control:changed .arrow,\n.simple-select-control:required .arrow,\n.simple-select-control:invalid .arrow {\n    -fx-pref-width: 15px;\n    -fx-border-insets: 0 5px 0 0;\n    -fx-background-insets: 0 5px 0 0;\n}\n\n/*noinspection CssInvalidPseudoSelector*/\n.simple-radio-control:invalid .radio-button > .radio,\n.simple-boolean-control:invalid .check-box > .box,\n.simple-checkbox-control:invalid .check-box > .box {\n    -fx-background-color:\n            -fx-shadow-highlight-color,\n            -fx-outer-border,\n            -fx-inner-border,\n            #ffe5e5;\n}\n\n/*noinspection CssInvalidPseudoSelector*/\n.simple-radio-control:invalid .radio-button:hover > .radio,\n.simple-radio-control:invalid .radio-button:armed > .radio {\n    -fx-color: #ffcccc;\n}\n\n/*noinspection CssInvalidPseudoSelector*/\n.simple-radio-control:invalid .radio-button:focused > .radio,\n.simple-boolean-control:invalid .check-box:focused > .box,\n.simple-checkbox-control:invalid .check-box:focused > .box {\n    -fx-background-color:\n            -fx-focus-color,\n            -fx-inner-border,\n            -fx-body-color,\n            -fx-faint-focus-color,\n            #ffe5e5;\n}\n\n/*noinspection CssInvalidPseudoSelector*/\n.simple-boolean-control:invalid .check-box:hover > .box,\n.simple-boolean-control:invalid .check-box:armed > .box,\n.simple-checkbox-control:invalid .check-box:hover > .box,\n.simple-checkbox-control:invalid .check-box:armed > .box {\n    -fx-color: #ffcccc;\n}"
  },
  {
    "path": "formsfx-core/src/test/java/com/dlsc/formsfx/model/structure/FieldTest.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *        http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.validators.StringLengthValidator;\nimport com.dlsc.formsfx.view.util.ColSpan;\nimport javafx.application.Platform;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ListProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleListProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.collections.FXCollections;\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport java.util.Arrays;\n\n/**\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\n@Ignore\npublic class FieldTest {\n\n    @BeforeClass\n    public static void beforeClass() {\n        try {\n            Platform.startup(() -> {});\n        } catch (IllegalStateException ex) {\n            // JavaFX may only be initialized once.\n        }\n    }\n\n    @Test\n    public void validTest() {\n        StringField s = Field.ofStringType(\"test\");\n\n        final int[] changes = {0};\n\n        s.validProperty().addListener((observable, oldValue, newValue) -> changes[0] += 1);\n\n        s.required(\"This field is required.\").validate(StringLengthValidator.atLeast(6, \"test\"));\n\n        Assert.assertFalse(s.isValid());\n\n        s.validate(StringLengthValidator.upTo(6, \"test\"));\n\n        Assert.assertEquals(2, changes[0]);\n        Assert.assertTrue(s.isValid());\n    }\n\n    @Test\n    public void singleSelectionTest() {\n        SingleSelectionField<String> s = Field.ofSingleSelectionType(Arrays.asList(\"Test\", \"Test 1\", \"Test 2\"), 0);\n\n        Assert.assertEquals(\"Test\", s.getSelection());\n\n        s.select(2);\n        Assert.assertEquals(\"Test 2\", s.getSelection());\n\n        s.select(4);\n        Assert.assertEquals(\"Test 2\", s.getSelection());\n\n        s.deselect();\n        Assert.assertEquals(null, s.getSelection());\n\n        s.select(2);\n        Assert.assertEquals(\"Test 2\", s.getSelection());\n\n        s.select(-1);\n        Assert.assertEquals(null, s.getSelection());\n    }\n\n    @Test\n    public void multiSelectionTest() {\n        MultiSelectionField<String> s = Field.ofMultiSelectionType(Arrays.asList(\"Test\", \"Test 1\", \"Test 2\"), Arrays.asList(0, 3));\n\n        Assert.assertEquals(1, s.getSelection().size());\n\n        s.select(2);\n        Assert.assertEquals(2, s.getSelection().size());\n\n        s.select(4);\n        Assert.assertEquals(2, s.getSelection().size());\n\n        s.deselect(1);\n        Assert.assertEquals(2, s.getSelection().size());\n\n        s.deselect(0);\n        Assert.assertEquals(1, s.getSelection().size());\n    }\n\n    @Test\n    public void multilineTest() {\n        StringField s = Field.ofStringType(\"test\").multiline(true);\n\n        Assert.assertTrue(s.isMultiline());\n\n        s.multiline(false);\n        Assert.assertFalse(s.multilineProperty().getValue());\n    }\n\n    @Test\n    public void dataBindingTest() {\n        StringProperty s = new SimpleStringProperty(\"test\");\n        DoubleProperty d = new SimpleDoubleProperty(1.0);\n        IntegerProperty i = new SimpleIntegerProperty(3);\n        BooleanProperty b = new SimpleBooleanProperty(false);\n\n        StringField sf = Field.ofStringType(s);\n        DoubleField df = Field.ofDoubleType(d);\n        IntegerField inf = Field.ofIntegerType(i);\n        BooleanField bf = Field.ofBooleanType(b);\n\n        sf.userInputProperty().setValue(\"test 2\");\n        Assert.assertEquals(\"test\", s.getValue());\n        sf.persist();\n        Assert.assertEquals(\"test 2\", s.getValue());\n\n        df.userInputProperty().setValue(\"2\");\n        Assert.assertEquals(1.0, d.get(), 0.0001);\n        df.persist();\n        Assert.assertEquals(2.0, d.get(), 0.0001);\n\n        inf.userInputProperty().setValue(\"5\");\n        Assert.assertEquals(3, i.get());\n        inf.persist();\n        Assert.assertEquals(5, i.get());\n\n        bf.userInputProperty().setValue(\"true\");\n        Assert.assertEquals(false, b.get());\n        bf.persist();\n        Assert.assertEquals(true, b.get());\n\n        s.setValue(\"test 3\");\n        Assert.assertEquals(\"test 3\", sf.getValue());\n        Assert.assertEquals(\"test 3\", sf.getValue());\n    }\n\n    @Ignore\n    public void selectionBindingTest() {\n        ObjectProperty<String> o1 = new SimpleObjectProperty<>(\"hello\");\n        ListProperty<String> l1 = new SimpleListProperty<>(FXCollections.observableArrayList(\"hello\", \"world\"));\n        ListProperty<String> l2 = new SimpleListProperty<>(FXCollections.observableArrayList(\"hi\", \"there\"));\n        ListProperty<String> l3 = new SimpleListProperty<>(FXCollections.observableArrayList(\"hi\"));\n\n        SingleSelectionField<String> sif = Field.ofSingleSelectionType(l1, o1);\n        MultiSelectionField<String> mf = Field.ofMultiSelectionType(l2, l3);\n\n        Assert.assertEquals(\"hello\", sif.getSelection());\n        Assert.assertEquals(1, mf.getSelection().size());\n\n        l1.setValue(FXCollections.observableArrayList(\"oh\", \"wonder\"));\n\n        Assert.assertEquals(null, sif.getSelection());\n\n        mf.select(1);\n\n        Assert.assertEquals(2, mf.getSelection().size());\n        Assert.assertEquals(1, l3.size());\n\n        mf.persist();\n\n        Assert.assertEquals(2, l3.size());\n    }\n\n    @Ignore\n    public void propertiesTest() {\n        StringProperty sp = new SimpleStringProperty(\"test 3\");\n\n        StringField s = Field.ofStringType(\"test\")\n                .multiline(true)\n                .editable(true)\n                .format(String::toString)\n                .styleClass(\"class\", \"thing\")\n                .id(\"element\")\n                .placeholder(\"temp\")\n                .label(\"Field\")\n                .required(\"error\")\n                .span(6)\n                .span(ColSpan.HALF);\n\n        Assert.assertEquals(\"test\", s.getValue());\n        Assert.assertEquals(6, s.getSpan());\n        Assert.assertEquals(2, s.getStyleClass().size());\n        Assert.assertEquals(\"Field\", s.getLabel());\n        Assert.assertEquals(\"temp\", s.getPlaceholder());\n\n        s.bind(sp);\n\n        Assert.assertEquals(\"test 3\", s.getValue());\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/test/java/com/dlsc/formsfx/model/structure/FormTest.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.util.ResourceBundleService;\nimport com.dlsc.formsfx.model.validators.RegexValidator;\nimport com.dlsc.formsfx.model.validators.StringLengthValidator;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.Locale;\nimport java.util.ResourceBundle;\n\n/**\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class FormTest {\n\n    private ResourceBundleService service;\n    private ResourceBundle rbDE = ResourceBundle.getBundle(\"testbundle\", new Locale(\"de\", \"CH\"));\n    private ResourceBundle rbEN = ResourceBundle.getBundle(\"testbundle\", new Locale(\"en\", \"UK\"));\n\n    @Before\n    public void beforeSuite() {\n        service = new ResourceBundleService(rbEN);\n    }\n\n    @Test\n    public void generateFormTest() {\n        Form f = Form.of(\n                Group.of(\n                        Field.ofDoubleType(2.0)\n                ),\n                Section.of(\n                        Field.ofStringType(\"test\"),\n                        Field.ofBooleanType(false)\n                ),\n                Section.of(\n                        Field.ofIntegerType(1),\n                        Field.ofDoubleType(1.0)\n                ),\n                Section.of(\n                        Field.ofPasswordType(\"\")\n                )\n        );\n\n        Assert.assertEquals(6, f.getElements().size());\n        Assert.assertEquals(4, f.getGroups().size());\n    }\n\n    @Test\n    public void translationTest() {\n        Form f = Form.of(\n                Section.of(\n                        Field.ofStringType(\"test\")\n                                .label(\"string_label\")\n                ).title(\"section_1_title\")\n        ).title(\"form_title\")\n                .i18n(service);\n\n        Assert.assertEquals(\"Test Form\", f.getTitle());\n\n        service.changeLocale(rbDE);\n\n        Assert.assertEquals(\"Testformular\", f.getTitle());\n        Assert.assertEquals(\"Erste Gruppe\", ((Section) f.getGroups().get(0)).getTitle());\n    }\n\n    @Test\n    public void changeTest() {\n        StringField s = Field.ofStringType(\"test\");\n        DoubleField d = Field.ofDoubleType(2.0);\n        Section sec = Section.of(s, d);\n\n        Form f = Form.of(sec);\n\n        s.userInputProperty().setValue(\"testttt\");\n        d.userInputProperty().setValue(\"3.0\");\n\n        Assert.assertEquals(true, s.hasChanged());\n        Assert.assertEquals(true, sec.hasChanged());\n        Assert.assertEquals(true, f.hasChanged());\n\n        s.reset();\n\n        Assert.assertEquals(false, s.hasChanged());\n        Assert.assertEquals(true, sec.hasChanged());\n        Assert.assertEquals(true, f.hasChanged());\n\n        d.persist();\n\n        Assert.assertEquals(false, s.hasChanged());\n        Assert.assertEquals(false, sec.hasChanged());\n        Assert.assertEquals(false, f.hasChanged());\n    }\n\n    @Test\n    public void validationTest() {\n        StringField s = Field.ofStringType(\"Testing\")\n                .validate(\n                        StringLengthValidator.atLeast(5, \"String not long enough.\"),\n                        RegexValidator.forPattern(\"[a-z ]*\", \"String is not all lowercase.\")\n                );\n        Section sec = Section.of(s);\n\n        Form f = Form.of(sec);\n\n        Assert.assertEquals(false, s.isValid());\n        Assert.assertEquals(false, sec.isValid());\n        Assert.assertEquals(false, f.isValid());\n        Assert.assertEquals(1, s.getErrorMessages().size());\n\n        s.userInputProperty().setValue(\"Test\");\n\n        Assert.assertEquals(false, s.isValid());\n        Assert.assertEquals(false, sec.isValid());\n        Assert.assertEquals(false, f.isValid());\n        Assert.assertEquals(2, s.getErrorMessages().size());\n\n        s.userInputProperty().setValue(\"Testing this\");\n\n        Assert.assertEquals(false, s.isValid());\n        Assert.assertEquals(false, sec.isValid());\n        Assert.assertEquals(false, f.isValid());\n        Assert.assertEquals(1, s.getErrorMessages().size());\n\n        s.userInputProperty().setValue(\"testing this properly\");\n\n        Assert.assertEquals(true, s.isValid());\n        Assert.assertEquals(true, sec.isValid());\n        Assert.assertEquals(true, f.isValid());\n        Assert.assertTrue(s.getErrorMessages().isEmpty());\n    }\n\n    @Test\n    public void persistableTest() {\n        StringField s = Field.ofStringType(\"test\");\n        DoubleField d = Field.ofDoubleType(2.0);\n        IntegerField i = Field.ofIntegerType(10);\n\n        Form f = Form.of(Group.of(i), Section.of(s), Section.of(d));\n\n        s.userInputProperty().setValue(\"testttt\");\n\n        Assert.assertEquals(true, s.hasChanged());\n        Assert.assertEquals(true, f.hasChanged());\n        Assert.assertEquals(true, f.isPersistable());\n\n        s.reset();\n\n        Assert.assertEquals(false, s.hasChanged());\n        Assert.assertEquals(false, f.hasChanged());\n        Assert.assertEquals(false, f.isPersistable());\n\n        i.userInputProperty().setValue(\"testttt\");\n\n        Assert.assertEquals(true, i.hasChanged());\n        Assert.assertEquals(true, f.hasChanged());\n        Assert.assertEquals(false, f.isPersistable());\n\n        f.persist();\n\n        Assert.assertEquals(true, i.hasChanged());\n        Assert.assertEquals(true, f.hasChanged());\n        Assert.assertEquals(false, f.isPersistable());\n\n        i.userInputProperty().setValue(\"50\");\n\n        Assert.assertEquals(true, f.isPersistable());\n\n        f.reset();\n\n        Assert.assertEquals(false, i.hasChanged());\n        Assert.assertEquals(false, f.hasChanged());\n        Assert.assertEquals(false, f.isPersistable());\n\n        i.userInputProperty().setValue(\"50\");\n        f.persist();\n\n        Assert.assertEquals(false, i.hasChanged());\n        Assert.assertEquals(false, f.hasChanged());\n        Assert.assertEquals(false, f.isPersistable());\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/test/java/com/dlsc/formsfx/model/structure/SectionTest.java",
    "content": "package com.dlsc.formsfx.model.structure;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class SectionTest {\n\n    @Test\n    public void collapseTest() {\n        Section s = Section.of();\n\n        final int[] changes = { 0 };\n\n        s.collapsedProperty().addListener((observable, oldValue, newValue) -> changes[0] += 1);\n\n        s.collapse(true);\n        s.collapse(false);\n\n        Assert.assertEquals(2, changes[0]);\n        Assert.assertFalse(s.isCollapsed());\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/test/java/com/dlsc/formsfx/model/util/ResourceBundleServiceTest.java",
    "content": "package com.dlsc.formsfx.model.util;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.Locale;\nimport java.util.MissingResourceException;\nimport java.util.ResourceBundle;\n\nimport static junit.framework.TestCase.fail;\n\n/**\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class ResourceBundleServiceTest {\n\n    private final ResourceBundle rbEN = ResourceBundle.getBundle(\"testbundle\", new Locale(\"en\", \"UK\"));\n    private final ResourceBundle rbDE = ResourceBundle.getBundle(\"testbundle\", new Locale(\"de\", \"CH\"));\n\n    @Test\n    public void translateTest() {\n        ResourceBundleService rbs = new ResourceBundleService(rbEN);\n        Assert.assertEquals(\"Test Form\", rbs.translate(\"form_title\"));\n\n        try {\n            rbs.translate(\"non_existing\");\n            fail();\n        } catch (MissingResourceException ignored) {}\n    }\n\n    @Test\n    public void changeLocaleTest() {\n        ResourceBundleService rbs = new ResourceBundleService(rbEN);\n        final int[] calls = new int[] { 0 };\n\n        Runnable r = () -> calls[0] += 1;\n\n        rbs.addListener(r);\n\n        rbs.changeLocale(rbDE);\n        Assert.assertEquals(1, calls[0]);\n\n        rbs.changeLocale(rbDE);\n        Assert.assertEquals(1, calls[0]);\n\n        rbs.removeListener(r);\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/test/java/com/dlsc/formsfx/model/validators/CustomValidatorTest.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class CustomValidatorTest {\n\n    @Test\n    public void customTest() {\n        CustomValidator<String> c = CustomValidator.forPredicate(s -> s.length() % 2 == 0, \"test\");\n\n        Assert.assertTrue(c.validate(\"abcd\").getResult());\n        Assert.assertFalse(c.validate(\"abc\").getResult());\n        Assert.assertTrue(c.validate(\"\").getResult());\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/test/java/com/dlsc/formsfx/model/validators/DoubleRangeValidatorTest.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport static junit.framework.TestCase.fail;\n\n/**\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class DoubleRangeValidatorTest {\n\n    @Test\n    public void betweenTest() {\n        DoubleRangeValidator i = DoubleRangeValidator.between(3.5, 12.1351, \"test\");\n\n        Assert.assertTrue(i.validate(11.5).getResult());\n        Assert.assertTrue(i.validate(3.50000001).getResult());\n        Assert.assertFalse(i.validate(12.13511).getResult());\n        Assert.assertFalse(i.validate(3.4999999).getResult());\n\n        try {\n            DoubleRangeValidator i2 = DoubleRangeValidator.between(10.0, 5.3, \"test\");\n            fail();\n        } catch (IllegalArgumentException ignored) {}\n\n        try {\n            DoubleRangeValidator i2 = DoubleRangeValidator.between(5.5, 5.5, \"test\");\n        } catch (IllegalArgumentException ignored) {\n            fail();\n        }\n    }\n\n    @Test\n    public void atLeastTest() {\n        DoubleRangeValidator i = DoubleRangeValidator.atLeast(5.12351, \"test\");\n\n        Assert.assertTrue(i.validate(6234.1).getResult());\n        Assert.assertFalse(i.validate(1.31).getResult());\n        Assert.assertTrue(i.validate(5.12351).getResult());\n        Assert.assertFalse(i.validate(5.1235).getResult());\n        Assert.assertTrue(i.validate(Double.MAX_VALUE).getResult());\n    }\n\n    @Test\n    public void upToTest() {\n        DoubleRangeValidator i = DoubleRangeValidator.upTo(3.14, \"test\");\n\n        Assert.assertFalse(i.validate(-1.14).getResult());\n        Assert.assertFalse(i.validate(5.13).getResult());\n        Assert.assertTrue(i.validate(3.14).getResult());\n        Assert.assertFalse(i.validate(3.141).getResult());\n        Assert.assertTrue(i.validate(Double.MIN_VALUE).getResult());\n    }\n\n    @Test\n    public void exactlyTest() {\n        DoubleRangeValidator i = DoubleRangeValidator.exactly(3.14, \"test\");\n\n        Assert.assertFalse(i.validate(-3.4).getResult());\n        Assert.assertFalse(i.validate(3.145).getResult());\n        Assert.assertTrue(i.validate(3.14).getResult());\n        Assert.assertFalse(i.validate(3.0).getResult());\n        Assert.assertFalse(i.validate(Double.MIN_VALUE).getResult());\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/test/java/com/dlsc/formsfx/model/validators/IntegerRangeValidatorTest.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport static junit.framework.TestCase.fail;\n\n/**\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class IntegerRangeValidatorTest {\n\n    @Test\n    public void betweenTest() {\n        IntegerRangeValidator i = IntegerRangeValidator.between(10, 20, \"test\");\n\n        Assert.assertTrue(i.validate(14).getResult());\n        Assert.assertFalse(i.validate(21).getResult());\n        Assert.assertTrue(i.validate(20).getResult());\n        Assert.assertTrue(i.validate(10).getResult());\n\n        try {\n            IntegerRangeValidator i2 = IntegerRangeValidator.between(20, 10, \"test\");\n            fail();\n        } catch (IllegalArgumentException ignored) {}\n\n        try {\n            IntegerRangeValidator i2 = IntegerRangeValidator.between(10, 10, \"test\");\n        } catch (IllegalArgumentException ignored) {\n            fail();\n        }\n    }\n\n    @Test\n    public void atLeastTest() {\n        IntegerRangeValidator i = IntegerRangeValidator.atLeast(10, \"test\");\n\n        Assert.assertTrue(i.validate(14).getResult());\n        Assert.assertFalse(i.validate(-139).getResult());\n        Assert.assertTrue(i.validate(10).getResult());\n        Assert.assertFalse(i.validate(9).getResult());\n        Assert.assertTrue(i.validate(Integer.MAX_VALUE).getResult());\n    }\n\n    @Test\n    public void upToTest() {\n        IntegerRangeValidator i = IntegerRangeValidator.upTo(10, \"test\");\n\n        Assert.assertFalse(i.validate(14).getResult());\n        Assert.assertFalse(i.validate(21).getResult());\n        Assert.assertTrue(i.validate(10).getResult());\n        Assert.assertFalse(i.validate(11).getResult());\n        Assert.assertTrue(i.validate(Integer.MIN_VALUE).getResult());\n    }\n\n    @Test\n    public void exactlyTest() {\n        IntegerRangeValidator i = IntegerRangeValidator.exactly(10, \"test\");\n\n        Assert.assertFalse(i.validate(11).getResult());\n        Assert.assertFalse(i.validate(9).getResult());\n        Assert.assertTrue(i.validate(10).getResult());\n        Assert.assertFalse(i.validate(Integer.MIN_VALUE).getResult());\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/test/java/com/dlsc/formsfx/model/validators/RegexValidatorTest.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.regex.PatternSyntaxException;\n\nimport static junit.framework.TestCase.fail;\n\n/**\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class RegexValidatorTest {\n\n    @Test\n    public void regexTest() {\n        RegexValidator r = RegexValidator.forPattern(\"[a-z]*\", \"test\");\n\n        Assert.assertTrue(r.validate(\"abc\").getResult());\n        Assert.assertTrue(r.validate(\"aeihafpiaheypfhapfhpa\").getResult());\n        Assert.assertFalse(r.validate(\"Ajj\").getResult());\n        Assert.assertTrue(r.validate(\"\").getResult());\n        Assert.assertFalse(r.validate(\"hlhlhL\").getResult());\n\n        try {\n            RegexValidator r1 = RegexValidator.forPattern(\"a[aa[\", \"test\");\n            fail();\n        } catch (PatternSyntaxException ignored) {}\n    }\n\n    @Test\n    public void emailTest() {\n        RegexValidator r = RegexValidator.forEmail(\"test\");\n\n        Assert.assertTrue(r.validate(\"test@test.com\").getResult());\n        Assert.assertFalse(r.validate(\"test@test\").getResult());\n        Assert.assertFalse(r.validate(\"test.com@test@\").getResult());\n        Assert.assertTrue(r.validate(\"test+ea@test.com\").getResult());\n    }\n\n    @Test\n    public void urlTest() {\n        RegexValidator r = RegexValidator.forURL(\"test\");\n\n        Assert.assertTrue(r.validate(\"http://test.com\").getResult());\n        Assert.assertFalse(r.validate(\"http:/test.com\").getResult());\n        Assert.assertTrue(r.validate(\"www.test.com\").getResult());\n        Assert.assertTrue(r.validate(\"https://www.test.com\").getResult());\n    }\n\n    @Test\n    public void alphaNumericTest() {\n        RegexValidator r= RegexValidator.forAlphaNumeric(\"test\");\n\n        Assert.assertTrue(r.validate(\"afaehofh3r1ohr1o3hro1h3A13OIHho\").getResult());\n        Assert.assertFalse(r.validate(\"aefih 391ur fj \").getResult());\n        Assert.assertFalse(r.validate(\"¢æ±#\").getResult());\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/test/java/com/dlsc/formsfx/model/validators/SelectionLengthValidatorTest.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport javafx.collections.FXCollections;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport static junit.framework.TestCase.fail;\n\n/**\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class SelectionLengthValidatorTest {\n\n    @Test\n    public void betweenTest() {\n        SelectionLengthValidator<Integer> s = SelectionLengthValidator.between(1, 3, \"test\");\n\n        Assert.assertTrue(s.validate(FXCollections.observableArrayList(1, 2, 3)).getResult());\n        Assert.assertFalse(s.validate(FXCollections.observableArrayList()).getResult());\n        Assert.assertFalse(s.validate(FXCollections.observableArrayList(1, 2, 3, 4, 5)).getResult());\n\n        try {\n            SelectionLengthValidator s2 = SelectionLengthValidator.between(-10, 2, \"test\");\n            fail();\n        } catch (IllegalArgumentException ignored) {}\n\n        try {\n            SelectionLengthValidator s3 = SelectionLengthValidator.between(0, 0, \"test\");\n        } catch (IllegalArgumentException e) {\n            fail();\n        }\n\n        try {\n            SelectionLengthValidator s4 = SelectionLengthValidator.between(10, 1, \"test\");\n            fail();\n        } catch (IllegalArgumentException ignored) {}\n    }\n\n    @Test\n    public void atLeastTest() {\n        SelectionLengthValidator<Integer> s = SelectionLengthValidator.atLeast(2, \"test\");\n\n        Assert.assertTrue(s.validate(FXCollections.observableArrayList(1, 4)).getResult());\n        Assert.assertFalse(s.validate(FXCollections.observableArrayList(1)).getResult());\n        Assert.assertFalse(s.validate(FXCollections.observableArrayList()).getResult());\n        Assert.assertTrue(s.validate(FXCollections.observableArrayList(1, 2, 3)).getResult());\n\n        try {\n            SelectionLengthValidator s2 = SelectionLengthValidator.atLeast(-10, \"test\");\n            fail();\n        } catch (IllegalArgumentException ignored) {}\n\n        try {\n            SelectionLengthValidator s3 = SelectionLengthValidator.atLeast(0, \"test\");\n        } catch (IllegalArgumentException e) {\n            fail();\n        }\n    }\n\n    @Test\n    public void upToTest() {\n        SelectionLengthValidator<Integer> s = SelectionLengthValidator.upTo(2, \"test\");\n\n        Assert.assertFalse(s.validate(FXCollections.observableArrayList(3, 5, 1)).getResult());\n        Assert.assertTrue(s.validate(FXCollections.observableArrayList(1, 2)).getResult());\n        Assert.assertTrue(s.validate(FXCollections.observableArrayList()).getResult());\n        Assert.assertFalse(s.validate(FXCollections.observableArrayList(1, 2, 3, 5, 14)).getResult());\n    }\n\n    @Test\n    public void exactlyTest() {\n        SelectionLengthValidator<Integer> s = SelectionLengthValidator.exactly(2, \"test\");\n\n        Assert.assertFalse(s.validate(FXCollections.observableArrayList(1, 2, 3)).getResult());\n        Assert.assertTrue(s.validate(FXCollections.observableArrayList(1, 2)).getResult());\n        Assert.assertFalse(s.validate(FXCollections.observableArrayList()).getResult());\n        Assert.assertFalse(s.validate(FXCollections.observableArrayList(1)).getResult());\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/test/java/com/dlsc/formsfx/model/validators/StringLengthValidatorTest.java",
    "content": "package com.dlsc.formsfx.model.validators;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport static junit.framework.TestCase.fail;\n\n/**\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class StringLengthValidatorTest {\n\n    @Test\n    public void betweenTest() {\n        StringLengthValidator s = StringLengthValidator.between(10, 20, \"test\");\n\n        Assert.assertTrue(s.validate(\"abcdefghijklmno\").getResult());\n        Assert.assertFalse(s.validate(\"abcde\").getResult());\n        Assert.assertTrue(s.validate(\"                \").getResult());\n        Assert.assertTrue(s.validate(\"æ¢¢äöä1ö3ä±æ#¢æ±“{\").getResult());\n\n        try {\n            StringLengthValidator s2 = StringLengthValidator.between(-10, 2, \"test\");\n            fail();\n        } catch (IllegalArgumentException ignored) {}\n\n        try {\n            StringLengthValidator s3 = StringLengthValidator.between(0, 0, \"test\");\n        } catch (IllegalArgumentException e) {\n            fail();\n        }\n\n        try {\n            StringLengthValidator s4 = StringLengthValidator.between(10, 1, \"test\");\n            fail();\n        } catch (IllegalArgumentException ignored) {}\n    }\n\n    @Test\n    public void atLeastTest() {\n        StringLengthValidator s = StringLengthValidator.atLeast(5, \"test\");\n\n        Assert.assertTrue(s.validate(\"gosjrgohgsr\").getResult());\n        Assert.assertFalse(s.validate(\" \").getResult());\n        Assert.assertFalse(s.validate(\"ae\").getResult());\n        Assert.assertTrue(s.validate(\"¶æ¢¶ππ§±#\").getResult());\n\n        try {\n            StringLengthValidator s2 = StringLengthValidator.atLeast(-10, \"test\");\n            fail();\n        } catch (IllegalArgumentException ignored) {}\n\n        try {\n            StringLengthValidator s3 = StringLengthValidator.atLeast(0, \"test\");\n        } catch (IllegalArgumentException e) {\n            fail();\n        }\n    }\n\n    @Test\n    public void upToTest() {\n        StringLengthValidator s = StringLengthValidator.upTo(5, \"test\");\n\n        Assert.assertFalse(s.validate(\"gosjrgohgsr\").getResult());\n        Assert.assertTrue(s.validate(\" \").getResult());\n        Assert.assertTrue(s.validate(\"ae\").getResult());\n        Assert.assertFalse(s.validate(\"¶æ¢¶ππ§±#\").getResult());\n    }\n\n    @Test\n    public void exactlyTest() {\n        StringLengthValidator s = StringLengthValidator.exactly(3, \"test\");\n\n        Assert.assertFalse(s.validate(\"gfyf\").getResult());\n        Assert.assertTrue(s.validate(\"   \").getResult());\n        Assert.assertTrue(s.validate(\"aee\").getResult());\n        Assert.assertFalse(s.validate(\"ee\").getResult());\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/test/java/com/dlsc/formsfx/view/controls/SimpleControlTest.java",
    "content": "package com.dlsc.formsfx.view.controls;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.Field;\nimport com.dlsc.formsfx.model.structure.MultiSelectionField;\nimport com.dlsc.formsfx.model.structure.SingleSelectionField;\nimport com.dlsc.formsfx.model.structure.StringField;\nimport javafx.application.Platform;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.RadioButton;\nimport javafx.scene.layout.StackPane;\nimport javafx.scene.layout.VBox;\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport java.util.Arrays;\n\n/**\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\n@Ignore\npublic class SimpleControlTest {\n\n    @BeforeClass\n    public static void before() {\n        try {\n            Platform.startup(() -> {});\n        } catch (IllegalStateException ex) {\n            // JavaFX may only be initialized once.\n        }\n    }\n\n    @Test\n    public void itemsTest() {\n        MultiSelectionField<Integer> mf = Field.ofMultiSelectionType(Arrays.asList(1, 2, 3), Arrays.asList(1, 2));\n        SingleSelectionField<Integer> sf = Field.ofSingleSelectionType(Arrays.asList(1, 2, 3), 1);\n\n        SimpleCheckBoxControl<Integer> cb = new SimpleCheckBoxControl<>();\n        cb.setField(mf);\n\n        SimpleListViewControl<Integer> lv = new SimpleListViewControl<>();\n        lv.setField(mf);\n\n        SimpleRadioButtonControl<Integer> rb = new SimpleRadioButtonControl<>();\n        rb.setField(sf);\n\n        SimpleComboBoxControl<Integer> cmb = new SimpleComboBoxControl<>();\n        cmb.setField(sf);\n\n        Assert.assertEquals(3, ((VBox) cb.getChildren().get(1)).getChildren().size());\n        Assert.assertTrue(((CheckBox) ((VBox) cb.getChildren().get(1)).getChildren().get(1)).isSelected());\n\n        Assert.assertEquals(3, ((ListView) lv.getChildren().get(1)).getItems().size());\n        Assert.assertTrue(((ListView) lv.getChildren().get(1)).getSelectionModel().isSelected(1));\n\n        Assert.assertEquals(3, ((VBox) rb.getChildren().get(1)).getChildren().size());\n        Assert.assertTrue(((RadioButton) ((VBox) rb.getChildren().get(1)).getChildren().get(1)).isSelected());\n\n        Assert.assertEquals(3, ((ComboBox) ((StackPane) cmb.getChildren().get(1)).getChildren().get(0)).getItems().size());\n        Assert.assertTrue(((ComboBox) ((StackPane) cmb.getChildren().get(1)).getChildren().get(0)).getSelectionModel().isSelected(1));\n\n        mf.items(Arrays.asList(1, 2, 3, 4, 5), Arrays.asList(0, 3));\n        sf.items(Arrays.asList(1, 2, 3, 4, 5), 3);\n\n        Assert.assertEquals(5, ((VBox) cb.getChildren().get(1)).getChildren().size());\n        Assert.assertTrue(((CheckBox) ((VBox) cb.getChildren().get(1)).getChildren().get(0)).isSelected());\n\n        Assert.assertEquals(5, ((ListView) lv.getChildren().get(1)).getItems().size());\n        Assert.assertTrue(((ListView) lv.getChildren().get(1)).getSelectionModel().isSelected(0));\n\n        Assert.assertEquals(5, ((VBox) rb.getChildren().get(1)).getChildren().size());\n        Assert.assertTrue(((RadioButton) ((VBox) rb.getChildren().get(1)).getChildren().get(3)).isSelected());\n\n        Assert.assertEquals(5, ((ComboBox) ((StackPane) cmb.getChildren().get(1)).getChildren().get(0)).getItems().size());\n        Assert.assertTrue(((ComboBox) ((StackPane) cmb.getChildren().get(1)).getChildren().get(0)).getSelectionModel().isSelected(3));\n    }\n\n    @Test\n    public void styleTest() {\n        StringField s = Field.ofStringType(\"test\").styleClass(\"test\");\n        SimpleTextControl t = new SimpleTextControl();\n        t.setField(s);\n\n        Assert.assertEquals(3, t.getStyleClass().size());\n\n        s.styleClass(\"hello\", \"world\");\n        Assert.assertEquals(4, t.getStyleClass().size());\n\n        s.styleClass(\"hi\", \"world\");\n        Assert.assertEquals(4, t.getStyleClass().size());\n        Assert.assertEquals(\"world\", t.getStyleClass().get(3));\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/test/java/com/dlsc/formsfx/view/renderer/RendererTest.java",
    "content": "package com.dlsc.formsfx.view.renderer;\n\n/*-\n * ========================LICENSE_START=================================\n * FormsFX\n * %%\n * Copyright (C) 2017 DLSC Software & Consulting\n * %%\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *        http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * =========================LICENSE_END==================================\n */\n\nimport com.dlsc.formsfx.model.structure.Field;\nimport com.dlsc.formsfx.model.structure.Form;\nimport com.dlsc.formsfx.model.structure.Group;\nimport com.dlsc.formsfx.model.structure.Section;\nimport com.dlsc.formsfx.view.controls.SimpleCheckBoxControl;\nimport com.dlsc.formsfx.view.controls.SimpleRadioButtonControl;\nimport com.dlsc.formsfx.view.controls.SimpleTextControl;\nimport javafx.application.Platform;\nimport javafx.scene.control.TitledPane;\nimport javafx.scene.layout.GridPane;\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\n/**\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\n@Ignore\npublic class RendererTest {\n\n    @BeforeClass\n    public static void before() throws IllegalStateException {\n        try {\n            Platform.startup(() -> {});\n        } catch (IllegalStateException ex) {\n            // JavaFX may only be initialized once.\n        }\n    }\n\n    @Test\n    public void formTest() {\n        Form f = Form.of(Group.of(), Section.of(), Group.of(), Group.of(), Section.of());\n        FormRenderer r = new FormRenderer(f);\n\n        Assert.assertTrue(r.getChildren().get(0) instanceof GroupRenderer);\n        Assert.assertEquals(5, r.getChildren().size());\n        Assert.assertFalse(r.getChildren().get(0) instanceof SectionRenderer);\n        Assert.assertTrue(r.getChildren().get(1) instanceof SectionRenderer);\n    }\n\n    @Test\n    public void groupTest() {\n        Group g = Group.of(\n                Field.ofStringType(\"\").span(7),\n                Field.ofBooleanType(false).span(8),\n                Field.ofMultiSelectionType(Arrays.asList(1, 2, 3), Collections.singletonList(1)),\n                Field.ofSingleSelectionType(Arrays.asList(1, 2, 3), 1).render(new SimpleRadioButtonControl<>())\n        );\n        GroupRenderer r = new GroupRenderer(g);\n\n        Assert.assertTrue(r.getChildren().get(0) instanceof GridPane);\n        Assert.assertTrue(((GridPane) r.getChildren().get(0)).getChildren().get(0) instanceof SimpleTextControl);\n    }\n\n    @Test\n    public void sectionTest() {\n        Section s = Section.of(\n                Field.ofDoubleType(2.0),\n                Field.ofIntegerType(1),\n                Field.ofMultiSelectionType(Arrays.asList(1, 2, 3), Collections.singletonList(1)).render(new SimpleCheckBoxControl<>()),\n                Field.ofSingleSelectionType(Arrays.asList(1, 2, 3), 1)\n        );\n        SectionRenderer r = new SectionRenderer(s);\n\n        Assert.assertTrue(r.getChildren().get(0) instanceof TitledPane);\n        Assert.assertTrue(((TitledPane) r.getChildren().get(0)).getContent() instanceof GridPane);\n        Assert.assertTrue(((TitledPane) r.getChildren().get(0)).isExpanded());\n\n        s.collapse(true);\n\n        Assert.assertFalse(((TitledPane) r.getChildren().get(0)).isExpanded());\n    }\n\n}\n"
  },
  {
    "path": "formsfx-core/src/test/resources/testbundle_de_CH.properties",
    "content": "form_title = Testformular\nsection_1_title = Erste Gruppe\nstring_label = Text\n"
  },
  {
    "path": "formsfx-core/src/test/resources/testbundle_en_UK.properties",
    "content": "form_title = Test Form\nsection_1_title = First Section\nstring_label = String\n"
  },
  {
    "path": "formsfx-demo/.gitignore",
    "content": "/target\n*.iml"
  },
  {
    "path": "formsfx-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<parent>\n\t\t<groupId>com.dlsc.formsfx</groupId>\n\t\t<artifactId>formsfx</artifactId>\n\t\t<version>11.6.0</version>\n\t</parent>\n\n    <name>FormsFX Demo</name>\n    <packaging>jar</packaging>\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>formsfx-demo</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.dlsc.formsfx</groupId>\n            <artifactId>formsfx-core</artifactId>\n            <version>11.6.0</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "formsfx-demo/src/main/java/com/dlsc/formsfx/demo/Demo.java",
    "content": "package com.dlsc.formsfx.demo;\n\nimport com.dlsc.formsfx.demo.model.DemoModel;\nimport com.dlsc.formsfx.demo.view.RootPane;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.stage.Stage;\n\n/**\n * This is the main class to start the application.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class Demo extends Application {\n\n    @Override\n    public void start(Stage primaryStage) throws Exception {\n        DemoModel model = new DemoModel();\n        RootPane rootPanel = new RootPane(model);\n\n        Scene scene = new Scene(rootPanel);\n\n        primaryStage.titleProperty().bind(model.getFormInstance().titleProperty());\n\n        primaryStage.setScene(scene);\n        primaryStage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "formsfx-demo/src/main/java/com/dlsc/formsfx/demo/model/Country.java",
    "content": "package com.dlsc.formsfx.demo.model;\n\nimport javafx.beans.property.*;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\n\nimport java.time.LocalDate;\nimport java.time.Month;\n\n/**\n * This class serves as the model for the demo application.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class Country {\n\n    private StringProperty name = new SimpleStringProperty(\"Switzerland\");\n    private StringProperty iso = new SimpleStringProperty(\"CH\");\n    private BooleanProperty independence = new SimpleBooleanProperty(true);\n    private ObjectProperty<LocalDate> independenceDay = new SimpleObjectProperty<>(LocalDate.of(1648, Month.OCTOBER, 24));\n\n    private StringProperty currencyShort = new SimpleStringProperty(\"CHF\");\n    private StringProperty currencyLong = new SimpleStringProperty(\"Swiss Franc\");\n\n    private IntegerProperty population = new SimpleIntegerProperty(8401120);\n    private DoubleProperty area = new SimpleDoubleProperty(41285);\n    private StringProperty tld = new SimpleStringProperty(\".ch\");\n\n    private StringProperty dateFormat = new SimpleStringProperty(\"dd.mm.yyyy\");\n    private ObjectProperty<String> driverSide = new SimpleObjectProperty<>(\"Right\");\n    private StringProperty timeZone = new SimpleStringProperty(\"CET\");\n    private StringProperty summerTimeZone = new SimpleStringProperty(\"CEST\");\n\n    private ListProperty<String> allSides = new SimpleListProperty<>(FXCollections.observableArrayList(\"Right\", \"Left\"));\n    private ListProperty<String> allCities = new SimpleListProperty<>(FXCollections.observableArrayList(\"Zurich (ZH)\", \"Geneva (GE)\", \"Basel (BS)\", \"Lausanne (VD)\", \"Bern (BE)\", \"Winterthur (ZH)\", \"Lucerne (LU)\", \"St. Gallen (SG)\", \"Lugano (TI)\", \"Biel (BE)\"));\n    private ListProperty<String> allCapitals = new SimpleListProperty<>(FXCollections.observableArrayList(\"Zurich (ZH)\", \"Geneva (GE)\", \"Basel (BS)\", \"Lausanne (VD)\", \"Bern (BE)\", \"Winterthur (ZH)\", \"Lucerne (LU)\", \"St. Gallen (SG)\", \"Lugano (TI)\", \"Biel (BE)\"));\n    private ListProperty<String> allContinents = new SimpleListProperty<>(FXCollections.observableArrayList(\"Africa\", \"Asia\", \"Europe\", \"North America\", \"South America\", \"Australia\"));\n\n    private ListProperty<String> continents = new SimpleListProperty<>(FXCollections.observableArrayList(\"Europe\"));\n\n    private ObjectProperty<String> capital = new SimpleObjectProperty<>(\"Bern (BE)\");\n    private ListProperty<String> germanCities = new SimpleListProperty<>(FXCollections.observableArrayList(\"Zurich (ZH)\", \"Basel (BS)\", \"Bern (BE)\", \"Winterthur (ZH)\", \"Lucerne (LU)\", \"St. Gallen (SG)\", \"Biel (BE)\"));\n\n    public String getName() {\n        return name.get();\n    }\n\n    public StringProperty nameProperty() {\n        return name;\n    }\n\n    public String getIso() {\n        return iso.get();\n    }\n\n    public StringProperty isoProperty() {\n        return iso;\n    }\n\n    public boolean isIndependence() {\n        return independence.get();\n    }\n\n    public BooleanProperty independenceProperty() {\n        return independence;\n    }\n\n    public LocalDate getIndependenceDay() {\n        return independenceDay.get();\n    }\n\n    public ObjectProperty<LocalDate> independenceDayProperty() {\n        return independenceDay;\n    }\n\n    public String getCurrencyShort() {\n        return currencyShort.get();\n    }\n\n    public StringProperty currencyShortProperty() {\n        return currencyShort;\n    }\n\n    public String getCurrencyLong() {\n        return currencyLong.get();\n    }\n\n    public StringProperty currencyLongProperty() {\n        return currencyLong;\n    }\n\n    public int getPopulation() {\n        return population.get();\n    }\n\n    public IntegerProperty populationProperty() {\n        return population;\n    }\n\n    public double getArea() {\n        return area.get();\n    }\n\n    public DoubleProperty areaProperty() {\n        return area;\n    }\n\n    public String getTld() {\n        return tld.get();\n    }\n\n    public StringProperty tldProperty() {\n        return tld;\n    }\n\n    public String getDateFormat() {\n        return dateFormat.get();\n    }\n\n    public StringProperty dateFormatProperty() {\n        return dateFormat;\n    }\n\n    public String getDriverSide() {\n        return driverSide.get();\n    }\n\n    public ObjectProperty<String> driverSideProperty() {\n        return driverSide;\n    }\n\n    public String getTimeZone() {\n        return timeZone.get();\n    }\n\n    public StringProperty timeZoneProperty() {\n        return timeZone;\n    }\n\n    public String getSummerTimeZone() {\n        return summerTimeZone.get();\n    }\n\n    public StringProperty summerTimeZoneProperty() {\n        return summerTimeZone;\n    }\n\n    public ObservableList<String> getAllSides() {\n        return allSides.get();\n    }\n\n    public ListProperty<String> allSidesProperty() {\n        return allSides;\n    }\n\n    public ObservableList<String> getAllCities() {\n        return allCities.get();\n    }\n\n    public ListProperty<String> allCitiesProperty() {\n        return allCities;\n    }\n\n    public ObservableList<String> getAllCapitals() {\n        return allCapitals.get();\n    }\n\n    public ListProperty<String> allCapitalsProperty() {\n        return allCapitals;\n    }\n\n    public ObservableList<String> getAllContinents() {\n        return allContinents.get();\n    }\n\n    public ListProperty<String> allContinentsProperty() {\n        return allContinents;\n    }\n\n    public ObservableList<String> getContinents() {\n        return continents.get();\n    }\n\n    public ListProperty<String> continentsProperty() {\n        return continents;\n    }\n\n    public String getCapital() {\n        return capital.get();\n    }\n\n    public ObjectProperty<String> capitalProperty() {\n        return capital;\n    }\n\n    public ObservableList<String> getGermanCities() {\n        return germanCities.get();\n    }\n\n    public ListProperty<String> germanCitiesProperty() {\n        return germanCities;\n    }\n}\n"
  },
  {
    "path": "formsfx-demo/src/main/java/com/dlsc/formsfx/demo/model/DemoModel.java",
    "content": "package com.dlsc.formsfx.demo.model;\n\nimport com.dlsc.formsfx.model.structure.Field;\nimport com.dlsc.formsfx.model.structure.Form;\nimport com.dlsc.formsfx.model.structure.Group;\nimport com.dlsc.formsfx.model.structure.Section;\nimport com.dlsc.formsfx.model.util.ResourceBundleService;\nimport com.dlsc.formsfx.model.validators.DoubleRangeValidator;\nimport com.dlsc.formsfx.model.validators.IntegerRangeValidator;\nimport com.dlsc.formsfx.model.validators.RegexValidator;\nimport com.dlsc.formsfx.model.validators.StringLengthValidator;\nimport com.dlsc.formsfx.view.controls.SimpleCheckBoxControl;\nimport com.dlsc.formsfx.view.controls.SimpleRadioButtonControl;\nimport com.dlsc.formsfx.view.util.ColSpan;\n\nimport java.util.Locale;\nimport java.util.ResourceBundle;\n\n/**\n * This class is used to create the form and holds all the necessary data. This\n * class acts as a singleton where the current instance is available using\n * {@code getInstance}.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic final class DemoModel {\n\n    private Country country = new Country();\n\n    /**\n     * These are the resource bundles for german and english.\n     */\n    private ResourceBundle rbDE = ResourceBundle.getBundle(\"demo-locale\", new Locale(\"de\", \"CH\"));\n    private ResourceBundle rbEN = ResourceBundle.getBundle(\"demo-locale\", new Locale(\"en\", \"UK\"));\n\n    /**\n     * The default locale is English, thus the {@code ResourceBundleService} is\n     * initialised with it.\n     */\n    private ResourceBundleService rbs = new ResourceBundleService(rbEN);\n\n    private Form formInstance;\n\n    /**\n     * Creates or simply returns to form singleton instance.\n     *\n     * @return Returns the form instance.\n     */\n    public Form getFormInstance() {\n        if (formInstance == null) {\n            createForm();\n        }\n\n        return formInstance;\n    }\n\n    /**\n     * Creates a new form instance with the required information.\n     */\n    private void createForm() {\n        formInstance = Form.of(\n                Group.of(\n                        Field.ofStringType(country.nameProperty())\n                                .label(\"country_label\")\n                                .placeholder(\"country_placeholder\")\n                                .required(\"required_error_message\")\n                                .validate(StringLengthValidator.atLeast(2, \"country_error_message\")),\n                        Field.ofStringType(country.isoProperty())\n                                .label(\"ISO_3166_label\")\n                                .placeholder(\"ISO_3166_placeholder\")\n                                .required(\"required_error_message\")\n                                .validate(StringLengthValidator.exactly(2, \"ISO_3166_error_message\")),\n                        Field.ofBooleanType(country.independenceProperty())\n                                .label(\"independent_label\")\n                                .required(\"required_error_message\"),\n                        Field.ofDate(country.getIndependenceDay())\n                                .label(\"independent_since_label\")\n                                .required(\"required_error_message\")\n                                .placeholder(\"independent_since_placeholder\")\n                ),\n                Section.of(\n                        Field.ofStringType(country.currencyShortProperty())\n                                .label(\"currency_label\")\n                                .placeholder(\"currency_placeholder\")\n                                .validate(StringLengthValidator.exactly(3, \"currency_error_message\"))\n                                .span(ColSpan.HALF),\n                        Field.ofStringType(country.currencyLongProperty())\n                                .label(\"currency_long_label\")\n                                .placeholder(\"currency_long_placeholder\")\n                                .span(ColSpan.HALF),\n                        Field.ofDoubleType(country.areaProperty())\n                                .label(\"area_label\")\n                                .format(\"format_error_message\")\n                                .placeholder(\"area_placeholder\")\n                                .validate(DoubleRangeValidator.atLeast(1, \"area_error_message\"))\n                                .span(ColSpan.HALF),\n                        Field.ofStringType(country.tldProperty())\n                                .label(\"internet_TLD_label\")\n                                .placeholder(\"internet_TLD_placeholder\")\n                                .span(ColSpan.HALF)\n                                .validate(\n                                        StringLengthValidator.exactly(3, \"internet_TLD_error_message\"),\n                                        RegexValidator.forPattern(\"^.[a-z]{2}$\", \"internet_TLD_format_error_message\")\n                                ),\n                        Field.ofStringType(country.dateFormatProperty())\n                                .label(\"date_format_label\")\n                                .placeholder(\"date_format_placeholder\")\n                                .multiline(true)\n                                .span(ColSpan.HALF)\n                                .validate(StringLengthValidator.atLeast(8, \"date_format_error_message\")),\n                        Field.ofSingleSelectionType(country.allSidesProperty(), country.driverSideProperty())\n                                .required(\"required_error_message\")\n                                .label(\"driving_label\")\n                                .span(ColSpan.HALF)\n                                .render(() -> new SimpleRadioButtonControl<>()),\n                        Field.ofStringType(country.timeZoneProperty())\n                                .label(\"time_zone_label\")\n                                .placeholder(\"time_zone_placeholder\")\n                                .span(ColSpan.HALF)\n                                .validate(StringLengthValidator.exactly(3, \"time_zone_error_message\")),\n                        Field.ofStringType(country.summerTimeZoneProperty())\n                                .label(\"summer_time_zone_label\")\n                                .placeholder(\"summer_time_zone_placeholder\")\n                                .span(ColSpan.HALF)\n                                .validate(StringLengthValidator.atLeast(3, \"summer_time_zone_error_message\"))\n                ).title(\"other_information_label\"),\n                Section.of(\n                        Field.ofSingleSelectionType(country.allCapitalsProperty(), country.capitalProperty())\n                                .label(\"capital_label\")\n                                .required(\"required_error_message\")\n                                .tooltip(\"capital_tooltip\")\n                                .span(ColSpan.HALF),\n                        Field.ofIntegerType(country.populationProperty())\n                                .label(\"population_label\")\n                                .format(\"format_error_message\")\n                                .placeholder(\"population_placeholder\")\n                                .required(\"required_error_message\")\n                                .span(ColSpan.HALF)\n                                .validate(IntegerRangeValidator.atLeast(1, \"population_error_message\")),\n                        Field.ofMultiSelectionType(country.allContinentsProperty(), country.continentsProperty())\n                                .label(\"continent_label\")\n                                .required(\"required_error_message\")\n                                .span(ColSpan.HALF)\n                                .render(() -> new SimpleCheckBoxControl<>()),\n                        Field.ofMultiSelectionType(country.allCitiesProperty(), country.germanCitiesProperty())\n                                .label(\"german_cities_label\")\n                                .span(ColSpan.HALF),\n                        Field.ofPasswordType(\"secret\")\n                                .label(\"secret_label\")\n                                .required(\"required_error_message\")\n                                .span(ColSpan.HALF)\n                                .validate(StringLengthValidator.between(1, 10, \"secret_error_message\"))\n                ).title(\"cities_and_population_label\")\n        ).title(\"form_label\")\n                .i18n(rbs);\n\n    }\n\n    /**\n     * Sets the locale of the form.\n     *\n     * @param language The language identifier for the new locale. Either DE or EN.\n     */\n    public void translate(String language) {\n        switch (language) {\n            case \"EN\":\n                rbs.changeLocale(rbEN);\n                break;\n            case \"DE\":\n                rbs.changeLocale(rbDE);\n                break;\n            default:\n                throw new IllegalArgumentException(\"Not a valid locale\");\n        }\n    }\n\n    public Country getCountry() {\n        return country;\n    }\n\n}\n"
  },
  {
    "path": "formsfx-demo/src/main/java/com/dlsc/formsfx/demo/view/RootPane.java",
    "content": "package com.dlsc.formsfx.demo.view;\n\nimport com.dlsc.formsfx.demo.model.DemoModel;\nimport com.dlsc.formsfx.model.structure.Section;\nimport com.dlsc.formsfx.view.renderer.FormRenderer;\nimport com.dlsc.formsfx.view.util.ViewMixin;\nimport javafx.geometry.Insets;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ScrollPane;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.VBox;\n\n/**\n * This class contains all the nodes and regions needed for the demo\n * application.\n *\n * @author Sacha Schmid\n * @author Rinesch Murugathas\n */\npublic class RootPane extends BorderPane implements ViewMixin {\n\n    /**\n     * - controls holds all the nodes which are rendered on the right side.\n     * - scrollContent holds the form.\n     * - languageButtons holds both buttons to change language.\n     * - statusContent holds all the state change labels.\n     * - formButtons holds the save and reset button.\n     * - toggleContent holds the buttons to toggle editable and sections.\n     * - bindingInfo holds the info of property changes.\n     */\n    private GridPane controls;\n    private ScrollPane scrollContent;\n    private HBox languageButtons;\n    private VBox statusContent;\n    private HBox formButtons;\n    private VBox toggleContent;\n    private VBox bindingInfo;\n\n    private Button save;\n    private Button reset;\n    private Button languageDE;\n    private Button languageEN;\n\n    private Label validLabel;\n    private Label changedLabel;\n    private Label persistableLabel;\n    private Label countryLabel;\n    private Label currencyLabel;\n    private Label populationLabel;\n\n    private Button editableToggle;\n    private Button sectionToggle;\n\n    private DemoModel model;\n    private FormRenderer displayForm;\n\n    /**\n     * The constructor to create the nodes and regions.\n     *\n     * @param model\n     *          The model that holds the data.\n     */\n    public RootPane(DemoModel model) {\n        this.model = model;\n        init();\n    }\n\n    /**\n     * This method is used to set up the stylesheets.\n     */\n    @Override\n    public void initializeSelf() {\n        getStylesheets().add(getClass().getResource(\"/style.css\").toExternalForm());\n        getStyleClass().add(\"root-pane\");\n    }\n\n    /**\n     * This method initializes all nodes and regions.\n     */\n    @Override\n    public void initializeParts() {\n        save = new Button(\"Save\");\n        reset = new Button(\"Reset\");\n        save.getStyleClass().add(\"save-button\");\n        reset.getStyleClass().add(\"reset-button\");\n\n        // The language buttons get a picture of the country from the flaticon\n        // font in the css.\n\n        languageDE = new Button(\"\\ue001\");\n        languageEN = new Button(\"\\ue000\");\n\n        validLabel = new Label(\"The form is valid.\");\n        persistableLabel = new Label(\"The form is not persistable.\");\n        changedLabel = new Label(\"The form has not changed.\");\n        countryLabel = new Label(\"Country: \" + model.getCountry().getName());\n        currencyLabel = new Label(\"Currency: \" + model.getCountry().getCurrencyShort());\n        populationLabel = new Label(\"Population: \" + model.getCountry().getPopulation());\n\n        editableToggle = new Button(\"Toggle Editable\");\n        sectionToggle = new Button(\"Toggle Sections\");\n        editableToggle.getStyleClass().add(\"toggle-button\");\n        sectionToggle.getStyleClass().add(\"toggle-button\");\n\n        languageButtons = new HBox();\n        statusContent = new VBox();\n        formButtons = new HBox();\n        toggleContent = new VBox();\n        bindingInfo = new VBox();\n\n        controls = new GridPane();\n        scrollContent = new ScrollPane();\n\n        displayForm = new FormRenderer(model.getFormInstance());\n    }\n\n    /**\n     * This method sets up the necessary bindings for the logic of the\n     * application.\n     */\n    @Override\n    public void setupBindings() {\n        save.disableProperty().bind(model.getFormInstance().persistableProperty().not());\n        reset.disableProperty().bind(model.getFormInstance().changedProperty().not());\n        displayForm.prefWidthProperty().bind(scrollContent.prefWidthProperty());\n    }\n\n    /**\n     * This method sets up listeners and sets the text of the state change\n     * labels.\n     */\n    @Override\n    public void setupValueChangedListeners() {\n        model.getFormInstance().changedProperty().addListener((observable, oldValue, newValue) -> changedLabel.setText(\"The form has \" + (newValue ? \"\" : \"not \") + \"changed.\"));\n        model.getFormInstance().validProperty().addListener((observable, oldValue, newValue) -> validLabel.setText(\"The form is \" + (newValue ? \"\" : \"not \") + \"valid.\"));\n        model.getFormInstance().persistableProperty().addListener((observable, oldValue, newValue) -> persistableLabel.setText(\"The form is \" + (newValue ? \"\" : \"not \") + \"persistable.\"));\n\n        model.getCountry().nameProperty().addListener((observable, oldValue, newValue) -> countryLabel.setText(\"Country: \" + newValue));\n        model.getCountry().currencyShortProperty().addListener((observable, oldValue, newValue) -> currencyLabel.setText(\"Currency: \" + newValue));\n        model.getCountry().populationProperty().addListener((observable, oldValue, newValue) -> populationLabel.setText(\"Population: \" + newValue));\n    }\n\n    /**\n     * This method sets up the handling for all the button clicks.\n     */\n    @Override\n    public void setupEventHandlers() {\n        reset.setOnAction(event -> model.getFormInstance().reset());\n        save.setOnAction(event -> model.getFormInstance().persist());\n\n        languageDE.setOnAction(event -> {\n            model.translate(\"DE\");\n            languageDE.setDisable(true);\n            languageEN.setDisable(false);\n        });\n\n        languageEN.setOnAction(event -> {\n            model.translate(\"EN\");\n            languageEN.setDisable(true);\n            languageDE.setDisable(false);\n        });\n\n        sectionToggle.setOnAction(event -> model.getFormInstance().getGroups().stream().filter(s -> s instanceof Section).forEach(s -> {\n            Section sec = (Section) s;\n            sec.collapse(!sec.isCollapsed());\n        }));\n        \n        editableToggle.setOnAction(event -> model.getFormInstance().getFields().forEach(s -> s.editable(!s.isEditable())));\n    }\n\n    /**\n     * This method is used to layout the nodes and regions properly.\n     */\n    @Override\n    public void layoutParts() {\n        scrollContent.setContent(displayForm);\n        scrollContent.setFitToWidth(true);\n        scrollContent.getStyleClass().add(\"scroll-pane\");\n\n        languageDE.getStyleClass().addAll(\"flaticon\", \"lang-button\", \"lang-button--left\");\n        languageEN.getStyleClass().addAll(\"flaticon\", \"lang-button\", \"lang-button--right\");\n\n        languageEN.setDisable(true);\n\n        languageDE.setMaxWidth(Double.MAX_VALUE);\n        languageEN.setMaxWidth(Double.MAX_VALUE);\n        HBox.setHgrow(languageDE, Priority.ALWAYS);\n        HBox.setHgrow(languageEN, Priority.ALWAYS);\n        languageButtons.getChildren().addAll(languageDE, languageEN);\n        languageButtons.setPadding(new Insets(10));\n        controls.add(languageButtons, 0, 0);\n\n        statusContent.setPadding(new Insets(10));\n        statusContent.getChildren().addAll(validLabel, changedLabel, persistableLabel);\n        statusContent.setSpacing(10);\n        statusContent.setPrefWidth(200);\n        statusContent.getStyleClass().add(\"bordered\");\n        controls.add(statusContent, 0, 1);\n\n        save.setMaxWidth(Double.MAX_VALUE);\n        reset.setMaxWidth(Double.MAX_VALUE);\n        HBox.setHgrow(save, Priority.ALWAYS);\n        HBox.setHgrow(reset, Priority.ALWAYS);\n        formButtons.getChildren().addAll(reset, save);\n        formButtons.setPadding(new Insets(10));\n        formButtons.setSpacing(10);\n        formButtons.setPrefWidth(200);\n        formButtons.getStyleClass().add(\"bordered\");\n        controls.add(formButtons, 0, 2);\n\n        bindingInfo.setPadding(new Insets(10));\n        bindingInfo.getChildren().addAll(countryLabel, currencyLabel, populationLabel);\n        bindingInfo.setSpacing(10);\n        bindingInfo.setPrefWidth(200);\n        bindingInfo.getStyleClass().add(\"bordered\");\n        controls.add(bindingInfo, 0, 3);\n\n        editableToggle.setMaxWidth(Double.MAX_VALUE);\n        sectionToggle.setMaxWidth(Double.MAX_VALUE);\n        HBox.setHgrow(editableToggle, Priority.ALWAYS);\n        HBox.setHgrow(sectionToggle, Priority.ALWAYS);\n        toggleContent.setPadding(new Insets(10));\n        toggleContent.getChildren().addAll(editableToggle, sectionToggle);\n        toggleContent.setSpacing(10);\n        toggleContent.setPrefWidth(200);\n        toggleContent.getStyleClass().add(\"bordered\");\n        controls.add(toggleContent, 0, 4);\n\n        controls.setPrefWidth(200);\n        controls.getStyleClass().add(\"controls\");\n\n        setCenter(scrollContent);\n        setRight(controls);\n    }\n\n}\n"
  },
  {
    "path": "formsfx-demo/src/main/java/module-info.java",
    "content": "module com.dlsc.formsfx.demo {\n\n    requires javafx.controls;\n    requires com.dlsc.formsfx;\n\n    exports com.dlsc.formsfx.demo;\n}"
  },
  {
    "path": "formsfx-demo/src/main/resources/demo-locale_de_CH.properties",
    "content": "capital_label = Hauptstadt\ngerman_cities_label = Deutschsprachige St\\u00e4dte\ncontinent_label = Kontinent\npopulation_label = Einwohner\nsecret_label = Geheimnis\ncities_and_population_label = St\\u00e4dte und Einwohner\ncurrency_label = W\\u00e4hrung\ncurrency_long_label = W\\u00e4hrung lang\narea_label = Fl\\u00e4che\ninternet_TLD_label = Internet Endung\ndate_format_label = Datumsformat\ntime_zone_label = Zeitzone\nsummer_time_zone_label = Sommer-Zeitzone\nother_information_label = Weitere Informationen\ncountry_label = Land\nindependent_since_label = Independence Day\nindependent_since_placeholder = Independent Since\nISO_3166_label = ISO 3166\nindependent_label = Unabh\\u00e4ngig\nform_label = L\\u00e4nder Formular\ndriving_label = Fahrerseite\n\npopulation_placeholder = Einwohner des Landes\ncurrency_placeholder = W\\u00e4hrungsk\\u00fcrzel des Landes\ncurrency_long_placeholder = W\\u00e4hrung des Landes ausgeschrieben\narea_placeholder = Fl\\u00e4che des Landes\ninternet_TLD_placeholder = Internet Endung des Landes\ndate_format_placeholder = Datumsformat des Landes\ntime_zone_placeholder = Zeitzone des Landes\nsummer_time_zone_placeholder = Sommer-Zeitzone des Landes\ncountry_placeholder = Landesname\nISO_3166_placeholder = ISO 3166 code des Landes\n\nformat_error_message = Keine g\\u00fcltiges Format f\\u00fcr dieses Feld\nrequired_error_message = Feld darf nicht leer sein\npopulation_error_message = Einwohnerzahl muss gr\\u00f6sser als 1 sein\ncurrency_error_message = W\\u00e4hrungsk\\u00fcrzel muss genau 3 Zeichen lang sein\narea_error_message = Fl\\u00e4che muss gr\\u00f6sser als 1 sein\ninternet_TLD_error_message = Internet Endung muss genau 3 Zeichen lang sein\ninternet_TLD_format_error_message = Internet Endung muss dem Format .xy folgen\ndate_format_error_message = Datumsformat muss l\\u00e4nger als 8 Zeichen sein\ntime_zone_error_message = Zeitzone muss genau 3 Zeichen lang sein\nsummer_time_zone_error_message = Sommer-Zeitzone muss l\\u00e4nger als 3 Zeichen sein\ncountry_error_message = Landesname muss l\\u00e4nger als 2 Zeichen sein\nISO_3166_error_message = ISO 3166 code muss genau 2 Zeichen lang sein\nsecret_error_message = Passwortl\\u00e4nge muss zwischen 1 und 10 Zeichen sein\n\ncapital_tooltip = Die Hauptstadt des Landes ist oftmals nicht die gr\\u00f6sste Stadt.\n"
  },
  {
    "path": "formsfx-demo/src/main/resources/demo-locale_en_UK.properties",
    "content": "capital_label = Capital\ngerman_cities_label = German-speaking Cities\ncontinent_label = Continent\npopulation_label = Population\nsecret_label = Secret\ncities_and_population_label = Cities and Population\ncurrency_label = Currency\ncurrency_long_label = Currency long\narea_label = Area\ninternet_TLD_label = Internet TLD\ndate_format_label = Date Format\ntime_zone_label = Time Zone\nsummer_time_zone_label = Summer Time Zone\nother_information_label = Other Information\ncountry_label = Country\nISO_3166_label = ISO 3166\nindependent_label = Independent\nindependent_since_label = Independence Day\nindependent_since_placeholder = Independent Since\nform_label = Country Form\ndriving_label = Driving on the\n\npopulation_placeholder = Population of country\ncurrency_placeholder = Currency code of country\ncurrency_long_placeholder = Currency in written format\narea_placeholder = Area of country\ninternet_TLD_placeholder = Internet domain of country\ndate_format_placeholder = Date format of country\ntime_zone_placeholder = Time zone of country\nsummer_time_zone_placeholder = Summer time zone of country\ncountry_placeholder = Name of country\nISO_3166_placeholder = ISO 3166 code of country\n\nformat_error_message = Not a valid input for this field\nrequired_error_message = Field must not be empty\npopulation_error_message = Population has to be larger than 1\ncurrency_error_message = Currency code has to be exactly 3 characters long\narea_error_message = Area has to be larger than 1\ninternet_TLD_error_message = Domain length has to be exactly 3 characters long\ninternet_TLD_format_error_message = Domain must follow the .xy format\ndate_format_error_message = Date format has to be longer than 8 characters\ntime_zone_error_message = Time zone has to be exactly 3 characters long\nsummer_time_zone_error_message = Summer time zone has to be at least 3 characters long\ncountry_error_message = Name of country has to be longer than 2 characters\nISO_3166_error_message = ISO 3166 code has to be exactly 2 characters long\nsecret_error_message = Password length must be between 1 and 10 characters\n\ncapital_tooltip = The capital city is often not the largest city.\n"
  },
  {
    "path": "formsfx-demo/src/main/resources/style.css",
    "content": "@font-face {\n    font-family: 'Flaticon';\n    src: url(/flaticon.ttf);\n}\n.field-label-description,\n.field-value-description {\n    -fx-text-fill: red;\n}\n.root-pane {\n    -fx-padding: 0;\n    -fx-font-family: 'Arial';\n}\n\n.flaticon .text {\n    -fx-font-family: 'Flaticon';\n}\n\n.lang-button {\n    -fx-border-color: dimgrey;\n    -fx-background-color: white;\n    -fx-background-insets: 0 0 0 0, 0, 1, 2;\n    -fx-border-width: 1px;\n    -fx-text-fill: dimgrey;\n    -fx-font-size: 24px;\n    -fx-cursor: hand;\n}\n\n.lang-button:disabled {\n    -fx-background-color: dimgrey;\n    -fx-opacity: 1;\n    -fx-text-fill: white;\n}\n\n.lang-button--left {\n    -fx-background-radius: 5px 0 0 5px;\n    -fx-border-radius: 5px 0 0 5px;\n}\n\n.lang-button--right {\n    -fx-background-radius: 0 5px 5px 0;\n    -fx-border-radius: 0 5px 5px 0;\n}\n\n.scroll-pane {\n    -fx-padding: 0;\n}\n\n.content {\n    -fx-padding: 10;\n}\n\n.controls {\n    -fx-background-color: #e0e0e0;\n    -fx-border-color: #b1b1b1;\n    -fx-border-width: 0 0 0 1px;\n}\n\n.save-button,\n.reset-button,\n.toggle-button {\n    -fx-font-size: 14px;\n    -fx-font-weight: normal;\n    -fx-text-alignment: center;\n    -fx-cursor: hand;\n    -fx-background-radius: 4px;\n    -fx-background-insets: 0 0 0 0, 0, 1, 2;\n    -fx-border-width: 1px;\n    -fx-border-radius: 4px;\n}\n\n.save-button {\n    -fx-text-fill: #fff;\n    -fx-background-color: #337ab7;\n    -fx-border-color: darkblue;\n}\n\n.save-button:hover {\n    -fx-background-color: #2d6da4;\n}\n\n.save-button:pressed {\n    -fx-background-color: #286192;\n}\n\n.reset-button,\n.toggle-button {\n    -fx-text-fill: #333;\n    -fx-background-color: #fff;\n    -fx-border-color: #ababab;\n}\n\n.reset-button:hover,\n.toggle-button:hover {\n    -fx-background-color: #f0f0f0;\n}\n\n.reset-button:pressed {\n    -fx-text-fill: #fff;\n    -fx-background-color: red;\n    -fx-border-color: darkred;\n}\n\n.bordered {\n    -fx-border-color: #b1b1b1 transparent transparent transparent;\n    -fx-border-width: 1px;\n}"
  },
  {
    "path": "jreleaser.yml",
    "content": "#\n# SPDX-License-Identifier: Apache-2.0\n#\n# Copyright 2021 Dirk Lemmermann Software & Consulting (dlsc.com)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nproject:\n  name: FormsFX\n  description: A framework for easily creating forms for a JavaFX UI.\n  website: https://github.com/dlsc-software-consulting-gmbh/GemsFX/\n  authors:\n    - Dirk Lemmermann\n  license: Apache-2.0\n  extraProperties:\n    inceptionYear: 2022\n\nrelease:\n  github:\n    branch: master-11\n    overwrite: true\n    milestone:\n      name: '{{projectVersion}}'\n    changelog:\n      sort: DESC\n      formatted: ALWAYS\n      format: '- {{commitShortHash}} {{commitTitle}}'\n      contributors:\n        format: '- {{contributorName}}{{#contributorUsernameAsLink}} ({{.}}){{/contributorUsernameAsLink}}'\n      hide:\n        contributors:\n          - 'GitHub'"
  },
  {
    "path": "mvnw",
    "content": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n# ----------------------------------------------------------------------------\n\n# ----------------------------------------------------------------------------\n# Maven Start Up Batch script\n#\n# Required ENV vars:\n# ------------------\n#   JAVA_HOME - location of a JDK home dir\n#\n# Optional ENV vars\n# -----------------\n#   M2_HOME - location of maven2's installed home dir\n#   MAVEN_OPTS - parameters passed to the Java VM when running Maven\n#     e.g. to debug Maven itself, use\n#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n# ----------------------------------------------------------------------------\n\nif [ -z \"$MAVEN_SKIP_RC\" ] ; then\n\n  if [ -f /usr/local/etc/mavenrc ] ; then\n    . /usr/local/etc/mavenrc\n  fi\n\n  if [ -f /etc/mavenrc ] ; then\n    . /etc/mavenrc\n  fi\n\n  if [ -f \"$HOME/.mavenrc\" ] ; then\n    . \"$HOME/.mavenrc\"\n  fi\n\nfi\n\n# OS specific support.  $var _must_ be set to either true or false.\ncygwin=false;\ndarwin=false;\nmingw=false\ncase \"`uname`\" in\n  CYGWIN*) cygwin=true ;;\n  MINGW*) mingw=true;;\n  Darwin*) darwin=true\n    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home\n    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html\n    if [ -z \"$JAVA_HOME\" ]; then\n      if [ -x \"/usr/libexec/java_home\" ]; then\n        export JAVA_HOME=\"`/usr/libexec/java_home`\"\n      else\n        export JAVA_HOME=\"/Library/Java/Home\"\n      fi\n    fi\n    ;;\nesac\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  if [ -r /etc/gentoo-release ] ; then\n    JAVA_HOME=`java-config --jre-home`\n  fi\nfi\n\nif [ -z \"$M2_HOME\" ] ; then\n  ## resolve links - $0 may be a link to maven's home\n  PRG=\"$0\"\n\n  # need this for relative symlinks\n  while [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n      PRG=\"$link\"\n    else\n      PRG=\"`dirname \"$PRG\"`/$link\"\n    fi\n  done\n\n  saveddir=`pwd`\n\n  M2_HOME=`dirname \"$PRG\"`/..\n\n  # make it fully qualified\n  M2_HOME=`cd \"$M2_HOME\" && pwd`\n\n  cd \"$saveddir\"\n  # echo Using m2 at $M2_HOME\nfi\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched\nif $cygwin ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --unix \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --unix \"$CLASSPATH\"`\nfi\n\n# For Mingw, ensure paths are in UNIX format before anything is touched\nif $mingw ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=\"`(cd \"$M2_HOME\"; pwd)`\"\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=\"`(cd \"$JAVA_HOME\"; pwd)`\"\nfi\n\nif [ -z \"$JAVA_HOME\" ]; then\n  javaExecutable=\"`which javac`\"\n  if [ -n \"$javaExecutable\" ] && ! [ \"`expr \\\"$javaExecutable\\\" : '\\([^ ]*\\)'`\" = \"no\" ]; then\n    # readlink(1) is not available as standard on Solaris 10.\n    readLink=`which readlink`\n    if [ ! `expr \"$readLink\" : '\\([^ ]*\\)'` = \"no\" ]; then\n      if $darwin ; then\n        javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n        javaExecutable=\"`cd \\\"$javaHome\\\" && pwd -P`/javac\"\n      else\n        javaExecutable=\"`readlink -f \\\"$javaExecutable\\\"`\"\n      fi\n      javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n      javaHome=`expr \"$javaHome\" : '\\(.*\\)/bin'`\n      JAVA_HOME=\"$javaHome\"\n      export JAVA_HOME\n    fi\n  fi\nfi\n\nif [ -z \"$JAVACMD\" ] ; then\n  if [ -n \"$JAVA_HOME\"  ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n      # IBM's JDK on AIX uses strange locations for the executables\n      JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n      JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n  else\n    JAVACMD=\"`\\\\unset -f command; \\\\command -v java`\"\n  fi\nfi\n\nif [ ! -x \"$JAVACMD\" ] ; then\n  echo \"Error: JAVA_HOME is not defined correctly.\" >&2\n  echo \"  We cannot execute $JAVACMD\" >&2\n  exit 1\nfi\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  echo \"Warning: JAVA_HOME environment variable is not set.\"\nfi\n\nCLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher\n\n# traverses directory structure from process work directory to filesystem root\n# first directory with .mvn subdirectory is considered project base directory\nfind_maven_basedir() {\n\n  if [ -z \"$1\" ]\n  then\n    echo \"Path not specified to find_maven_basedir\"\n    return 1\n  fi\n\n  basedir=\"$1\"\n  wdir=\"$1\"\n  while [ \"$wdir\" != '/' ] ; do\n    if [ -d \"$wdir\"/.mvn ] ; then\n      basedir=$wdir\n      break\n    fi\n    # workaround for JBEAP-8937 (on Solaris 10/Sparc)\n    if [ -d \"${wdir}\" ]; then\n      wdir=`cd \"$wdir/..\"; pwd`\n    fi\n    # end of workaround\n  done\n  echo \"${basedir}\"\n}\n\n# concatenates all lines of a file\nconcat_lines() {\n  if [ -f \"$1\" ]; then\n    echo \"$(tr -s '\\n' ' ' < \"$1\")\"\n  fi\n}\n\nBASE_DIR=`find_maven_basedir \"$(pwd)\"`\nif [ -z \"$BASE_DIR\" ]; then\n  exit 1;\nfi\n\n##########################################################################################\n# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central\n# This allows using the maven wrapper in projects that prohibit checking in binary data.\n##########################################################################################\nif [ -r \"$BASE_DIR/.mvn/wrapper/maven-wrapper.jar\" ]; then\n    if [ \"$MVNW_VERBOSE\" = true ]; then\n      echo \"Found .mvn/wrapper/maven-wrapper.jar\"\n    fi\nelse\n    if [ \"$MVNW_VERBOSE\" = true ]; then\n      echo \"Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ...\"\n    fi\n    if [ -n \"$MVNW_REPOURL\" ]; then\n      jarUrl=\"$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar\"\n    else\n      jarUrl=\"https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar\"\n    fi\n    while IFS=\"=\" read key value; do\n      case \"$key\" in (wrapperUrl) jarUrl=\"$value\"; break ;;\n      esac\n    done < \"$BASE_DIR/.mvn/wrapper/maven-wrapper.properties\"\n    if [ \"$MVNW_VERBOSE\" = true ]; then\n      echo \"Downloading from: $jarUrl\"\n    fi\n    wrapperJarPath=\"$BASE_DIR/.mvn/wrapper/maven-wrapper.jar\"\n    if $cygwin; then\n      wrapperJarPath=`cygpath --path --windows \"$wrapperJarPath\"`\n    fi\n\n    if command -v wget > /dev/null; then\n        if [ \"$MVNW_VERBOSE\" = true ]; then\n          echo \"Found wget ... using wget\"\n        fi\n        if [ -z \"$MVNW_USERNAME\" ] || [ -z \"$MVNW_PASSWORD\" ]; then\n            wget \"$jarUrl\" -O \"$wrapperJarPath\" || rm -f \"$wrapperJarPath\"\n        else\n            wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD \"$jarUrl\" -O \"$wrapperJarPath\" || rm -f \"$wrapperJarPath\"\n        fi\n    elif command -v curl > /dev/null; then\n        if [ \"$MVNW_VERBOSE\" = true ]; then\n          echo \"Found curl ... using curl\"\n        fi\n        if [ -z \"$MVNW_USERNAME\" ] || [ -z \"$MVNW_PASSWORD\" ]; then\n            curl -o \"$wrapperJarPath\" \"$jarUrl\" -f\n        else\n            curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o \"$wrapperJarPath\" \"$jarUrl\" -f\n        fi\n\n    else\n        if [ \"$MVNW_VERBOSE\" = true ]; then\n          echo \"Falling back to using Java to download\"\n        fi\n        javaClass=\"$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java\"\n        # For Cygwin, switch paths to Windows format before running javac\n        if $cygwin; then\n          javaClass=`cygpath --path --windows \"$javaClass\"`\n        fi\n        if [ -e \"$javaClass\" ]; then\n            if [ ! -e \"$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class\" ]; then\n                if [ \"$MVNW_VERBOSE\" = true ]; then\n                  echo \" - Compiling MavenWrapperDownloader.java ...\"\n                fi\n                # Compiling the Java class\n                (\"$JAVA_HOME/bin/javac\" \"$javaClass\")\n            fi\n            if [ -e \"$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class\" ]; then\n                # Running the downloader\n                if [ \"$MVNW_VERBOSE\" = true ]; then\n                  echo \" - Running MavenWrapperDownloader.java ...\"\n                fi\n                (\"$JAVA_HOME/bin/java\" -cp .mvn/wrapper MavenWrapperDownloader \"$MAVEN_PROJECTBASEDIR\")\n            fi\n        fi\n    fi\nfi\n##########################################################################################\n# End of extension\n##########################################################################################\n\nexport MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-\"$BASE_DIR\"}\nif [ \"$MVNW_VERBOSE\" = true ]; then\n  echo $MAVEN_PROJECTBASEDIR\nfi\nMAVEN_OPTS=\"$(concat_lines \"$MAVEN_PROJECTBASEDIR/.mvn/jvm.config\") $MAVEN_OPTS\"\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --path --windows \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --path --windows \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --windows \"$CLASSPATH\"`\n  [ -n \"$MAVEN_PROJECTBASEDIR\" ] &&\n    MAVEN_PROJECTBASEDIR=`cygpath --path --windows \"$MAVEN_PROJECTBASEDIR\"`\nfi\n\n# Provide a \"standardized\" way to retrieve the CLI args that will\n# work with both Windows and non-Windows executions.\nMAVEN_CMD_LINE_ARGS=\"$MAVEN_CONFIG $@\"\nexport MAVEN_CMD_LINE_ARGS\n\nWRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\nexec \"$JAVACMD\" \\\n  $MAVEN_OPTS \\\n  $MAVEN_DEBUG_OPTS \\\n  -classpath \"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar\" \\\n  \"-Dmaven.home=${M2_HOME}\" \\\n  \"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}\" \\\n  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG \"$@\"\n"
  },
  {
    "path": "mvnw.cmd",
    "content": "@REM ----------------------------------------------------------------------------\n@REM Licensed to the Apache Software Foundation (ASF) under one\n@REM or more contributor license agreements.  See the NOTICE file\n@REM distributed with this work for additional information\n@REM regarding copyright ownership.  The ASF licenses this file\n@REM to you under the Apache License, Version 2.0 (the\n@REM \"License\"); you may not use this file except in compliance\n@REM with the License.  You may obtain a copy of the License at\n@REM\n@REM    http://www.apache.org/licenses/LICENSE-2.0\n@REM\n@REM Unless required by applicable law or agreed to in writing,\n@REM software distributed under the License is distributed on an\n@REM \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n@REM KIND, either express or implied.  See the License for the\n@REM specific language governing permissions and limitations\n@REM under the License.\n@REM ----------------------------------------------------------------------------\n\n@REM ----------------------------------------------------------------------------\n@REM Maven Start Up Batch script\n@REM\n@REM Required ENV vars:\n@REM JAVA_HOME - location of a JDK home dir\n@REM\n@REM Optional ENV vars\n@REM M2_HOME - location of maven2's installed home dir\n@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands\n@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending\n@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven\n@REM     e.g. to debug Maven itself, use\n@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n@REM ----------------------------------------------------------------------------\n\n@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'\n@echo off\n@REM set title of command window\ntitle %0\n@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'\n@if \"%MAVEN_BATCH_ECHO%\" == \"on\"  echo %MAVEN_BATCH_ECHO%\n\n@REM set %HOME% to equivalent of $HOME\nif \"%HOME%\" == \"\" (set \"HOME=%HOMEDRIVE%%HOMEPATH%\")\n\n@REM Execute a user defined script before this one\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPre\n@REM check for pre script, once with legacy .bat ending and once with .cmd ending\nif exist \"%USERPROFILE%\\mavenrc_pre.bat\" call \"%USERPROFILE%\\mavenrc_pre.bat\" %*\nif exist \"%USERPROFILE%\\mavenrc_pre.cmd\" call \"%USERPROFILE%\\mavenrc_pre.cmd\" %*\n:skipRcPre\n\n@setlocal\n\nset ERROR_CODE=0\n\n@REM To isolate internal variables from possible post scripts, we use another setlocal\n@setlocal\n\n@REM ==== START VALIDATION ====\nif not \"%JAVA_HOME%\" == \"\" goto OkJHome\n\necho.\necho Error: JAVA_HOME not found in your environment. >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n:OkJHome\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto init\n\necho.\necho Error: JAVA_HOME is set to an invalid directory. >&2\necho JAVA_HOME = \"%JAVA_HOME%\" >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n@REM ==== END VALIDATION ====\n\n:init\n\n@REM Find the project base dir, i.e. the directory that contains the folder \".mvn\".\n@REM Fallback to current working directory if not found.\n\nset MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%\nIF NOT \"%MAVEN_PROJECTBASEDIR%\"==\"\" goto endDetectBaseDir\n\nset EXEC_DIR=%CD%\nset WDIR=%EXEC_DIR%\n:findBaseDir\nIF EXIST \"%WDIR%\"\\.mvn goto baseDirFound\ncd ..\nIF \"%WDIR%\"==\"%CD%\" goto baseDirNotFound\nset WDIR=%CD%\ngoto findBaseDir\n\n:baseDirFound\nset MAVEN_PROJECTBASEDIR=%WDIR%\ncd \"%EXEC_DIR%\"\ngoto endDetectBaseDir\n\n:baseDirNotFound\nset MAVEN_PROJECTBASEDIR=%EXEC_DIR%\ncd \"%EXEC_DIR%\"\n\n:endDetectBaseDir\n\nIF NOT EXIST \"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\" goto endReadAdditionalConfig\n\n@setlocal EnableExtensions EnableDelayedExpansion\nfor /F \"usebackq delims=\" %%a in (\"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a\n@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%\n\n:endReadAdditionalConfig\n\nSET MAVEN_JAVA_EXE=\"%JAVA_HOME%\\bin\\java.exe\"\nset WRAPPER_JAR=\"%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.jar\"\nset WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\nset DOWNLOAD_URL=\"https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar\"\n\nFOR /F \"usebackq tokens=1,2 delims==\" %%A IN (\"%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.properties\") DO (\n    IF \"%%A\"==\"wrapperUrl\" SET DOWNLOAD_URL=%%B\n)\n\n@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central\n@REM This allows using the maven wrapper in projects that prohibit checking in binary data.\nif exist %WRAPPER_JAR% (\n    if \"%MVNW_VERBOSE%\" == \"true\" (\n        echo Found %WRAPPER_JAR%\n    )\n) else (\n    if not \"%MVNW_REPOURL%\" == \"\" (\n        SET DOWNLOAD_URL=\"%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar\"\n    )\n    if \"%MVNW_VERBOSE%\" == \"true\" (\n        echo Couldn't find %WRAPPER_JAR%, downloading it ...\n        echo Downloading from: %DOWNLOAD_URL%\n    )\n\n    powershell -Command \"&{\"^\n\t\t\"$webclient = new-object System.Net.WebClient;\"^\n\t\t\"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {\"^\n\t\t\"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');\"^\n\t\t\"}\"^\n\t\t\"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')\"^\n\t\t\"}\"\n    if \"%MVNW_VERBOSE%\" == \"true\" (\n        echo Finished downloading %WRAPPER_JAR%\n    )\n)\n@REM End of extension\n\n@REM Provide a \"standardized\" way to retrieve the CLI args that will\n@REM work with both Windows and non-Windows executions.\nset MAVEN_CMD_LINE_ARGS=%*\n\n%MAVEN_JAVA_EXE% ^\n  %JVM_CONFIG_MAVEN_PROPS% ^\n  %MAVEN_OPTS% ^\n  %MAVEN_DEBUG_OPTS% ^\n  -classpath %WRAPPER_JAR% ^\n  \"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%\" ^\n  %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*\nif ERRORLEVEL 1 goto error\ngoto end\n\n:error\nset ERROR_CODE=1\n\n:end\n@endlocal & set ERROR_CODE=%ERROR_CODE%\n\nif not \"%MAVEN_SKIP_RC%\"==\"\" goto skipRcPost\n@REM check for post script, once with legacy .bat ending and once with .cmd ending\nif exist \"%USERPROFILE%\\mavenrc_post.bat\" call \"%USERPROFILE%\\mavenrc_post.bat\"\nif exist \"%USERPROFILE%\\mavenrc_post.cmd\" call \"%USERPROFILE%\\mavenrc_post.cmd\"\n:skipRcPost\n\n@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'\nif \"%MAVEN_BATCH_PAUSE%\"==\"on\" pause\n\nif \"%MAVEN_TERMINATE_CMD%\"==\"on\" exit %ERROR_CODE%\n\ncmd /C exit /B %ERROR_CODE%\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <name>FormsFX</name>\n    <groupId>com.dlsc.formsfx</groupId>\n    <artifactId>formsfx</artifactId>\n    <packaging>pom</packaging>\n    <version>11.6.0</version>\n\n    <parent>\n        <groupId>com.dlsc</groupId>\n        <artifactId>dlsc-maven-parent</artifactId>\n        <version>1.0.0</version>\n    </parent>\n\n    <modules>\n        <module>formsfx-core</module>\n        <module>formsfx-demo</module>\n    </modules>\n\n    <properties>\n        <project.identifier>formsfx</project.identifier>\n        <project.github.repository>dlsc-software-consulting-gmbh/FormsFX</project.github.repository>\n        <local.repository.path>/tmp/repository</local.repository.path>\n        <java.version>11</java.version>\n        <javafx.version>17.0.1</javafx.version>\n        <sonar.projectKey>dlsc-software-consulting-gmbh_FormsFX</sonar.projectKey>\n        <sonar.organization>dlsc-software-consulting-gmbh</sonar.organization>\n        <sonar.host.url>https://sonarcloud.io</sonar.host.url>\n        <sonar.moduleKey>${project.artifactId}</sonar.moduleKey>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <maven.deploy.skip>true</maven.deploy.skip>\n    </properties>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.8.1</version>\n                <configuration>\n                    <source>11</source>\n                    <target>11</target>\n                    <release>11</release>\n                </configuration>\n            </plugin>\n\n        </plugins>\n    </build>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-controls</artifactId>\n                <version>${javafx.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-fxml</artifactId>\n                <version>${javafx.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>junit</groupId>\n                <artifactId>junit</artifactId>\n                <version>4.13.1</version>\n            </dependency>\n\n        </dependencies>\n    </dependencyManagement>\n\n</project>\n"
  }
]