[
  {
    "path": ".editorconfig",
    "content": "# EditorConfig is awesome: http://EditorConfig.org\n\nroot = true\n\n[*]\nend_of_line = lf\ninsert_final_newline = true\ncharset = utf-8\nindent_style = space\nindent_size = 4\n\n[*.yml]\nindent_size = 2\n"
  },
  {
    "path": ".gitattributes",
    "content": "#\n# https://help.github.com/articles/dealing-with-line-endings/\n#\n# These are explicitly windows files and should use crlf\n*.bat           text eol=crlf\n\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: davidmc24\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**Prerequisites**\n\n* [ ] Are you running the latest version of the plugin? (Check [releases](https://github.com/davidmc24/gradle-avro-plugin/releases))\n* [ ] Are you running a supported version of Gradle? (Check the [README](https://github.com/davidmc24/gradle-avro-plugin/blob/master/README.md))\n* [ ] Are you running a supported version of Apache Avro? (Check the [README](https://github.com/davidmc24/gradle-avro-plugin/blob/master/README.md))\n* [ ] Are you running a supported version of Java? (Check the [README](https://github.com/davidmc24/gradle-avro-plugin/blob/master/README.md))\n* [ ] Did you check to see if an [issue](https://github.com/davidmc24/gradle-avro-plugin/issues) has already been submitted?\n* [ ] Are you reporting to the correct repository?  If your schema doesn't work with the Apache Avro CLI tool either, it's not a problem with this plugin.  Running your file through the `CLIComparisonTest` in the sample project under the `test-project` directory can help diagnose this.\n* [ ] Did you perform a cursory search?\n\nFor more information, see the [CONTRIBUTING](https://github.com/davidmc24/gradle-avro-plugin/blob/master/CONTRIBUTING.md) guide.\n\n**Describe the bug**\n\nA clear and concise description of what the bug is.\n\n**To Reproduce**\n\nSteps to reproduce the behavior:\n\n1. Project set up like this...\n2. Source files like this...\n3. Ran this task...\n4. See error\n\nPlease provide complete input files that reproduce the problem, not fragments.\nWhen possible, please express this using `test-project`.\n\n**Expected behavior**\n\nA clear and concise description of what you expected to happen.\n\n**Environment (please complete the following information):**\n - Gradle Version [e.g. 5.6.1]\n - Apache Avro Version [e.g. 1.8.2]\n - Gradle-Avro Plugin Version [e.g. 0.17.0]\n - Java Version [e.g. 13.0.2]\n - OS: [e.g. Mac OS X Mojave, Windows 10, Ubuntu 16.04]\n\n**Additional context**\n\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Feature requests and ideas\n    url: https://github.com/davidmc24/gradle-avro-plugin/discussions/categories/ideas\n    about: Suggest an idea for this project\n  - name: Questions\n    url: https://github.com/davidmc24/gradle-avro-plugin/discussions/categories/q-a\n    about: Please ask and answer questions here\n"
  },
  {
    "path": ".github/workflows/avro-compatibility.yml",
    "content": "name: Avro Compatibility Tests\non: [push, pull_request]\njobs:\n  test:\n    name: \"Compatibility: avro ${{ matrix.avro }}/gradle ${{ matrix.gradle }}\"\n    runs-on: \"ubuntu-latest\"\n    strategy:\n      matrix:\n        avro: [\"1.11.0\", \"1.11.1\", \"1.11.2\" , \"1.11.3\"]\n        gradle: [\"5.1\", \"7.6\"]\n        java: [\"8\"]\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-java@v3\n        with:\n          distribution: \"zulu\"\n          java-version: ${{ matrix.java }}\n      - uses: gradle/gradle-build-action@v2\n        with:\n          arguments: testCompatibility -PavroVersion=${{ matrix.avro }} -PgradleVersion=${{ matrix.gradle }}\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI Build\non: [push, pull_request]\njobs:\n  build:\n    name: \"Build\"\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v3\n    - uses: actions/setup-java@v3\n      with:\n        distribution: \"zulu\"\n        java-version: 8\n    - uses: gradle/gradle-build-action@v2\n      with:\n        arguments: build\n#    - uses: codecov/codecov-action@v1\n#      with:\n#        file: ./build/reports/jacoco/test/jacocoTestReport.xml\n#        fail_ci_if_error: true\n"
  },
  {
    "path": ".github/workflows/gradle-compatibility.yml",
    "content": "name: Gradle Compatibility Tests\non: [push, pull_request]\njobs:\n  test:\n    name: \"Compatibility: gradle ${{ matrix.gradle }}/java ${{ matrix.java }}\"\n    runs-on: \"ubuntu-latest\"\n    strategy:\n      matrix:\n        avro: [\"1.11.0\"]\n        gradle: [\n          \"5.1\", \"5.1.1\", \"5.2\", \"5.2.1\", \"5.3\", \"5.3.1\", \"5.4\", \"5.4.1\", \"5.5\", \"5.5.1\", \"5.6\", \"5.6.1\", \"5.6.2\", \"5.6.3\", \"5.6.4\",\n          \"6.0\", \"6.0.1\", \"6.1\", \"6.1.1\", \"6.2\", \"6.2.1\", \"6.2.2\", \"6.3\", \"6.4\", \"6.4.1\", \"6.5\", \"6.5.1\", \"6.6\", \"6.6.1\", \"6.7\", \"6.7.1\",\n          \"6.8\", \"6.8.1\", \"6.8.2\", \"6.8.3\", \"6.9\", \"6.9.1\", \"6.9.2\", \"6.9.3\",\n          \"7.0\", \"7.0.1\", \"7.0.2\", \"7.1\", \"7.1.1\", \"7.2\", \"7.3\", \"7.3.1\", \"7.3.2\", \"7.3.3\", \"7.4\", \"7.4.1\", \"7.4.2\", \"7.5\", \"7.5.1\", \"7.6\"\n          # See here for latest versions: https://services.gradle.org/versions/\n        ]\n        java: [\"8\", \"11\"]\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-java@v3\n        with:\n          distribution: \"zulu\"\n          java-version: ${{ matrix.java }}\n      - uses: gradle/gradle-build-action@v2\n        with:\n          arguments: testCompatibility -PavroVersion=${{ matrix.avro }} -PgradleVersion=${{ matrix.gradle }}\n"
  },
  {
    "path": ".github/workflows/gradle-wrapper-validation.yml",
    "content": "# See https://github.com/marketplace/actions/gradle-wrapper-validation\nname: \"Validate Gradle Wrapper\"\non: [push, pull_request]\n\njobs:\n  validation:\n    name: \"Validation\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: gradle/wrapper-validation-action@v1\n"
  },
  {
    "path": ".github/workflows/java-compatibility.yml",
    "content": "# See https://docs.gradle.org/current/userguide/compatibility.html\nname: Java Compatibility Tests\non: [push, pull_request]\njobs:\n  java8-12:\n    name: \"Compatibility: java ${{ matrix.java }}/gradle ${{ matrix.gradle }}\"\n    runs-on: \"ubuntu-latest\"\n    strategy:\n      matrix:\n        avro: [\"1.11.0\"]\n        gradle: [\"5.1\", \"7.6\"]\n        java: [\"8\", \"11\"]\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-java@v3\n        with:\n          distribution: \"zulu\"\n          java-version: ${{ matrix.java }}\n      - uses: gradle/gradle-build-action@v2\n        with:\n          arguments: testCompatibility -PavroVersion=${{ matrix.avro }} -PgradleVersion=${{ matrix.gradle }}\n  java17:\n    name: \"Compatibility: java ${{ matrix.java }}/gradle ${{ matrix.gradle }}\"\n    runs-on: \"ubuntu-latest\"\n    strategy:\n      matrix:\n        avro: [\"1.11.0\"]\n        gradle: [\"7.3\", \"7.6\"] # See here for latest versions: https://services.gradle.org/versions/\n        java: [\"17\"]\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-java@v3\n        with:\n          distribution: \"zulu\"\n          java-version: ${{ matrix.java }}\n      - uses: gradle/gradle-build-action@v2\n        with:\n          arguments: testCompatibility -PavroVersion=${{ matrix.avro }} -PgradleVersion=${{ matrix.gradle }}\n  java18:\n    name: \"Compatibility: java ${{ matrix.java }}/gradle ${{ matrix.gradle }}\"\n    runs-on: \"ubuntu-latest\"\n    strategy:\n      matrix:\n        avro: [\"1.11.0\"]\n        gradle: [\"7.5\", \"7.6\"] # See here for latest versions: https://services.gradle.org/versions/\n        java: [\"18\"]\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-java@v3\n        with:\n          distribution: \"zulu\"\n          java-version: ${{ matrix.java }}\n      - uses: gradle/gradle-build-action@v2\n        with:\n          arguments: testCompatibility -PavroVersion=${{ matrix.avro }} -PgradleVersion=${{ matrix.gradle }}\n  java-19:\n    name: \"Compatibility: java ${{ matrix.java }}/gradle ${{ matrix.gradle }}\"\n    runs-on: \"ubuntu-latest\"\n    strategy:\n      matrix:\n        avro: [\"1.11.0\"]\n        gradle: [\"7.6\"] # See here for latest versions: https://services.gradle.org/versions/\n        java: [\"19\"]\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-java@v3\n        with:\n          distribution: \"zulu\"\n          java-version: ${{ matrix.java }}\n      - uses: gradle/gradle-build-action@v2\n        continue-on-error: true\n        with:\n          arguments: testCompatibility -PavroVersion=${{ matrix.avro }} -PgradleVersion=${{ matrix.gradle }}\n  java-ea:\n    name: \"Compatibility: java ${{ matrix.java }}/gradle ${{ matrix.gradle }}\"\n    runs-on: \"ubuntu-latest\"\n    strategy:\n      matrix:\n        avro: [\"1.11.0\"]\n        gradle: [\"7.6\"] # See here for latest versions: https://services.gradle.org/versions/\n        java: [\"20-ea\"]\n      fail-fast: false\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-java@v3\n        with:\n          distribution: \"zulu\"\n          java-version: ${{ matrix.java }}\n      - uses: gradle/gradle-build-action@v2\n        continue-on-error: true\n        with:\n          arguments: testCompatibility -PavroVersion=${{ matrix.avro }} -PgradleVersion=${{ matrix.gradle }}\n"
  },
  {
    "path": ".github/workflows/os-compatibility.yml",
    "content": "name: OS Compatibility\non: [push, pull_request]\njobs:\n  build:\n    name: \"Compatibility: ${{ matrix.os }}\"\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        java: [8] # Minimum supported major version\n        os: [ubuntu-latest, windows-latest, macOS-latest] # All supported OS\n    steps:\n    - uses: actions/checkout@v3\n    - uses: actions/setup-java@v3\n      with:\n        distribution: \"zulu\"\n        java-version: ${{ matrix.java }}\n    - uses: gradle/gradle-build-action@v2\n      with:\n        arguments: test\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish package to the Maven Central Repository\non:\n  release:\n    types: [created]\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-java@v3\n        with:\n          distribution: \"zulu\"\n          java-version: 8\n      - uses: gradle/gradle-build-action@v2\n        with:\n          arguments: publishToSonatype closeAndReleaseSonatypeStagingRepository -PsonatypeUsername=${{ secrets.SONATYPE_USERNAME }} -PsonatypePassword=${{ secrets.SONATYPE_PASSWORD }}\n        env:\n          SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}\n          SIGNING_KEY: ${{ secrets.SIGNING_KEY }}\n          SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Compiled source #\n###################\n*.com\n*.class\n*.dll\n*.exe\n*.o\n*.so\n\n# Packages #\n############\n# it's better to unpack these files and commit the raw source\n# git has its own built in compression methods\n*.7z\n*.dmg\n*.gz\n*.iso\n*.jar\n*.rar\n*.tar\n*.zip\n\n# Logs and databases #\n######################\n*.log\n\n# OS generated files #\n######################\n.DS_Store*\nehthumbs.db\nIcon?\nThumbs.db\n\n# Editor Files #\n################\n*~\n*.swp\n\n# Gradle Files #\n################\n.gradle\nlocal.properties\n\n# Build output directies\n/target\n**/test-output\n**/target\n**/bin\nbuild\n*/build\n.m2\n\n# IntelliJ specific files/directories\nout\n.idea\n*.ipr\n*.iws\n*.iml\natlassian-ide-plugin.xml\n\n# Eclipse specific files/directories\n.classpath\n.project\n.settings\n.metadata\n.factorypath\n.generated\n\n# NetBeans specific files/directories\n.nbattrs\n\n# Specifically include the gradle wrapper jar\n!/gradle/wrapper/gradle-wrapper.jar\n\n# Scripts for use on the local machine\n/local-*.sh\n/scripts/downloads/\n/scripts/input/\n/scripts/output/\n"
  },
  {
    "path": "CHANGES.md",
    "content": "# Change Log\n\n## Unreleased\n\n## 1.9.1\n* Upgrade commons-compress to 1.24.0 due to CVE-2023-42503\n\n## 1.9.0\n* Built using Avro 1.11.3\n\n## 1.8.0\n* Built using Avro 1.11.2\n\n## 1.7.1\n* Fix vulnerabilities in transitive dependencies (contribution from [BlacCello](https://github.com/BlacCello)); see https://github.com/davidmc24/gradle-avro-plugin/pull/229\n\n## 1.7.0\n* Support for using conversions and type factories located outside of build classpath (contribution from [erdi](https://github.com/erdi)); see https://github.com/davidmc24/gradle-avro-plugin/pull/228\n\n## 1.6.0\n* Add support for configuring classpath for `GenerateAvroJavaTask` (thanks to [crtlib](https://github.com/crtlib)); see https://github.com/davidmc24/gradle-avro-plugin/pull/222\n* Drop compatibility testing for old versions of Java (9, 10, 12, 13, 14, 15, 16)\n* Built using Gradle 7.6\n* Updated compatibility testing through Gradle 7.6\n* Updated compatibility testing through Java 19\n\n## 1.5.0\n* Added support for `additionalVelocityTool` thanks to a contribution from [dcracauer](https://github.com/dcracauer); see https://github.com/davidmc24/gradle-avro-plugin/pull/211\n* Built using Avro 1.11.1\n* Built using Gradle 7.5.1\n* Updated compatibility testing through Gradle 7.5.1\n* Updated compatibility testing through Java 18\n\n## 1.4.0\n* Drop support for Kotlin plugin integration\n\n## 1.3.0\n* Built using Avro 1.11.0\n* Dropped support for Avro 1.9.0-1.10.2 due to use of new SpecificRecordBuilderBase constructor in Avro 1.11.0\n* Default field visibility is now \"PRIVATE\" to match Avro's new default, as \"PUBLIC_DEPRECATED\" is no longer supported in Avro 1.11.0\n* Built using Gradle 7.3\n* Updated compatibility testing through Gradle 7.3\n* Updated compatibility testing through Kotlin 1.5.31\n* Added compatibility with Java 17\n* `GenerateAvroProtocolTask` now has a debug log to output its classpath\n* `GenerateAvroProtocolTask` will no longer delegate to the system classloader\n\n## 1.2.1\n* Built using Gradle 7.1.1\n* Updated compatibility testing through Gradle 7.1.1\n* When `sourcesJar` is used, declares dependency on `GenerateAvroJavaTask`s to avoid disabling execution optimizations introduced in Gradle 7.1. (see #167)\n\n## 1.2.0\n* Avro 1.9.0-1.9.2 is supported again (no changed needed; just change in support policy and testing)\n* `generateAvroProtocol` task fails if avpr file will get overwritten (due to multiple IDL files using the same namespace and protocol)\n\n## 1.1.0\n* Built using Avro 1.10.2\n* Built using Gradle 7.0-rc-1\n* Updated compatibility testing through Gradle 6.8.3/7.0-rc-1\n* Updated compatibility testing through Kotlin 1.4.32\n* Updated compatibility testing to include Java 16/17-ea\n* Adopted Github Actions for compatibility testing\n\n## 1.0.0\n* Published to Maven Central (no longer published to JCenter)\n* New plugin IDs: `com.github.davidmc24.gradle.plugin.avro` and `com.github.davidmc24.gradle.plugin.avro-base`\n* New package for tasks: `com.github.davidmc24.gradle.plugin.avro`\n\n# Pre-1.0 Versions\n\nThese versions used a different publishing process.  They use different coordinates/packages and may no longer be available in a traditional Maven repository.  It is strongly recommended to upgrade to a newer version.\n\nIf you still need to use them, the artifacts can be downloaded from [GitHub Releases](https://github.com/davidmc24/gradle-avro-plugin/releases) or accessed via [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin).\nThe plugin IDs are `com.commercehub.gradle.plugin.avro` and `com.commercehub.gradle.plugin.avro-base`, with all tasks in the package `com.commercehub.gradle.plugin.avro`.\n\n## 0.22.0\n* Add [Configuration Cache](https://docs.gradle.org/6.6/userguide/configuration_cache.html) support (#129; thanks to [dcabasson](https://github.com/dcabasson) and [eskatos](https://github.com/eskatos))\n* Add coverage reporting via JaCoco/Codecov to the plugin's build pipeline\n* Add support for multiple IDL files with the same name in different directories (#123)\n  * The `.avpr` file generated by `GenerateAvroProtocolTask` is now based on the namespace and name of the protocol, rather than the name of the `.avdl` file.\n* Built using Avro 1.10.1\n* Built using Gradle 6.7.1\n* Updated compatibility testing to include Java 15\n* Updated compatibility testing through Gradle 6.7.1\n* Updated compatibility testing through Kotlin 1.4.20\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.22.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.22.0)\n\n## 0.21.0\n* Built using Avro 1.10.0\n* Drop support for Avro 1.9.X\n* Removed support for `dateTimeLogicalType`; The behavior is now as if it were always `JSR-310` due to an upstream change\n* Add support for `optionalGettersForNullableFieldsOnly`\n* Apply @Classpath annotation to classpath on `GenerateAvroProtocolTask`\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.21.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.21.0)\n\n## 0.20.0\n* Built using Gradle 6.5\n* Updated compatibility testing to include Java 14\n* Updated compatibility testing through Gradle 6.5\n* Add `ResolveAvroDependenciesTask` (#115)\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.20.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.20.0)\n\n## 0.19.1\n* Fix schema dependency resolution when types are referenced with a `{ \"type\": NAME }` block rather than just `NAME` (#107)\n* Eliminate `NullPointerException` handling in schema dependency resolution, as it no longer appears to be needed.\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.19.1)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.19.1)\n\n## 0.19.0\n* Add support for Gradle 6.0-6.2.2 (#101)\n* Drop support for Gradle versions prior to 5.1\n* Update version of kotlin plugin in tests/example\n* Built using Avro 1.9.2 (#104)\n* Add support for Java 13\n* Add support for testing multiple Kotlin versions\n* Update plugin's own build to address some deprecation warnings of APIs being removed in Gradle 7\n* Add tests for Kotlin DSL usage (#61)\n* Support [Task Configuration Avoidance](https://docs.gradle.org/current/userguide/task_configuration_avoidance.html) (#97); thanks to [dcabasson](https://github.com/dcabasson) for the collaboration\n* Upgrade Codenarc from 1.4 to 1.5\n* Preliminary Java 14 support\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.19.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.19.0)\n\n## 0.18.0\n* Use reproducible file order for plugin archives\n* Eliminate usage of internal conventions API, using new Lazy Configuration approach instead; requires Gradle 4.4+\n  * Technically, the APIs needed are available in Gradle 4.3, but there is a bug related to un-set `Property` instances in 4.3 and 4.3.1; see https://github.com/gradle/gradle/issues/3879\n* Cleaned up compatibility code for older versions of Gradle\n* Built using Gradle 5.6.2\n* Upgrade Spock from 1.2 to 1.3\n* Upgrade Checkstyle from 6.1.1 to 8.23 and adjust rules used\n* Upgrade Codenarc from 1.0 to 1.4 and adjust rules used\n* Change source compatibility to 8\n* Modernized for Java 8\n* Built using Avro 1.9.1\n* GenerateAvroProtocolTask now has a `classpath` property; defaults to the runtime configuration when the Avro plugin is applied\n* GenerateAvroProtocolTask now properly declares the `classpath` as an input; fixes #86; thanks to [RichSteele](https://github.com/RichSteele) for the bug report\n* Fix handling of default `outputCharacterEncoding` (use of system default character set to match Java compiler)\n* Add support for generating getters that return Optional (#90); contribution from [bspeakmon](https://github.com/bspeakmon)\n* Add support for `logicalTypeFactories` and `customConversions`; fixes #92\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.18.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.18.0)\n\n## 0.17.0\n* Built using Avro 1.9.0\n* Removed configuration setting `validateDefaults`; defaults are now always validated due to an upstream change\n* Java 7 is no longer supported, as Avro 1.9.0 is now Java 8+\n* Began testing using Java 12\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.17.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.17.0)\n\n## 0.16.0\n* Built using Gradle 4.10.2\n* Updated compatibility testing through Gradle 4.10.2\n* Added support for the Gradle [Build Cache](https://docs.gradle.org/current/userguide/build_cache.html) (#48); contribution from [dcabasson](https://github.com/dcabasson)\n* Upgrade Spock from 1.0 to 1.2\n* Update plugin publishing mode to address Gradle 5.0 deprecation warning\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.16.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.16.0)\n\n## 0.15.1\n* Fix \"Boolean configuration cannot be set with boolean values from Kotlin DSL\" (#60)\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.15.1)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.15.1)\n\n## 0.15.0\n* Built using Gradle 4.9\n* Updated compatibility testing through Gradle 4.9\n* Began testing using Java 11\n* Add support for generating schema files (#56)\n* Fix bug where `GenerateAvroProtocolTask` can't be used without a runtime configuration\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.15.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.15.0)\n\n## 0.14.2\n* Stop creating default generated output directories when `outputDir` is customized and IntelliJ integration is used (#52)\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.14.2)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.14.2)\n\n## 0.14.1\n* Built using Gradle 4.6\n* Updated compatibility testing through Gradle 4.6\n* Began testing using Java 10\n* Began testing using Kotlin 1.2.31\n* Fixed infinite loop when a schema file contains multiple definitions of the same type (#47)\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.14.1)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.14.1)\n\n## 0.14.0\n* Built using Gradle 4.5\n* Updated compatibility testing through Gradle 4.5\n* Support for validation of default values in schema (#42)\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.14.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.14.0)\n\n## 0.13.0\n* Remove pre-cleaning behavior from `GenerateAvroJavaTask` (#41)\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.13.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.13.0)\n\n## 0.12.0\n* Improve support for Kotlin (#36)\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.12.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.12.0)\n\n## 0.11.0\n* Built using Gradle 4.2.1\n* Began testing using Java 9\n* Built using Avro 1.8.2\n* Breaking backward compatibility with Avro versions older than 1.8.2\n* Add new configuration option \"enableDecimalLogicalType\" to generate `BigDecimal` for fields annotated with `logicalType` equals to `decimal`\n* Breaking backward compatibility caused by \"enableDecimalLogicalType\" default value set `true`. `BigDecimal` will be used instead of old usage of `ByteBuffer`\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.11.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.11.0)\n\n## 0.10.0\n* Drop support for Gradle 2.x\n* As Gradle 3.0+ has a minimum Java version requiremenet of Java 7, drop support for Java 6\n* Update source compatibility to Java 7\n* Reduce access to utility methods not intended for re-use\n* Stopped publishing to [Gradle plugin portal](https://plugins.gradle.org)\n* Published to [Bintray](https://bintray.com/commercehub-oss/main/gradle-avro-plugin)\n* MapUtils class is no longer public\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.10.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.10.0)\n\n## 0.9.1\n* Built using Gradle 4.1\n* Updated versions for cross-compatibility testing\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.9.1)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.9.1)\n\n## 0.9.0\n* Built using Avro 1.8.1 (#23)\n* Built using Gradle 2.13\n* Added version cross-compatibility testing\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.9.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.9.0)\n\n## 0.8.1\n* Compatible at runtime with Gradle 5; no functional changes.  Compiled with Gradle 5.6.\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.8.1)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.8.1)\n\n## 0.8.0\n* Add support for Java 6 (#21)\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.8.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.8.0)\n\n## 0.7.0\n* Remove usage of Apache Commons IO (#19)\n* Add ability to retry processing of duplicate type definitions (#13)\n* Renamed \"encoding\" option to \"outputCharacterEncoding\" to match Avro compiler\n* Allowed setting \"outputCharacterEncoding\" to a `java.nio.charset.Charset` (in addition to a `String` charset name)\n* Allowed setting \"stringType\" to a `org.apache.avro.generic.GenericData.StringType` (in addition to a String)\n* Allowed setting \"fieldVisibility\" to a `org.apache.avro.compiler.specific.SpecificCompiler.FieldVisibility` (in addition to a String)\n* Fixed handling of non-\"true\" String settings for \"createSetters\" option\n* Automatically use encoding from `JavaCompile` task as \"outputCharacterEncoding\", if set\n* Change default \"outputCharacterEncoding\" to system default to match `JavaCompile` task behavior (#20)\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.7.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.7.0)\n\n## 0.6.1\n* Add Checkstyle ImportControl to prevent accidentally adding dependencies on libraries that Gradle makes available for build but not runtime.\n* Remove usage of Guava (#18)\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.6.1)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.6.1)\n\n## 0.6.0\n* Add new configuration option \"templateDirectory\" to set source directory for the Avro compiler's Velocity templates.\n* Add new configuration option \"createSetters\" to allow suppressing the Avro compiler's creation of setters in created domain objects.\n* Matching of fieldVisibility settings is now case-insensitive.\n* Removed some excessive debug logging\n* Built against Gradle 2.7\n* Added Checkstyle and Codenarc to build\n* Known Bug: doesn't work properly unless you manually add a dependency on guava; please upgrade to 0.6.1\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.6.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.6.0)\n\n## 0.5.0\n* Add support for schemas/protocols/IDL in subdirectories of `src/main/avro`, etc. (#11)\n* Expose original error messages from `avro-compiler` when compilation fails\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.5.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.5.0)\n\n## 0.4.0\n* Add ability to specify fieldVisibility for generated Java source; contribution from [wooder79](https://github.com/wooder79)\n* Removed support for unqualified plugin ID (just \"avro\")\n* Published via new mechanism to [Gradle plugin portal](https://plugins.gradle.org)\n* Stopped publishing to previous location on Bintray\n* Built against Gradle 2.6; uses [test kit](https://docs.gradle.org/current/userguide/test_kit.html) for functional testing\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.4.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.4.0)\n\n## 0.3.4\n* Fix registration of generated sources for compilation (#8)\n* Change classloader handling to better support import of external dependencies (#9)\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.3.4)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.3.4)\n\n## 0.3.3\n* Fix generation of Java files from .avdl files; contribution from [viacoban](https://github.com/viacoban)\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.3.3)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.3.3)\n\n## 0.3.2\n* Improve handling when custom buildDir is used\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.3.2)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.3.2)\n\n## 0.3.1\n* Fix extension support for configuring encoding\n* Make default encoding UTF-8\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.3.1)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.3.1)\n\n## 0.3.0\n* IntelliJ: register generated source directories even if they don't already exist.\n* Add avro-base plugin, which exposes tasks and the extension without creating tasks, defaults, etc.\n* Add support for configuring encoding\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.3.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.3.0)\n\n## 0.2.0\n* Build against Gradle 1.12\n* Compile using Avro 1.7.6\n* Support for qualified plugin ID\n* Deprecate unqualified plugin ID\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.2.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.2.0)\n\n## 0.1.3\n* Always regenerate all Java classes when any schema file changes to avoid some classes having outdated schema information.\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.1.3)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.1.3)\n\n## 0.1.2\n* Eliminate dependency on guava, make dependency on commons-io explicit\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.1.2)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.1.2)\n\n## 0.1.1\n* Fixed NullPointerException when performing clean builds\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.1.1)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.1.1)\n\n## 0.1.0\n* Add support for converting IDL files to JSON protocol declaration files\n* Add support for generating Java classes from JSON protocol declaration files\n* Add support for generating Java classes from JSON schema declaration files\n* Add support for inter-dependent JSON schema declaration files\n* Add support for tweaking source/exclude directories in IntelliJ\n* Add support for specifying the string type to use in generated classes\n\nLinks:\n* [Release](https://github.com/davidmc24/gradle-avro-plugin/releases/tag/0.1.0)\n* [JitPack](https://jitpack.io/#davidmc24/gradle-avro-plugin/0.1.0)\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, gender identity and expression, level of experience,\nnationality, personal appearance, race, religion, or sexual identity and\norientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\nadvances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at david@carrclan.us.\nWe will endeavor to review submitted complaints and respond in a manner that\nCommerceHub (in its sole discretion) deems necessary and appropriate to the\ncircumstances. In enforcing this Code of Conduct, Project maintainers may\n(but shall not be obligated to) remove, edit, or reject comments, commits,\ncode, wiki edits, issues, and other contributions that are not aligned to\nthis Code of Conduct, or to ban temporarily or permanently any contributor\nfor other behaviors that they deem inappropriate, threatening, offensive,\nor harmful.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n> Before contributing, please read our [code of conduct](https://github.com/davidmc24/gradle-avro-plugin/blob/master/CODE_OF_CONDUCT.md).\n\nBefore starting work on an enhancement, it's highly recommended to open an [issue](https://github.com/davidmc24/gradle-avro-plugin/issues) to describe the intended change.\nThis allows for the project maintainers to provide feedback before you've done work that may not fit the project's vision.\n\nNote that this plugin is primarily focussed on exposing functionality from the [Apache Avro Java API](https://avro.apache.org/docs/current/api/java/index.html) in the ways most commonly used in Gradle builds.\nIf the capability that you are looking for doesn't currently exist in said upstream API, you're likely better off requesting the feature from the [Apache Avro project](https://avro.apache.org/) than requesting it here.\n\nSome possible enhancements may have already been considered and documented.  Check the design-docs folder for the design specification for such features.\n\nTo run the project's build, run:\n\n* (Mac/Linux): `./gradlew build`\n* (Windows): `gradlew.bat build`\n\nThis will run static analysis against the project, run the project's tests, and build the project.\nIf any failures are detected, please correct them prior to submitting your pull request.\n\nAll enhancements should be accompanied by test coverage.\nOur tests are based on [Spock](https://github.com/spockframework/spock).\nGenerally, it's best to extend our `FunctionalSpec` class, which provides useful functions for running the plugin within Gradle.\n\nNote that the \"build\" task only tests the plugin against a single version of Gradle/Avro.\nIf you want to test compatibility with a larger range, consider using the `testRecentVersionCompatibility` task or `testVersionCompatibility` task.\n\nFor information on how to use GitHub to submit a pull request, see [Collaborating on projects using issues and pull requests](https://help.github.com/categories/collaborating-on-projects-using-issues-and-pull-requests/).\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, \"control\" means (i) the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n\"submitted\" means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution.\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\nYou must cause any modified files to carry prominent notices stating that You\nchanged the files; and\nYou must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n5. Submission of Contributions.\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n6. Trademarks.\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty.\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n8. Limitation of Liability.\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability.\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"[]\" replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same \"printed page\" as the copyright notice for easier identification within\nthird-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": "# End of life\n\nThis project is no longer maintained.\nIts code has been donated to the [Apache Avro project](http://avro.apache.org/), which will be handling releases going forward.\n\n# Overview\n\nThis is a [Gradle](http://www.gradle.org/) plugin to allow easily performing Java code generation for [Apache Avro](http://avro.apache.org/).  It supports JSON schema declaration files, JSON protocol declaration files, and Avro IDL files.\n\n[![Build Status](https://github.com/davidmc24/gradle-avro-plugin/workflows/CI%20Build/badge.svg)](https://github.com/davidmc24/gradle-avro-plugin/actions)\n\n# Compatibility\n\n**NOTE**: Pre-1.0 versions used a different publishing process/namespace.  It is strongly recommended to upgrade to a newer version.  Further details can be found in the [change log](CHANGES.md).\n\n* Currently tested against Java 8, 11, and 17-19\n    * Though not supported yet, tests are also run against Java 20 to provide early notification of potential incompatibilities.\n    * Java 19 support requires Gradle 7.6 or higher (as per Gradle's release notes)\n    * Java 18 support requires Gradle 7.5 or higher (as per Gradle's release notes)\n    * Java 17 support requires Gradle 7.3 or higher (as per Gradle's release notes)\n    * Java 16 support requires Gradle 7.0 or higher (as per Gradle's release notes)\n    * Java 15 support requires Gradle 6.7 or higher (as per Gradle's release notes)\n    * Java 14 support requires Gradle 6.3 or higher (as per Gradle's release notes)\n    * Java 13 support requires Gradle 6.0 or higher\n    * Java 8-12 support requires Gradle 5.1 or higher (versions lower than 5.1 are no longer supported)\n* Currently built against Gradle 7.6\n    * Currently tested against Gradle 5.1-5.6.4 and 6.0-7.6\n* Currently built against Avro 1.11.3\n    * Currently tested against Avro 1.11.0-1.11.3\n    * Avro 1.9.0-1.10.2 were last supported in version 1.2.1 \n* Support for Kotlin\n    * Dropped integration with the Kotlin plugin in plugin version 1.4.0, as Kotlin 1.7.x would require compile-time dependency on a specific Kotlin version\n      * Wiring between the tasks added by the plugin and the Kotlin compilation tasks can either be added by your build logic, or a derived plugin\n    * Plugin version 1.3.0 was the last version with tested support for Kotlin \n      * It is believed to work with Kotlin 1.6.x as well\n      * It was tested against Kotlin plugin versions 1.3.20-1.3.72 and 1.4.0-1.4.32 and 1.5.0-1.5.31 using the latest compatible version of Gradle\n      * It was tested against Kotlin plugin versions 1.2.20-1.2.71 and 1.3.0-1.3.11 using Gradle 5.1\n    * Kotlin plugin versions 1.4.20-1.4.32 require special settings to work with Java 17+; see [KT-43704](https://youtrack.jetbrains.com/issue/KT-43704#focus=Comments-27-4639603.0-0)\n    * Kotlin plugin version 1.3.30 is not compatible with Gradle 7.0+\n    * Kotlin plugin versions starting with 1.4.0 require Gradle 5.3+\n    * Kotlin plugin versions prior to 1.3.20 do not support Gradle 6.0+\n    * Kotlin plugin versions prior to 1.2.30 do not support Java 10+\n    * Version of the Kotlin plugin prior to 1.2.20 are unlikely to work\n* Support for Gradle Kotlin DSL\n\n# Usage\n\nAdd the following to your build files.  Substitute the desired version based on [CHANGES.md](https://github.com/davidmc24/gradle-avro-plugin/blob/master/CHANGES.md).\n\n`settings.gradle`:\n```groovy\npluginManagement {\n    repositories {\n        gradlePluginPortal()\n        mavenCentral()\n    }\n}\n```\n\n`build.gradle`:\n```groovy\nplugins {\n    id \"com.github.davidmc24.gradle.plugin.avro\" version \"VERSION\"\n}\n```\n\nAdditionally, ensure that you have an implementation dependency on Avro, such as:\n\n```groovy\nrepositories {\n    mavenCentral()\n}\ndependencies {\n    implementation \"org.apache.avro:avro:1.11.0\"\n}\n```\n\nIf you now run `gradle build`, Java classes will be compiled from Avro files in `src/main/avro`.\nActually, it will attempt to process an \"avro\" directory in every `SourceSet` (main, test, etc.)\n\n# Configuration\n\nThere are a number of configuration options supported in the `avro` block.\n\n| option                               | default                            | description                                                                                                       |\n|--------------------------------------|------------------------------------|-------------------------------------------------------------------------------------------------------------------|\n| createSetters                        | `true`                             | `createSetters` passed to Avro compiler                                                                           |\n| createOptionalGetters                | `false`                            | `createOptionalGetters` passed to Avro compiler                                                                   |\n| gettersReturnOptional                | `false`                            | `gettersReturnOptional` passed to Avro compiler                                                                   |\n| optionalGettersForNullableFieldsOnly | `false`                            | `optionalGettersForNullableFieldsOnly` passed to Avro compiler                                                    |\n| fieldVisibility                      | `\"PRIVATE\"`                        | `fieldVisibility` passed to Avro compiler                                                                         |\n| outputCharacterEncoding              | see below                          | `outputCharacterEncoding` passed to Avro compiler                                                                 |\n| stringType                           | `\"String\"`                         | `stringType` passed to Avro compiler                                                                              |\n| templateDirectory                    | see below                          | `templateDir` passed to Avro compiler                                                                             |\n| additionalVelocityToolClasses        | see below                          | `additionalVelocityTools` passed to Avro compiler                                                                 |\n| enableDecimalLogicalType             | `true`                             | `enableDecimalLogicalType` passed to Avro compiler                                                                |\n| conversionsAndTypeFactoriesClasspath | empty `ConfigurableFileCollection` | used for loading custom conversions and logical type factories                                                    |\n| logicalTypeFactoryClassNames         | empty `Map`                        | map from names to class names of logical types factories to be loaded from `conversionsAndTypeFactoriesClasspath` |\n| customConversionClassNames           | empty `List`                       | class names of custom conversions to be loaded from `conversionsAndTypeFactoriesClasspath`                        |\n\nAdditionally, the `avro` extension exposes the following methods:\n\n* `logicalTypeFactory(String typeName, Class typeFactoryClass)`: register an additional logical type factory\n* `customConversion(Class conversionClass)`: register a custom conversion\n\n## createSetters\n\nValid values: `true` (default), `false`; supports equivalent `String` values\n\nSet to `false` to not create setter methods in the generated classes.\n\nExample:\n\n```groovy\navro {\n    createSetters = false\n}\n```\n\n## createOptionalGetters\n\nValid values: `false` (default), `true`; supports equivalent `String` values\n\nSet to `true` to create additional getter methods that return their fields wrapped in an\n[Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html). For a field with\nname `abc` and type `string`, this setting will create a method\n`Optional<java.lang.String> getOptionalAbc()`.\n\nExample:\n\n```groovy\navro {\n    createOptionalGetters = false\n}\n```\n\n## gettersReturnOptional\n\nValid values: `false` (default), `true`; supports equivalent `String` values\n\nSet to `true` to cause getter methods to return\n[Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) wrappers of the\nunderlying type. Where [`createOptionalGetters`](#createoptionalgetters) generates an additional\nmethod, this one replaces the existing getter.\n\nExample:\n\n```groovy\navro {\n    gettersReturnOptional = false\n}\n```\n\n## optionalGettersForNullableFieldsOnly\n\nValid values: `false` (default), `true`; supports equivalent `String` values\n\nSet to `true` in conjuction with `gettersReturnOptional` to `true` to return\n[Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) wrappers of the\nunderlying type. Where [`gettersReturnOptional`](#gettersReturnOptional) alone changes all getters to\nreturn `Optional`, this one only returns Optional for nullable (null union) field definitions.\nSetting this to `true` without setting `gettersReturnOptional` to `true` will result in this flag having no effect.\n\nExample:\n```groovy\navro {\n    gettersReturnOptional = true\n    optionalGettersForNullableFieldsOnly = true\n}\n```\n\n## fieldVisibility\n\nValid values: any [FieldVisibility](https://avro.apache.org/docs/1.11.0/api/java/org/apache/avro/compiler/specific/SpecificCompiler.FieldVisibility.html) or equivalent `String` name (matched case-insensitively); default `\"PRIVATE\"` (default)\n\nBy default, the fields in generated Java files will have private visibility.\nSet to `\"PRIVATE\"` to explicitly specify private visibility of the fields, or `\"PUBLIC\"` to specify public visibility of the fields.\n\nExample:\n\n```groovy\navro {\n    fieldVisibility = \"PUBLIC\"\n}\n```\n\n## outputCharacterEncoding\n\nValid values: any [Charset](http://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html) or equivalent `String` name\n\nControls the character encoding of generated Java files.\nIf using the plugin's conventions (i.e., not just the base plugin), the associated `JavaCompile` task's encoding will be used automatically.\nOtherwise, it will use the value configured in the `avro` block, defaulting to `\"UTF-8\"`.\n\nExamples:\n\n```groovy\n// Option 1: configure compilation task (avro plugin will automatically match)\ntasks.withType(JavaCompile).configureEach {\n    options.encoding = 'UTF-8'\n}\n// Option 2: just configure avro plugin\navro {\n    outputCharacterEncoding = \"UTF-8\"\n}\n```\n\n## stringType\n\nValid values: any [StringType](http://avro.apache.org/docs/1.8.1/api/java/org/apache/avro/generic/GenericData.StringType.html) or equivalent `String` name (matched case-insensitively); default `\"String\"` (default)\n\nBy default, the generated Java files will use [`java.lang.String`](http://docs.oracle.com/javase/7/docs/api/java/lang/String.html) to represent string types.\nAlternatively, you can set it to `\"Utf8\"` to use [`org.apache.avro.util.Utf8`](https://avro.apache.org/docs/1.8.1/api/java/org/apache/avro/util/Utf8.html) or `\"charSequence\"` to use [`java.lang.CharSequence`](http://docs.oracle.com/javase/7/docs/api/java/lang/CharSequence.html).\n\n```groovy\navro {\n    stringType = \"CharSequence\"\n}\n```\n\n## templateDirectory\n\nBy default, files will be generated using Avro's default templates.\nIf desired, you can override the template set used by either setting this property or the `\"org.apache.avro.specific.templates\"` System property.\n\n```groovy\navro {\n    templateDirectory = \"/path/to/velocity/templates\"\n}\n```\n\n## additionalVelocityToolClasses\n\nWhen overriding the default set of Velocity templates provided with Avro, it is often desirable to provide additional tools to use during generation. \nThe class names you provide will be made available for use in your Velocity templates. An instance of each class provided will be created using \nthe default constructor (required). When registered, they will be available as $class.simpleName(). Given the example configuration below,\ntwo tools would be registered, and be available as escape and json.\n \n\n```groovy\navro {\n  additionalVelocityToolClasses = ['com.yourpackage.Escape', 'com.yourpackage.JSON']\n}\n```\n\n## enableDecimalLogicalType\n\nValid values: `true` (default), `false`; supports equivalent `String` values\n\nBy default, generated Java files will use [`java.math.BigDecimal`](https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html)\nfor representing `fixed` or `bytes` fields annotated with `\"logicalType\": \"decimal\"`.\nSet to `false` to use [`java.nio.ByteBuffer`](https://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html) in generated classes.\n\nExample:\n\n```groovy\navro {\n    enableDecimalLogicalType = false\n}\n```\n\n## conversionsAndTypeFactoriesClasspath, logicalTypeFactoryClassNames and customConversionClassNames\n\nProperties that can be used for loading [Conversion](https://avro.apache.org/docs/current/api/java/org/apache/avro/Conversion.html) and [LogicalTypeFactory](https://avro.apache.org/docs/current/api/java/org/apache/avro/LogicalTypes.LogicalTypeFactory.html) classes from outside of the build classpath.\n\nExample:\n\n```groovy\nconfigurations {\n    customConversions\n}\n\ndependencies {\n    customConversions(project(\":custom-conversions\"))\n}\n\navro {\n    conversionsAndTypeFactoriesClasspath.from(configurations.customConversions)\n    logicalTypeFactoryClassNames.put(\"timezone\", \"com.github.davidmc24.gradle.plugin.avro.test.custom.TimeZoneLogicalTypeFactory\")\n    customConversionClassNames.add(\"com.github.davidmc24.gradle.plugin.avro.test.custom.TimeZoneConversion\")\n}\n```\n\n# IntelliJ Integration\n\nThe plugin attempts to make IntelliJ play more smoothly with generated sources when using Gradle-generated project files.\nHowever, there are still some rough edges.  It will work best if you first run `gradle build`, and _after_ that run `gradle idea`.\nIf you do it in the other order, IntelliJ may not properly exclude some directories within your `build` directory.\n\n# Alternate Usage\n\nIf the defaults used by the plugin don't work for you, you can still use the tasks by themselves.\nIn this case, use the `com.github.davidmc24.gradle.plugin.avro-base` plugin instead, and create tasks of type `GenerateAvroJavaTask` and/or `GenerateAvroProtocolTask`.\n\nHere's a short example of what this might look like:\n\n```groovy\nimport com.github.davidmc24.gradle.plugin.avro.GenerateAvroJavaTask\n\napply plugin: \"java\"\napply plugin: \"com.github.davidmc24.gradle.plugin.avro-base\"\n\ndependencies {\n    implementation \"org.apache.avro:avro:1.11.0\"\n}\n\ndef generateAvro = tasks.register(\"generateAvro\", GenerateAvroJavaTask) {\n    source(\"src/avro\")\n    outputDir = file(\"dest/avro\")\n}\n\ntasks.named(\"compileJava\").configure {\n    source(generateAvro)\n}\n```\n\n# File Processing\n\nWhen using this plugin, it is recommended to define each record/enum/fixed type in its own file rather than using inline type definitions.\nThis approach allows defining any type of schema structure, and eliminates the potential for conflicting definitions of a type between multiple files.\nThe plugin will automatically recognize the dependency and compile the files in the correct order.\nFor example, instead of `Cat.avsc`:\n\n```json\n{\n    \"name\": \"Cat\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\n            \"name\": \"breed\",\n            \"type\": {\n                \"name\": \"Breed\",\n                \"type\": \"enum\",\n                \"symbols\" : [\n                    \"ABYSSINIAN\", \"AMERICAN_SHORTHAIR\", \"BIRMAN\", \"MAINE_COON\", \"ORIENTAL\", \"PERSIAN\", \"RAGDOLL\", \"SIAMESE\", \"SPHYNX\"\n                ]\n            }\n        }\n    ]\n}\n```\n\nuse `Breed.avsc`:\n\n```json\n{\n    \"name\": \"Breed\",\n    \"namespace\": \"example\",\n    \"type\": \"enum\",\n    \"symbols\" : [\"ABYSSINIAN\", \"AMERICAN_SHORTHAIR\", \"BIRMAN\", \"MAINE_COON\", \"ORIENTAL\", \"PERSIAN\", \"RAGDOLL\", \"SIAMESE\", \"SPHYNX\"]\n}\n```\n\n\nand `Cat.avsc`:\n\n```json\n{\n    \"name\": \"Cat\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"breed\", \"type\": \"Breed\"}\n    ]\n}\n```\n\nThere may be cases where the schema files contain inline type definitions and it is undesirable to modify them.\nIn this case, the plugin will automatically recognize any duplicate type definitions and check if they match.\nIf any conflicts are identified, it will cause a build failure.\n\n# Kotlin Support\n\nThe Java classes generated from your Avro files should be automatically accessible in the classpath to Kotlin classes in the same sourceset, and transitively to any sourcesets that depend on that sourceset.\nThis is accomplished by this plugin detecting that the Kotlin plugin has been applied, and informing the Kotlin compilation tasks of the presence of the generated sources directories for cross-compilation.\n\nThis support does *not* support producing the Avro generated classes as Kotlin classes, as that functionality is not currently provided by the upstream Avro library.\n\n# Kotlin DSL Support\n\nSpecial notes relevant to using this plugin via the Gradle Kotlin DSL:\n\n* Apply the plugin declaratively using the `plugins {}` block.  Otherwise, various features may not work as intended.  See [Configuring Plugins in the Gradle Kotlin DSL](https://github.com/gradle/kotlin-dsl/blob/master/doc/getting-started/Configuring-Plugins.md) for more details.\n* Configuration in the `avro {}` block must be applied differently than in the Groovy DSL.  See the example below for details.\n\n### Example Kotlin DSL Setup:\n\nIn `gradle.build.kts` add:\n\n```kotlin\nplugins {\n    // Find latest release here: https://github.com/davidmc24/gradle-avro-plugin/releases\n    id(\"com.github.davidmc24.gradle.plugin.avro\") version \"VERSION\"\n}\n```\n\nAnd then in your `settings.gradle.kts` add:\n\n```kotlin\npluginManagement {\n    repositories {\n        gradlePluginPortal()\n        mavenCentral()\n    }\n}\n```\n\nThe syntax for configuring the extension looks like this:\n\n```kotlin\navro {\n    isCreateSetters.set(true)\n    isCreateOptionalGetters.set(false)\n    isGettersReturnOptional.set(false)\n    isOptionalGettersForNullableFieldsOnly.set(false)\n    fieldVisibility.set(\"PUBLIC_DEPRECATED\")\n    outputCharacterEncoding.set(\"UTF-8\")\n    stringType.set(\"String\")\n    templateDirectory.set(null as String?)\n    isEnableDecimalLogicalType.set(true)\n}\n```\n\n# Resolving schema dependencies\n\nIf desired, you can generate JSON schema with dependencies resolved.\n\nExample build:\n\n```groovy\nimport com.github.davidmc24.gradle.plugin.avro.ResolveAvroDependenciesTask\n\napply plugin: \"com.github.davidmc24.gradle.plugin.avro-base\"\n\ntasks.register(\"resolveAvroDependencies\", ResolveAvroDependenciesTask) {\n    source file(\"src/avro/normalized\")\n    outputDir = file(\"build/avro/resolved\")\n}\n```\n\n# Generating schema files from protocol/IDL\n\nIf desired, you can generate JSON schema files.\nTo do this, apply the plugin (either `avro` or `avro-base`), and define custom tasks as needed for the schema generation.\nFrom JSON protocol files, all that's needed is the `GenerateAvroSchemaTask`.\nFrom IDL files, first use `GenerateAvroProtocolTask` to convert the IDL files to JSON protocol files, then use `GenerateAvroSchemaTask`.\n\nExample using base plugin with support for both IDL and JSON protocol files in `src/main/avro`:\n\n```groovy\nimport com.github.davidmc24.gradle.plugin.avro.GenerateAvroProtocolTask\nimport com.github.davidmc24.gradle.plugin.avro.GenerateAvroSchemaTask\n\napply plugin: \"com.github.davidmc24.gradle.plugin.avro-base\"\n\ndef generateProtocol = tasks.register(\"generateProtocol\", GenerateAvroProtocolTask) {\n    source file(\"src/main/avro\")\n    include(\"**/*.avdl\")\n    outputDir = file(\"build/generated-avro-main-avpr\")\n}\n\ntasks.register(\"generateSchema\", GenerateAvroSchemaTask) {\n    dependsOn generateProtocol\n    source file(\"src/main/avro\")\n    source file(\"build/generated-avro-main-avpr\")\n    include(\"**/*.avpr\")\n    outputDir = file(\"build/generated-main-avro-avsc\")\n}\n```\n"
  },
  {
    "path": "RELEASING.md",
    "content": "# Release Process\n\n1. Update `CHANGES.md`\n1. Ensure that there is a milestone for the version, and that appropriate issues are associated with the milestone.\n1. Update the plugin version in `build.gradle` under \"version\"\n1. Commit and tag with the version number (don't push yet)\n1. Run `./gradlew clean build` to make sure it looks good.\n1. Update the version in `build.gradle` to the next SNAPSHOT and commit.\n1. Push\n1. If there was a issue requesting the release, close it.\n1. Close the milestone.\n1. Go to the [GitHub Releases page](https://github.com/davidmc24/gradle-avro-plugin/releases), click \"Draft a new release\", select the tag version, use the version number as the title, copy the relevant segment from `CHANGES.md` into the description, and click \"Publish release\".  This will trigger the CI job that does the actual publishing.\n"
  },
  {
    "path": "build.gradle",
    "content": "plugins {\n    id \"groovy\"\n    id \"checkstyle\"\n    id \"codenarc\"\n    id \"idea\"\n//    id \"jacoco\"\n    id \"maven-publish\"\n    id \"signing\"\n    id \"java-gradle-plugin\"\n    id \"org.nosphere.gradle.github.actions\" version \"1.2.0\"\n    id \"io.github.gradle-nexus.publish-plugin\" version \"1.0.0\"\n//    id \"pl.droidsonroids.jacoco.testkit\" version \"1.0.8\"\n}\n\n// Gradle TestKit (which most of this plugins tests run using) runs the builds in a separate\n// JVM than the tests.  Thus, for Jacoco to pick up on it, you need to do some extra legwork.\n// https://github.com/koral--/jacoco-gradle-testkit-plugin seeks to solve that\n// However, Gradle 7.1 doesn't currently support the combination of the configuration cache\n// with a Java agent (such as Jacoco) and TestKit.\n// Thus, for now, no coverage reporting, as I place a higher value on testing configuration cache\n// support.\n\ngroup = \"com.github.davidmc24.gradle.plugin\"\nversion = \"1.9.2-SNAPSHOT\"\n\ndef isCI = System.getenv(\"CI\") == \"true\"\n\nrepositories {\n    mavenCentral()\n    maven {\n        // Used for snapshot builds for some libraries\n        name 'Sonatype OSS'\n        url 'https://oss.sonatype.org/content/repositories/snapshots'\n    }\n}\n\ndef compileAvroVersion = \"1.11.3\"\n\n// Write the plugin's classpath to a file to share with the tests\ntask createClasspathManifest {\n    def outputDir = file(\"$buildDir/$name\")\n\n    inputs.files sourceSets.main.runtimeClasspath\n    outputs.dir outputDir\n\n    doLast {\n        outputDir.mkdirs()\n        file(\"$outputDir/plugin-classpath.txt\").text = sourceSets.main.runtimeClasspath.join(\"\\n\")\n    }\n}\n\ndependencies {\n    implementation localGroovy()\n    implementation \"org.apache.avro:avro-compiler:${compileAvroVersion}\"\n    constraints {\n        implementation ('com.fasterxml.jackson.core:jackson-databind:2.12.7.1') {\n            because 'previous versions have vulnerabilities: CVE-2022-42004, CVE-2022-42003'\n        }\n        implementation ('org.apache.commons:commons-text:1.10.0') {\n            because 'previous versions have vulnerability: CVE-2022-42889'\n        }\n        implementation ('org.apache.commons:commons-compress:1.24.0') {\n            because 'previous versions have vulnerability: CVE-2023-42503'\n        }\n    }\n    testImplementation \"org.spockframework:spock-core:2.0-M5-groovy-3.0\"\n    testImplementation gradleTestKit()\n    testImplementation \"uk.co.datumedge:hamcrest-json:0.2\"\n    testImplementation \"com.vdurmont:semver4j:3.1.0\"\n    testRuntimeOnly files(createClasspathManifest) // Add the classpath file to the test runtime classpath\n    // tool version specified in dependencies in order to override Groovy version for Java compatibility\n    codenarc \"org.codenarc:CodeNarc:2.2.0\"\n    codenarc \"org.codehaus.groovy:groovy-all:3.0.9\"\n}\n\ntasks.withType(AbstractCompile) {\n    options.encoding = \"UTF-8\"\n}\ntasks.withType(JavaCompile) {\n    options.compilerArgs << \"-Xlint:all\" << \"-Xlint:-options\" << \"-Werror\"\n}\n\ntasks.withType(AbstractArchiveTask) {\n    preserveFileTimestamps = false\n    reproducibleFileOrder = true\n}\n\njava {\n    withJavadocJar()\n    withSourcesJar()\n}\n\njavadoc {\n    if(JavaVersion.current().isJava9Compatible()) {\n        options.addBooleanOption('html5', true)\n    }\n    options.addStringOption('Xdoclint:none', '-quiet')\n}\n\npublishing {\n    publications.withType(MavenPublication) {\n        pom {\n            name = \"gradle-avro-plugin\"\n            description = \"A Gradle plugin to allow easily performing Java code generation for Apache Avro. It supports JSON schema declaration files, JSON protocol declaration files, and Avro IDL files.\"\n            url = \"https://github.com/davidmc24/gradle-avro-plugin\"\n            licenses {\n                license {\n                    name = \"The Apache License, Version 2.0\"\n                    url = \"http://www.apache.org/licenses/LICENSE-2.0.txt\"\n                }\n            }\n            developers {\n                developer {\n                    id = 'davidmc24'\n                    name = 'David M. Carr'\n                    email = 'david@carrclan.us'\n                }\n            }\n            scm {\n                connection = 'scm:git:https://github.com/davidmc24/gradle-avro-plugin.git'\n                developerConnection = 'scm:git:ssh://github.com/davidmc24/gradle-avro-plugin.git'\n                url = 'https://github.com/davidmc24/gradle-avro-plugin'\n            }\n        }\n    }\n}\n\nnexusPublishing {\n    repositories {\n        sonatype()\n    }\n}\n\nsigning {\n    if (isCI) {\n        def signingKeyId = System.getenv(\"SIGNING_KEY_ID\")\n        def signingKey = System.getenv(\"SIGNING_KEY\")\n        def signingPassword = System.getenv(\"SIGNING_PASSWORD\")\n        useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)\n    }\n    publishing.publications.all { publication ->\n        sign publication\n    }\n}\n\ngradlePlugin {\n    plugins {\n        avro {\n            id = \"com.github.davidmc24.gradle.plugin.avro\"\n            implementationClass = \"com.github.davidmc24.gradle.plugin.avro.AvroPlugin\"\n            displayName = \"avro\"\n            description = \"Conventions plugin for gradle-avro-plugin\"\n        }\n        avroBase {\n            id = \"com.github.davidmc24.gradle.plugin.avro-base\"\n            implementationClass = \"com.github.davidmc24.gradle.plugin.avro.AvroBasePlugin\"\n            displayName = \"avro-base\"\n            description = \"Capabilities plugin for gradle-avro-plugin\"\n        }\n    }\n}\n\nidea {\n    project {\n        vcs = \"Git\"\n        ipr {\n            withXml { provider ->\n                def node = provider.asNode()\n                node.append(new XmlParser().parseText(\"\"\"\n                <component name=\"ProjectCodeStyleSettingsManager\">\n                    <option name=\"PER_PROJECT_SETTINGS\">\n                        <value>\n                            <option name=\"LINE_SEPARATOR\" value=\"&#10;\"/>\n                            <option name=\"RIGHT_MARGIN\" value=\"140\"/>\n                        </value>\n                    </option>\n                    <option name=\"USE_PER_PROJECT_SETTINGS\" value=\"true\"/>\n                </component>\n                \"\"\".stripIndent()))\n            }\n        }\n    }\n}\n\ncheckstyle {\n    ignoreFailures = false\n    maxErrors = 0\n    maxWarnings = 0\n    showViolations = true\n    toolVersion = \"8.23\"\n}\n// In Gradle 4.8 the checkstyle basedir changed to no longer be the project root by default; thus we need to specify\ncheckstyleMain {\n    configProperties = ['basedir': \"$rootDir/config/checkstyle\"]\n}\ncheckstyleTest {\n    configProperties = ['basedir': \"$rootDir/config/checkstyle\"]\n}\n\ncodenarc {\n    config = project.resources.text.fromFile(\"config/codenarc/codenarc.groovy\")\n    ignoreFailures = false\n    maxPriority1Violations = 0\n    maxPriority2Violations = 0\n    maxPriority3Violations = 0\n    // tool version specified in dependencies in order to override Groovy version for Java compatibility\n//    toolVersion = \"2.2.0\"\n}\n\n// Java 8+ is required due to requirements introduced in Avro 1.9.0\n// Java 8+ is also required by Gradle 5.x\nif (JavaVersion.current().java10Compatible) {\n    compileJava {\n        options.release = 8\n    }\n} else {\n    sourceCompatibility = 8\n}\n\ntest {\n    useJUnitPlatform()\n    systemProperties = [\n        avroVersion: compileAvroVersion,\n        gradleVersion: gradle.gradleVersion,\n    ]\n//    finalizedBy jacocoTestReport // report is always generated after tests run\n}\n\ntasks.create(name: \"testCompatibility\", type: Test) {\n    description = \"Test cross-compatibility of the plugin with Avro/Gradle\"\n    group = \"Verification\"\n    useJUnitPlatform()\n    systemProperties = [\n        avroVersion: findProperty(\"avroVersion\"),\n        gradleVersion: findProperty(\"gradleVersion\"),\n    ]\n    reports {\n        html.destination = file(\"$buildDir/reports/tests/compatibility\")\n        junitXml.destination = file(\"$buildDir/reports/tests/compatibility\")\n    }\n}\n\n//jacoco {\n//    // 0.8.7+ needed for Java 15 support\n//    // See https://www.jacoco.org/jacoco/trunk/doc/changes.html\n//    toolVersion = \"0.8.7-SNAPSHOT\"\n//}\n\n//jacocoTestReport {\n//    reports {\n//        html.enabled true\n//        xml.enabled true\n//    }\n//}\n\ntasks.withType(Test) {\n    jvmArgs \"-Xss320k\"\n    minHeapSize \"120m\"\n    maxHeapSize \"280m\"\n    maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1\n}\n"
  },
  {
    "path": "config/checkstyle/checkstyle.xml",
    "content": "<!DOCTYPE module PUBLIC\n    \"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN\"\n    \"https://checkstyle.org/dtds/configuration_1_3.dtd\">\n<module name=\"Checker\">\n\n    <module name=\"TreeWalker\">\n        <module name=\"Indentation\"/>\n\n        <!-- Annotations -->\n        <module name=\"AnnotationLocation\"/>\n        <module name=\"AnnotationUseStyle\"/>\n        <module name=\"MissingDeprecated\"/>\n        <module name=\"MissingOverride\"/>\n        <module name=\"PackageAnnotation\"/>\n\n        <!-- Blocks -->\n        <module name=\"AvoidNestedBlocks\"/>\n        <module name=\"EmptyBlock\">\n            <!-- Explicitly configure to avoid checking for catch blocks, which is handled differently in later versions -->\n            <property name=\"tokens\" value=\"LITERAL_WHILE,LITERAL_TRY,LITERAL_FINALLY,LITERAL_DO,LITERAL_IF,LITERAL_ELSE,LITERAL_FOR,STATIC_INIT,LITERAL_SWITCH\"/>\n        </module>\n        <module name=\"EmptyCatchBlock\">\n            <property name=\"exceptionVariableName\" value=\"expected|ignore\"/>\n        </module>\n        <module name=\"LeftCurly\"/>\n        <module name=\"NeedBraces\"/>\n        <module name=\"RightCurly\"/>\n\n        <!-- Class Design -->\n        <module name=\"InnerTypeLast\"/>\n        <module name=\"OneTopLevelClass\"/>\n\n        <!-- Coding -->\n        <module name=\"ArrayTrailingComma\"/>\n        <module name=\"CovariantEquals\"/>\n        <module name=\"DeclarationOrder\"/>\n        <module name=\"DefaultComesLast\"/>\n        <module name=\"EmptyStatement\"/>\n        <module name=\"EqualsAvoidNull\"/>\n        <module name=\"EqualsHashCode\"/>\n        <module name=\"ExplicitInitialization\"/>\n        <module name=\"FallThrough\"/>\n        <module name=\"HiddenField\">\n            <property name=\"ignoreConstructorParameter\" value=\"true\"/>\n            <property name=\"ignoreSetter\" value=\"true\"/>\n        </module>\n        <module name=\"IllegalCatch\"/>\n        <module name=\"IllegalThrows\"/>\n        <module name=\"IllegalType\"/>\n        <module name=\"InnerAssignment\"/>\n        <module name=\"MagicNumber\">\n            <property name=\"ignoreHashCodeMethod\" value=\"true\"/>\n            <property name=\"ignoreAnnotation\" value=\"true\"/>\n        </module>\n        <module name=\"MissingSwitchDefault\"/>\n        <module name=\"ModifiedControlVariable\"/>\n        <module name=\"MultipleVariableDeclarations\"/>\n        <module name=\"NestedForDepth\"/>\n        <module name=\"NestedIfDepth\">\n            <property name=\"max\" value=\"2\"/>\n        </module>\n        <module name=\"NestedTryDepth\"/>\n        <module name=\"NoClone\"/>\n        <module name=\"NoFinalizer\"/>\n        <module name=\"OneStatementPerLine\"/>\n        <module name=\"OverloadMethodsDeclarationOrder\"/>\n        <module name=\"PackageDeclaration\"/>\n        <module name=\"ParameterAssignment\"/>\n        <module name=\"SimplifyBooleanExpression\"/>\n        <module name=\"SimplifyBooleanReturn\"/>\n        <module name=\"StringLiteralEquality\"/>\n        <module name=\"SuperClone\"/>\n        <module name=\"SuperFinalize\"/>\n        <module name=\"UnnecessaryParentheses\"/>\n        <module name=\"UnnecessarySemicolonInEnumeration\"/>\n        <module name=\"UnnecessarySemicolonInTryWithResources\"/>\n\n        <!-- Imports -->\n        <module name=\"AvoidStarImport\"/>\n        <module name=\"IllegalImport\"/>\n        <module name=\"ImportControl\">\n            <property name=\"file\" value=\"${basedir}/import-control.xml\"/>\n        </module>\n        <module name=\"ImportOrder\">\n            <property name=\"option\" value=\"bottom\"/>\n            <property name=\"ordered\" value=\"true\"/>\n            <property name=\"separated\" value=\"true\"/>\n        </module>\n        <module name=\"RedundantImport\"/>\n        <module name=\"UnusedImports\"/>\n\n        <!-- Miscellaneous -->\n        <module name=\"ArrayTypeStyle\"/>\n        <module name=\"AvoidEscapedUnicodeCharacters\"/>\n        <module name=\"CommentsIndentation\"/>\n        <module name=\"Indentation\"/>\n        <module name=\"OuterTypeFilename\"/>\n        <module name=\"UpperEll\"/>\n\n        <!-- Modifiers -->\n        <module name=\"ModifierOrder\"/>\n        <module name=\"RedundantModifier\"/>\n\n        <!-- Sizes -->\n        <module name=\"LineLength\">\n            <property name=\"max\" value=\"140\"/>\n        </module>\n\n        <!-- Whitespace -->\n        <module name=\"GenericWhitespace\"/>\n        <module name=\"EmptyForInitializerPad\"/>\n        <module name=\"EmptyForIteratorPad\"/>\n        <module name=\"MethodParamPad\"/>\n        <module name=\"NoWhitespaceAfter\"/>\n        <module name=\"NoWhitespaceBefore\"/>\n        <module name=\"OperatorWrap\"/>\n        <module name=\"ParenPad\"/>\n        <module name=\"TypecastParenPad\"/>\n        <module name=\"WhitespaceAfter\"/>\n        <module name=\"WhitespaceAround\">\n            <property name=\"allowEmptyConstructors\" value=\"true\"/>\n            <property name=\"allowEmptyMethods\" value=\"true\"/>\n        </module>\n\n        <module name=\"SuppressWarningsHolder\"/>\n    </module>\n\n    <!-- Miscellaneous -->\n    <module name=\"NewlineAtEndOfFile\">\n        <property name=\"lineSeparator\" value=\"lf\"/>\n    </module>\n    <module name=\"OrderedProperties\"/>\n\n    <!-- Whitespace -->\n    <module name=\"FileTabCharacter\"/>\n\n    <!-- Leftover code templates -->\n    <module name=\"RegexpSingleline\">\n        <property name=\"format\" value=\"File \\| Settings \\| File Templates\"/>\n    </module>\n    <module name=\"RegexpSingleline\">\n        <property name=\"format\" value=\"Created with IntelliJ IDEA\"/>\n    </module>\n\n    <module name=\"SuppressWarningsFilter\"/>\n\n</module>\n"
  },
  {
    "path": "config/checkstyle/import-control.xml",
    "content": "<!DOCTYPE import-control PUBLIC\n    \"-//Checkstyle//DTD ImportControl Configuration 1.4//EN\"\n    \"https://checkstyle.org/dtds/import_control_1_4.dtd\">\n<import-control pkg=\"com.github.davidmc24.gradle.plugin.avro\">\n    <allow pkg=\"com.github.davidmc24.gradle.plugin.avro\"/>\n\n    <allow pkg=\"java.io\"/>\n    <allow pkg=\"java.lang\"/>\n    <allow pkg=\"java.net\"/>\n    <allow pkg=\"java.nio.charset\"/>\n    <allow pkg=\"java.nio.file\"/>\n    <allow pkg=\"java.util\"/>\n    <allow pkg=\"java.util.concurrent\"/>\n    <allow pkg=\"javax.annotation\"/>\n    <allow pkg=\"javax.inject\"/>\n\n    <allow pkg=\"org.apache.avro\"/>\n    <allow pkg=\"org.apache.avro.compiler.idl\"/>\n    <allow pkg=\"org.apache.avro.compiler.specific\"/>\n    <allow pkg=\"org.apache.avro.generic\"/>\n\n    <allow pkg=\"org.gradle.api\"/>\n    <allow pkg=\"org.gradle.plugins.ide.idea\"/>\n    <allow pkg=\"org.gradle.util\"/>\n</import-control>\n"
  },
  {
    "path": "config/codenarc/codenarc.groovy",
    "content": "ruleset {\n\n    // rulesets/basic.xml\n    AssertWithinFinallyBlock\n    AssignmentInConditional\n    BigDecimalInstantiation\n    BitwiseOperatorInConditional\n    BooleanGetBoolean\n    BrokenNullCheck\n    BrokenOddnessCheck\n    ClassForName\n    ComparisonOfTwoConstants\n    ComparisonWithSelf\n    ConstantAssertExpression\n    ConstantIfExpression\n    ConstantTernaryExpression\n    DeadCode\n    DoubleNegative\n    DuplicateCaseStatement\n    DuplicateMapKey\n    DuplicateSetValue\n    EmptyCatchBlock\n    EmptyClass\n    EmptyElseBlock\n    EmptyFinallyBlock\n    EmptyForStatement\n    EmptyIfStatement\n    EmptyInstanceInitializer\n    EmptyMethod\n    EmptyStaticInitializer\n    EmptySwitchStatement\n    EmptySynchronizedStatement\n    EmptyTryBlock\n    EmptyWhileStatement\n    EqualsAndHashCode\n    EqualsOverloaded\n    ExplicitGarbageCollection\n    ForLoopShouldBeWhileLoop\n    HardCodedWindowsFileSeparator\n    HardCodedWindowsRootDirectory\n    IntegerGetInteger\n    RandomDoubleCoercedToZero\n    RemoveAllOnSelf\n    ReturnFromFinallyBlock\n    ThrowExceptionFromFinallyBlock\n\n    // rulesets/braces.xml\n    ElseBlockBraces\n    ForStatementBraces\n    IfStatementBraces\n    WhileStatementBraces\n\n    // rulesets/concurrency.xml\n    BusyWait\n    DoubleCheckedLocking\n    InconsistentPropertyLocking\n    InconsistentPropertySynchronization\n    NestedSynchronization\n    StaticCalendarField\n    StaticConnection\n    StaticDateFormatField\n    StaticMatcherField\n    StaticSimpleDateFormatField\n    SynchronizedMethod\n    SynchronizedOnBoxedPrimitive\n    SynchronizedOnGetClass\n    SynchronizedOnReentrantLock\n    SynchronizedOnString\n    SynchronizedOnThis\n    SynchronizedReadObjectMethod\n    SystemRunFinalizersOnExit\n    ThisReferenceEscapesConstructor\n    ThreadGroup\n    ThreadLocalNotStaticFinal\n    ThreadYield\n    UseOfNotifyMethod\n    VolatileArrayField\n    VolatileLongOrDoubleField\n    WaitOutsideOfWhileLoop\n\n    // rulesets/convention.xml\n    ConfusingTernary\n    CouldBeElvis\n    HashtableIsObsolete\n    IfStatementCouldBeTernary\n    InvertedIfElse\n    LongLiteralWithLowerCaseL\n    ParameterReassignment\n    TernaryCouldBeElvis\n    VectorIsObsolete\n\n    // rulesets/design.xml\n    AbstractClassWithPublicConstructor\n    BooleanMethodReturnsNull\n    BuilderMethodWithSideEffects\n    CloneableWithoutClone\n    CloseWithoutCloseable\n    CompareToWithoutComparable\n    ConstantsOnlyInterface\n    EmptyMethodInAbstractClass\n    FinalClassWithProtectedMember\n    ImplementationAsType\n    LocaleSetDefault\n    PrivateFieldCouldBeFinal\n    PublicInstanceField\n    ReturnsNullInsteadOfEmptyArray\n    ReturnsNullInsteadOfEmptyCollection\n    SimpleDateFormatMissingLocale\n    StatelessSingleton\n\n    // rulesets/exceptions.xml\n    CatchArrayIndexOutOfBoundsException\n    CatchError\n    CatchException\n    CatchIllegalMonitorStateException\n    CatchIndexOutOfBoundsException\n    CatchNullPointerException\n    CatchRuntimeException\n    CatchThrowable\n    ConfusingClassNamedException\n    ExceptionExtendsError\n    ExceptionNotThrown\n    MissingNewInThrowStatement\n    ReturnNullFromCatchBlock\n    SwallowThreadDeath\n    ThrowError\n    ThrowException\n    ThrowNullPointerException\n    ThrowRuntimeException\n    ThrowThrowable\n\n    // rulesets/formatting.xml\n    BracesForClass\n    BracesForForLoop\n    BracesForIfElse\n    BracesForMethod\n    BracesForTryCatchFinally\n    ClosureStatementOnOpeningLineOfMultipleLineClosure\n    LineLength (length: 140)\n    SpaceAfterCatch\n    SpaceAfterClosingBrace\n    SpaceAfterComma\n    SpaceAfterFor\n    SpaceAfterIf\n    SpaceAfterOpeningBrace\n    SpaceAfterSemicolon\n    SpaceAfterSwitch\n    SpaceAfterWhile\n    SpaceAroundClosureArrow\n    SpaceAroundMapEntryColon (characterAfterColonRegex: /\\s/)\n    SpaceAroundOperator\n    SpaceBeforeClosingBrace\n    SpaceBeforeOpeningBrace\n\n    // rulesets/generic.xml\n    IllegalClassMember\n    IllegalClassReference\n    IllegalPackageReference\n    IllegalRegex\n    IllegalString\n    RequiredRegex\n    RequiredString\n    StatelessClass\n\n    // rulesets/groovyism.xml\n    AssignCollectionSort\n    AssignCollectionUnique\n    ClosureAsLastMethodParameter\n    CollectAllIsDeprecated\n    ConfusingMultipleReturns\n    ExplicitArrayListInstantiation\n    ExplicitCallToAndMethod\n    ExplicitCallToCompareToMethod\n    ExplicitCallToDivMethod\n    ExplicitCallToEqualsMethod\n    ExplicitCallToGetAtMethod\n    ExplicitCallToLeftShiftMethod\n    ExplicitCallToMinusMethod\n    ExplicitCallToModMethod\n    ExplicitCallToMultiplyMethod\n    ExplicitCallToOrMethod\n    ExplicitCallToPlusMethod\n    ExplicitCallToPowerMethod\n    ExplicitCallToRightShiftMethod\n    ExplicitCallToXorMethod\n    ExplicitHashMapInstantiation\n    ExplicitHashSetInstantiation\n    ExplicitLinkedHashMapInstantiation\n    ExplicitLinkedListInstantiation\n    ExplicitStackInstantiation\n    ExplicitTreeSetInstantiation\n    GStringAsMapKey\n    GStringExpressionWithinString\n    GetterMethodCouldBeProperty\n    GroovyLangImmutable\n    UseCollectMany\n    UseCollectNested\n\n    // rulesets/imports.xml\n    DuplicateImport\n    ImportFromSamePackage\n    ImportFromSunPackages\n    MisorderedStaticImports (comesBefore: false)\n    UnnecessaryGroovyImport\n    UnusedImport\n\n    // rulesets/junit.xml\n    ChainedTest\n    CoupledTestCase\n    JUnitAssertAlwaysFails\n    JUnitAssertAlwaysSucceeds\n    JUnitFailWithoutMessage\n    JUnitLostTest\n    JUnitPublicField\n    // Triggers incorrectly for Spock methods\n    // JUnitPublicNonTestMethod\n    JUnitSetUpCallsSuper\n    JUnitStyleAssertions\n    JUnitTearDownCallsSuper\n    JUnitTestMethodWithoutAssert\n    JUnitUnnecessarySetUp\n    JUnitUnnecessaryTearDown\n    JUnitUnnecessaryThrowsException\n    SpockIgnoreRestUsed\n    UnnecessaryFail\n    UseAssertEqualsInsteadOfAssertTrue\n    UseAssertFalseInsteadOfNegation\n    UseAssertNullInsteadOfAssertEquals\n    UseAssertSameInsteadOfAssertTrue\n    UseAssertTrueInsteadOfAssertEquals\n    UseAssertTrueInsteadOfNegation\n\n    // rulesets/logging.xml\n    LoggerForDifferentClass\n    LoggerWithWrongModifiers\n    LoggingSwallowsStacktrace\n    MultipleLoggers\n    PrintStackTrace\n    Println\n    SystemErrPrint\n    SystemOutPrint\n\n    // rulesets/naming.xml\n    AbstractClassName\n    ClassName\n    ClassNameSameAsFilename\n    ConfusingMethodName\n    FieldName\n    InterfaceName\n    ObjectOverrideMisspelledMethodName\n    PackageName\n    ParameterName\n    PropertyName\n    VariableName\n\n    // rulesets/security.xml\n    FileCreateTempFile\n    InsecureRandom\n    NonFinalPublicField\n    NonFinalSubclassOfSensitiveInterface\n    ObjectFinalize\n    PublicFinalizeMethod\n    SystemExit\n    UnsafeArrayDeclaration\n\n    // rulesets/serialization.xml\n    EnumCustomSerializationIgnored\n    SerialPersistentFields\n    SerialVersionUID\n    SerializableClassMustDefineSerialVersionUID\n\n    // rulesets/size.xml\n    ClassSize\n    MethodCount\n    MethodSize\n    NestedBlockDepth\n\n    // rulesets/unnecessary.xml\n    AddEmptyString\n    ConsecutiveLiteralAppends\n    ConsecutiveStringConcatenation\n    UnnecessaryBigDecimalInstantiation\n    UnnecessaryBigIntegerInstantiation\n    UnnecessaryBooleanExpression\n    UnnecessaryBooleanInstantiation\n    UnnecessaryCallForLastElement\n    UnnecessaryCallToSubstring\n    UnnecessaryCatchBlock\n    UnnecessaryCollectCall\n    UnnecessaryCollectionCall\n    UnnecessaryConstructor\n    UnnecessaryDefInFieldDeclaration\n    UnnecessaryDefInMethodDeclaration\n    UnnecessaryDefInVariableDeclaration\n    UnnecessaryDotClass\n    UnnecessaryDoubleInstantiation\n    UnnecessaryElseStatement\n    UnnecessaryFinalOnPrivateMethod\n    UnnecessaryFloatInstantiation\n    UnnecessaryGetter\n    UnnecessaryIfStatement\n    UnnecessaryInstanceOfCheck\n    UnnecessaryInstantiationToGetClass\n    UnnecessaryIntegerInstantiation\n    UnnecessaryLongInstantiation\n    UnnecessaryModOne\n    UnnecessaryNullCheck\n    UnnecessaryNullCheckBeforeInstanceOf\n    // Unnecessarily complicates Spock assertions\n    // UnnecessaryObjectReferences\n    UnnecessaryOverridingMethod\n    UnnecessaryPackageReference\n    UnnecessaryParenthesesForMethodCallWithClosure\n    UnnecessaryPublicModifier\n    UnnecessarySelfAssignment\n    UnnecessarySemicolon\n    UnnecessaryStringInstantiation\n    UnnecessaryTernaryExpression\n    UnnecessaryTransientModifier\n\n    // rulesets/unused.xml\n    UnusedArray\n    UnusedMethodParameter\n    UnusedObject\n    UnusedPrivateField\n    UnusedPrivateMethod\n    UnusedPrivateMethodParameter\n    UnusedVariable\n}\n"
  },
  {
    "path": "design-docs/configurations-for-additional-schema.md",
    "content": "Periodically, we get requests for help figuring out how to\nload schema files from a JAR, whether it is a JAR from a\nrepository or the outputs of a subproject.\n\n`examples/avsc-from-subproject` gives an example of how\nthis can be configured based on a new configuration.\nWe might want to consider having the Avro plugin create\nand configure such configurations as part of the\nconventional usage pattern.\n\nIf we do this, a few gotchas:\n\n1. We need to take into account all sourceSets (main, test, etc.)\n2. We need to consider the naming so it's clear what the\n   configurations are for and don't conflict with other plugins.\n   For example, `additionalSchema` is too ambiguous.\n   `additionalAvroSchema` might be better.\n3. Whether it makes sense to exclude generated classes from jars\n"
  },
  {
    "path": "design-docs/external-schemata-and-protocols.md",
    "content": "Originally requested as [#4](https://github.com/davidmc24/gradle-avro-plugin/issues/4).\nSome users would like the ability to have JAR files that contain Avro schema/protocol files, and have a way to declare a dependency on these, such that the plugin's generation capability can use them without needing to manual extract the archives.\n\nIntended approach:\n\n* The plugin defines a new `<sourceSetName>Avro` configuration for every source-set that it is working with.\n* As with any configuration, the build script can define dependencies in the configuration, and the resolution mechanism will resolve the configuration against the configured repositories when it is resolved.\n* A task would be added to extract all such archives to a `<buildDir>/unpacked-<sourceSetName>-avro` directory, per source-set\n* The plugin's generation tasks would be configured to use the relevant unpacked directories as additional source directories.\n"
  },
  {
    "path": "design-docs/run-avro-as-an-external-process.md",
    "content": "Originally reported as [#27](https://github.com/davidmc24/gradle-avro-plugin/issues/27).\nCurrently, Avro generation takes by running the avro-compiler library as part of the Gradle plugin process.\nThis is simple and works, but has a few drawbacks:\n\n* Custom templates need to be available on the classpath for the plugin, which isn't compatible with the new style of Gradle plugin declarations.\n* The Gradle plugin may use a different version of Avro for generation than you're using on the compile classpath for compilation.\n\nInstead, here is an alternative view of how it could work.\n\n* There is an enhanced-avro-compiler library that externalizes most of the logic currently present in GenerateAvroJavaTask/GenerateAvroProtocolTask, and makes those calls accessible as JVM entry points (via `main` methods).\n    * This library would be published on Maven Central, and potentially have multiple versions as needed for compatibility with multiple versions of Avro\n    * It's possible we might be able to get this logic pushed upstream into avro-compiler, in which case the need for this library would be eliminated.\n* For a source-set, the plugin would take a single declaration of the desired Avro version, which is then used for both generation and compilation\n* The plugin would use a configuration per source-set to resolve the appropriate version of enhanced-avro-compiler\n    * The build script could add additional dependencies to this configuration in order to pull in custom templates\n* When doing generation, the plugin would use [JavaExec](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/JavaExec.html) to spawn a child JVM and execute the appropriate logic in enhanced-avro-compiler.\n"
  },
  {
    "path": "examples/avsc-from-external-jar/README.md",
    "content": "# Purpose\n\nAn example project for having dependencies on .avsc schema files loaded from an external JAR file\n(not produced by the current Gradle project).\n\n# Maintainer Notes\n\n* Command to create JAR: `(cd external-files && jar --create --no-manifest --file ../external-libs/schema.jar Breed.avsc)`\n"
  },
  {
    "path": "examples/avsc-from-external-jar/build.gradle",
    "content": "plugins {\n    id \"com.github.davidmc24.gradle.plugin.avro\" version \"1.2.1\"\n}\n\nrepositories {\n    mavenCentral()\n}\ndependencies {\n    implementation \"org.apache.avro:avro:1.10.1\"\n}\n\ngenerateAvroJava {\n    source zipTree(\"external-libs/schema.jar\")\n}\n"
  },
  {
    "path": "examples/avsc-from-external-jar/external-files/Breed.avsc",
    "content": "{\n    \"name\": \"Breed\",\n    \"namespace\": \"example\",\n    \"type\": \"enum\",\n    \"symbols\" : [\"ABYSSINIAN\", \"AMERICAN_SHORTHAIR\", \"BIRMAN\", \"MAINE_COON\", \"ORIENTAL\", \"PERSIAN\", \"RAGDOLL\", \"SIAMESE\", \"SPHYNX\"]\n}\n"
  },
  {
    "path": "examples/avsc-from-external-jar/external-files/Cat.avsc",
    "content": "{\n    \"name\": \"Cat\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"breed\", \"type\": \"Breed\"}\n    ]\n}\n"
  },
  {
    "path": "examples/avsc-from-external-jar/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.3-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "examples/avsc-from-external-jar/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\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\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\nAPP_HOME=$( cd \"${APP_HOME:-./}\" && pwd -P ) || exit\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=${0##*/}\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -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    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n# Collect all arguments for the java command;\n#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of\n#     shell script including quotes and variable substitutions, so put them in\n#     double quotes to make sure that they get re-expanded; and\n#   * put everything else in single quotes, so that it's not re-expanded.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "examples/avsc-from-external-jar/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n\r\n@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "examples/avsc-from-external-jar/settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        mavenCentral()\n        mavenLocal()\n    }\n}\n\nrootProject.name = \"avsc-from-external-jar\"\n"
  },
  {
    "path": "examples/avsc-from-external-jar/src/main/avro/Cat.avsc",
    "content": "{\n    \"name\": \"Cat\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"breed\", \"type\": \"Breed\"}\n    ]\n}\n"
  },
  {
    "path": "examples/avsc-from-subproject/README.md",
    "content": "# Purpose\n\nAn example project for having dependencies on .avsc schema files loaded from an JAR file\nproduced by a subproject of the current multi-project Gradle build.\n\n# Variants\n            \n## schema project JAR doesn't contain classes\n\nIf you'd rather have the `schema` project **not** generate Java classes, you can rename `src/main/avro` to `src/main/resources`.\nIn that case, you can also replace the Avro plugin with the `java` plugin.\n"
  },
  {
    "path": "examples/avsc-from-subproject/cat/build.gradle",
    "content": "import java.util.zip.ZipFile\n\nplugins {\n    id \"com.github.davidmc24.gradle.plugin.avro\" version \"1.2.1\"\n}\n\nrepositories {\n    mavenCentral()\n}\nconfigurations {\n    additionalSchema\n}\ndependencies {\n    implementation \"org.apache.avro:avro:1.10.1\"\n    additionalSchema project(\":schema\")\n}\n\ngenerateAvroJava {\n    dependsOn configurations.additionalSchema\n    source {\n        // As of Gradle 7.2, Using zipTree within source appears to disable build caching\n        configurations.additionalSchema.collect { zipTree(it) }\n    }\n}\n\ndef configureJar = tasks.register(\"configureJar\") {\n    it.doLast {\n        // Exclude classes that are already in schema.jar from this jar\n        tasks.jar.exclude(\n            configurations.additionalSchema\n                .findAll { it.name.endsWith(\"jar\") }\n                .collect { File file ->\n                    new ZipFile(file).entries()\n                        .findAll { it.name.endsWith(\".class\") }\n                        .collect { it.name }\n                }\n                .flatten()\n        )\n    }\n    // otherwise the jars of dependent projects might not have been built\n    // TODO is there a way to copy the dependencies of the jar task? classes is not part of tasks.jar.dependsOn\n    it.dependsOn(tasks.classes)\n}\n\ntasks.named(\"jar\") {\n    it.dependsOn(configureJar)\n}\n"
  },
  {
    "path": "examples/avsc-from-subproject/cat/src/main/avro/Cat.avsc",
    "content": "{\n    \"name\": \"Cat\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"breed\", \"type\": \"Breed\"}\n    ]\n}\n"
  },
  {
    "path": "examples/avsc-from-subproject/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.3-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "examples/avsc-from-subproject/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\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\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\nAPP_HOME=$( cd \"${APP_HOME:-./}\" && pwd -P ) || exit\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=${0##*/}\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -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    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n# Collect all arguments for the java command;\n#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of\n#     shell script including quotes and variable substitutions, so put them in\n#     double quotes to make sure that they get re-expanded; and\n#   * put everything else in single quotes, so that it's not re-expanded.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "examples/avsc-from-subproject/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n\r\n@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "examples/avsc-from-subproject/schema/build.gradle",
    "content": "plugins {\n    id \"com.github.davidmc24.gradle.plugin.avro\" version \"1.2.1\"\n}\n\nrepositories {\n    mavenCentral()\n}\ndependencies {\n    compileOnly \"org.apache.avro:avro:1.10.1\"\n}\n\nsourceSets {\n    main {\n        resources {\n            srcDirs \"src/main/avro\"\n        }\n    }\n}\n"
  },
  {
    "path": "examples/avsc-from-subproject/schema/src/main/avro/Breed.avsc",
    "content": "{\n    \"name\": \"Breed\",\n    \"namespace\": \"example\",\n    \"type\": \"enum\",\n    \"symbols\" : [\"ABYSSINIAN\", \"AMERICAN_SHORTHAIR\", \"BIRMAN\", \"MAINE_COON\", \"ORIENTAL\", \"PERSIAN\", \"RAGDOLL\", \"SIAMESE\", \"SPHYNX\"]\n}\n"
  },
  {
    "path": "examples/avsc-from-subproject/settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        mavenCentral()\n        mavenLocal()\n    }\n}\n\nrootProject.name = \"avsc-from-subproject\"\n\ninclude \"schema\"\ninclude \"cat\"\n"
  },
  {
    "path": "examples/default-custom-types/README.md",
    "content": "This example demonstrates a custom plugin that registers a logical type factory and custom conversion.\n\nTo simplify the example, the custom conversion/logicalTypeFactory classes are duplicated in both buildSrc and the project.\nIn the real world, you would likely put them in a separate project, publish them as a JAR, and depend on them in both places.\n"
  },
  {
    "path": "examples/default-custom-types/build.gradle",
    "content": "apply plugin: custom.AvroConventionPlugin\n\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation 'org.apache.avro:avro:1.11.0'\n}\n"
  },
  {
    "path": "examples/default-custom-types/buildSrc/build.gradle",
    "content": "repositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation 'com.github.davidmc24.gradle.plugin:gradle-avro-plugin:1.2.0'\n    implementation 'org.apache.avro:avro:1.11.0'\n}\n"
  },
  {
    "path": "examples/default-custom-types/buildSrc/src/main/java/custom/AvroConventionPlugin.java",
    "content": "package custom;\n\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport com.github.davidmc24.gradle.plugin.avro.AvroPlugin;\nimport com.github.davidmc24.gradle.plugin.avro.AvroExtension;\n\npublic class AvroConventionPlugin implements Plugin<Project> {\n    public void apply(Project project) {\n        project.getPluginManager().apply(AvroPlugin.class);\n        AvroExtension avroExtension = project.getExtensions().findByType(AvroExtension.class);\n        avroExtension.logicalTypeFactory(\"timezone\", TimeZoneLogicalTypeFactory.class);\n        avroExtension.customConversion(TimeZoneConversion.class);\n    }\n}\n"
  },
  {
    "path": "examples/default-custom-types/buildSrc/src/main/java/custom/TimeZoneConversion.java",
    "content": "package custom;\n\nimport java.util.TimeZone;\nimport org.apache.avro.Conversion;\nimport org.apache.avro.LogicalType;\nimport org.apache.avro.Schema;\n\n@SuppressWarnings(\"unused\")\npublic class TimeZoneConversion extends Conversion<TimeZone> {\n    public static final String LOGICAL_TYPE_NAME = \"timezone\";\n\n    @Override\n    public Class<TimeZone> getConvertedType() {\n        return TimeZone.class;\n    }\n\n    @Override\n    public String getLogicalTypeName() {\n        return LOGICAL_TYPE_NAME;\n    }\n\n    @Override\n    public TimeZone fromCharSequence(CharSequence value, Schema schema, LogicalType type) {\n        return TimeZone.getTimeZone(value.toString());\n    }\n\n    @Override\n    public CharSequence toCharSequence(TimeZone value, Schema schema, LogicalType type) {\n        return value.getID();\n    }\n\n    @Override\n    public Schema getRecommendedSchema() {\n        return TimeZoneLogicalType.INSTANCE.addToSchema(Schema.create(Schema.Type.STRING));\n    }\n}\n"
  },
  {
    "path": "examples/default-custom-types/buildSrc/src/main/java/custom/TimeZoneLogicalType.java",
    "content": "package custom;\n\nimport org.apache.avro.LogicalType;\nimport org.apache.avro.Schema;\n\npublic class TimeZoneLogicalType extends LogicalType {\n    static final TimeZoneLogicalType INSTANCE = new TimeZoneLogicalType();\n\n    private TimeZoneLogicalType() {\n        super(TimeZoneConversion.LOGICAL_TYPE_NAME);\n    }\n\n    @Override\n    public void validate(Schema schema) {\n        super.validate(schema);\n        if (schema.getType() != Schema.Type.STRING) {\n            throw new IllegalArgumentException(\"Timezone can only be used with an underlying string type\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/default-custom-types/buildSrc/src/main/java/custom/TimeZoneLogicalTypeFactory.java",
    "content": "package custom;\n\nimport org.apache.avro.LogicalType;\nimport org.apache.avro.LogicalTypes;\nimport org.apache.avro.Schema;\n\npublic class TimeZoneLogicalTypeFactory implements LogicalTypes.LogicalTypeFactory {\n    @Override\n    public LogicalType fromSchema(Schema schema) {\n        return TimeZoneLogicalType.INSTANCE;\n    }\n}\n"
  },
  {
    "path": "examples/default-custom-types/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.0-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "examples/default-custom-types/gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\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\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -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\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -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    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif [ \"$cygwin\" = \"true\" -o \"$msys\" = \"true\" ] ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=`expr $i + 1`\n    done\n    case $i in\n        0) set -- ;;\n        1) set -- \"$args0\" ;;\n        2) set -- \"$args0\" \"$args1\" ;;\n        3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=`save \"$@\"`\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "examples/default-custom-types/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n\r\n@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "examples/default-custom-types/settings.gradle",
    "content": "rootProject.name = 'default-custom-types'\n"
  },
  {
    "path": "examples/default-custom-types/src/main/avro/customConversion.avsc",
    "content": "{\"namespace\": \"example\",\n \"type\": \"record\",\n \"name\": \"Event\",\n \"fields\": [\n     {\"name\": \"name\", \"type\": \"string\"},\n     {\"name\": \"start\", \"type\": {\"type\": \"long\", \"logicalType\": \"timestamp-millis\"} },\n     {\"name\": \"timezone\", \"type\": {\"type\": \"string\", \"logicalType\": \"timezone\"} }\n ]\n}\n"
  },
  {
    "path": "examples/default-custom-types/src/main/java/custom/TimeZoneConversion.java",
    "content": "package custom;\n\nimport java.util.TimeZone;\nimport org.apache.avro.Conversion;\nimport org.apache.avro.LogicalType;\nimport org.apache.avro.Schema;\n\n@SuppressWarnings(\"unused\")\npublic class TimeZoneConversion extends Conversion<TimeZone> {\n    public static final String LOGICAL_TYPE_NAME = \"timezone\";\n\n    @Override\n    public Class<TimeZone> getConvertedType() {\n        return TimeZone.class;\n    }\n\n    @Override\n    public String getLogicalTypeName() {\n        return LOGICAL_TYPE_NAME;\n    }\n\n    @Override\n    public TimeZone fromCharSequence(CharSequence value, Schema schema, LogicalType type) {\n        return TimeZone.getTimeZone(value.toString());\n    }\n\n    @Override\n    public CharSequence toCharSequence(TimeZone value, Schema schema, LogicalType type) {\n        return value.getID();\n    }\n\n    @Override\n    public Schema getRecommendedSchema() {\n        return TimeZoneLogicalType.INSTANCE.addToSchema(Schema.create(Schema.Type.STRING));\n    }\n}\n"
  },
  {
    "path": "examples/default-custom-types/src/main/java/custom/TimeZoneLogicalType.java",
    "content": "package custom;\n\nimport org.apache.avro.LogicalType;\nimport org.apache.avro.Schema;\n\npublic class TimeZoneLogicalType extends LogicalType {\n    static final TimeZoneLogicalType INSTANCE = new TimeZoneLogicalType();\n\n    private TimeZoneLogicalType() {\n        super(TimeZoneConversion.LOGICAL_TYPE_NAME);\n    }\n\n    @Override\n    public void validate(Schema schema) {\n        super.validate(schema);\n        if (schema.getType() != Schema.Type.STRING) {\n            throw new IllegalArgumentException(\"Timezone can only be used with an underlying string type\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/default-custom-types/src/main/java/custom/TimeZoneLogicalTypeFactory.java",
    "content": "package custom;\n\nimport org.apache.avro.LogicalType;\nimport org.apache.avro.LogicalTypes;\nimport org.apache.avro.Schema;\n\npublic class TimeZoneLogicalTypeFactory implements LogicalTypes.LogicalTypeFactory {\n    @Override\n    public LogicalType fromSchema(Schema schema) {\n        return TimeZoneLogicalType.INSTANCE;\n    }\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.6-bin.zip\nnetworkTimeout=10000\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "org.gradle.warning.mode=all\norg.gradle.parallel=true\norg.gradle.vfs.watch=true\nsystemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\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\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\nAPP_HOME=$( cd \"${APP_HOME:-./}\" && pwd -P ) || exit\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -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    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC3045 \n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC3045 \n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n# Collect all arguments for the java command;\n#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of\n#     shell script including quotes and variable substitutions, so put them in\n#     double quotes to make sure that they get re-expanded; and\n#   * put everything else in single quotes, so that it's not re-expanded.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "scripts/run-avro-cli.sh",
    "content": "#!/usr/bin/env bash\nset -ex\navroVersion=${1?\"Usage: $0 AVRO_VERSION ARGUMENTS\"};\nshift\nmkdir -p downloads\nwget --timestamping --directory-prefix=downloads/ http://archive.apache.org/dist/avro/avro-${avroVersion}/java/avro-tools-${avroVersion}.jar\njava -jar downloads/avro-tools-${avroVersion}.jar $*\n"
  },
  {
    "path": "scripts/run-compile-schema.sh",
    "content": "#!/usr/bin/env bash\nset -ex\navroVersion=${1?\"Usage: $0 AVRO_VERSION SCHEMA_FILE\"};\nschemaFile=${2?\"Usage: $0 AVRO_VERSION SCHEMA_FILE\"};\nmkdir -p input/\nmkdir -p output/\n./run-avro-cli.sh $1 compile schema input/$2 output/\n"
  },
  {
    "path": "settings.gradle",
    "content": "plugins {\n    id \"com.gradle.enterprise\" version \"3.3.4\"\n}\n\nrootProject.name = \"gradle-avro-plugin\"\n\ngradleEnterprise {\n    buildScan {\n        if (System.getenv(\"CI\")) {\n            termsOfServiceUrl = \"https://gradle.com/terms-of-service\"\n            termsOfServiceAgree = \"yes\"\n            publishAlways()\n            uploadInBackground = false\n            tag \"CI\"\n        } else {\n            tag \"Local\"\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/AvroBasePlugin.java",
    "content": "/**\n * Copyright © 2014-2019 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\n\npublic class AvroBasePlugin implements Plugin<Project> {\n    @Override\n    public void apply(final Project project) {\n        configureExtension(project);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    private static void configureExtension(final Project project) {\n        final AvroExtension avroExtension =\n            GradleCompatibility.createExtensionWithObjectFactory(project, Constants.AVRO_EXTENSION_NAME, DefaultAvroExtension.class);\n        project.getTasks().withType(GenerateAvroJavaTask.class).configureEach(task -> {\n            task.getOutputCharacterEncoding().convention(avroExtension.getOutputCharacterEncoding());\n            task.getStringType().convention(avroExtension.getStringType());\n            task.getFieldVisibility().convention(avroExtension.getFieldVisibility());\n            task.getTemplateDirectory().convention(avroExtension.getTemplateDirectory());\n            task.getAdditionalVelocityToolClasses().convention(avroExtension.getAdditionalVelocityToolClasses());\n            task.isCreateSetters().convention(avroExtension.isCreateSetters());\n            task.isCreateOptionalGetters().convention(avroExtension.isCreateOptionalGetters());\n            task.isGettersReturnOptional().convention(avroExtension.isGettersReturnOptional());\n            task.isOptionalGettersForNullableFieldsOnly().convention(avroExtension.isOptionalGettersForNullableFieldsOnly());\n            task.isEnableDecimalLogicalType().convention(avroExtension.isEnableDecimalLogicalType());\n            task.getConversionsAndTypeFactoriesClasspath().from(avroExtension.getConversionsAndTypeFactoriesClasspath());\n            task.getLogicalTypeFactories().convention(avroExtension.getLogicalTypeFactories());\n            task.getLogicalTypeFactoryClassNames().convention(avroExtension.getLogicalTypeFactoryClassNames());\n            task.getCustomConversions().convention(avroExtension.getCustomConversions());\n            task.getCustomConversionClassNames().convention(avroExtension.getCustomConversionClassNames());\n        });\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/AvroExtension.java",
    "content": "/**\n * Copyright © 2013-2019 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport org.apache.avro.Conversion;\nimport org.apache.avro.LogicalTypes;\nimport org.gradle.api.file.ConfigurableFileCollection;\nimport org.gradle.api.provider.ListProperty;\nimport org.gradle.api.provider.MapProperty;\nimport org.gradle.api.provider.Property;\n\n@SuppressWarnings(\"unused\")\npublic interface AvroExtension {\n    Property<String> getOutputCharacterEncoding();\n    Property<String> getStringType();\n    Property<String> getFieldVisibility();\n    Property<String> getTemplateDirectory();\n    ListProperty<String> getAdditionalVelocityToolClasses();\n    Property<Boolean> isCreateSetters();\n    Property<Boolean> isCreateOptionalGetters();\n    Property<Boolean> isGettersReturnOptional();\n    Property<Boolean> isOptionalGettersForNullableFieldsOnly();\n    Property<Boolean> isEnableDecimalLogicalType();\n    ConfigurableFileCollection getConversionsAndTypeFactoriesClasspath();\n\n    /**\n     * @deprecated use {@link #getLogicalTypeFactoryClassNames()} instead\n     */\n    @Deprecated\n    MapProperty<String, Class<? extends LogicalTypes.LogicalTypeFactory>> getLogicalTypeFactories();\n    MapProperty<String, String> getLogicalTypeFactoryClassNames();\n\n    /**\n     * @deprecated use {@link #getCustomConversionClassNames()} instead\n     */\n    @Deprecated\n    ListProperty<Class<? extends Conversion<?>>> getCustomConversions();\n    ListProperty<String> getCustomConversionClassNames();\n\n    /**\n     * @deprecated use {@link #logicalTypeFactory(String, String)}\n     */\n    @Deprecated\n    AvroExtension logicalTypeFactory(String typeName, Class<? extends LogicalTypes.LogicalTypeFactory> typeFactoryClass);\n    AvroExtension logicalTypeFactory(String typeName, String typeFactoryClassName);\n\n    /**\n     * @deprecated use {@link #customConversion(String)} instead\n     */\n    @Deprecated\n    AvroExtension customConversion(Class<? extends Conversion<?>> conversionClass);\n    AvroExtension customConversion(String conversionClassName);\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/AvroPlugin.java",
    "content": "/**\n * Copyright © 2013-2019 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.io.File;\nimport java.io.FileFilter;\nimport java.nio.charset.Charset;\nimport java.util.Optional;\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.file.Directory;\nimport org.gradle.api.plugins.JavaPlugin;\nimport org.gradle.api.provider.Provider;\nimport org.gradle.api.tasks.SourceSet;\nimport org.gradle.api.tasks.SourceSetContainer;\nimport org.gradle.api.tasks.SourceTask;\nimport org.gradle.api.tasks.TaskProvider;\nimport org.gradle.api.tasks.compile.JavaCompile;\nimport org.gradle.plugins.ide.idea.GenerateIdeaModule;\nimport org.gradle.plugins.ide.idea.IdeaPlugin;\nimport org.gradle.plugins.ide.idea.model.IdeaModule;\n\nimport static org.gradle.api.plugins.JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME;\n\npublic class AvroPlugin implements Plugin<Project> {\n    @Override\n    public void apply(final Project project) {\n        project.getPlugins().apply(JavaPlugin.class);\n        project.getPlugins().apply(AvroBasePlugin.class);\n        configureTasks(project);\n        configureIntelliJ(project);\n    }\n\n    private static void configureTasks(final Project project) {\n        getSourceSets(project).configureEach(sourceSet -> {\n            TaskProvider<GenerateAvroProtocolTask> protoTaskProvider = configureProtocolGenerationTask(project, sourceSet);\n            TaskProvider<GenerateAvroJavaTask> javaTaskProvider = configureJavaGenerationTask(project, sourceSet, protoTaskProvider);\n            configureTaskDependencies(project, sourceSet, javaTaskProvider);\n        });\n    }\n\n    private static void configureIntelliJ(final Project project) {\n        project.getPlugins().withType(IdeaPlugin.class).configureEach(ideaPlugin -> {\n            SourceSet mainSourceSet = getMainSourceSet(project);\n            SourceSet testSourceSet = getTestSourceSet(project);\n            IdeaModule module = ideaPlugin.getModel().getModule();\n            module.setSourceDirs(new SetBuilder<File>()\n                .addAll(module.getSourceDirs())\n                .add(getAvroSourceDir(project, mainSourceSet))\n                .add(getGeneratedOutputDir(project, mainSourceSet, Constants.JAVA_EXTENSION).map(Directory::getAsFile).get())\n                .build());\n            GradleCompatibility.addTestSources(module,\n                getAvroSourceDir(project, testSourceSet),\n                getGeneratedOutputDir(project, testSourceSet, Constants.JAVA_EXTENSION).map(Directory::getAsFile).get()\n            );\n            // IntelliJ doesn't allow source directories beneath an excluded directory.\n            // Thus, we remove the build directory exclude and add all non-generated sub-directories as excludes.\n            SetBuilder<File> excludeDirs = new SetBuilder<>();\n            excludeDirs.addAll(module.getExcludeDirs()).remove(project.getBuildDir());\n            File buildDir = project.getBuildDir();\n            if (buildDir.isDirectory()) {\n                excludeDirs.addAll(project.getBuildDir().listFiles(new NonGeneratedDirectoryFileFilter()));\n            }\n            module.setExcludeDirs(excludeDirs.build());\n        });\n        project.getTasks().withType(GenerateIdeaModule.class).configureEach(generateIdeaModule ->\n            generateIdeaModule.doFirst(task ->\n                project.getTasks().withType(GenerateAvroJavaTask.class, generateAvroJavaTask ->\n                    project.mkdir(generateAvroJavaTask.getOutputDir().get()))));\n    }\n\n    private static TaskProvider<GenerateAvroProtocolTask> configureProtocolGenerationTask(final Project project,\n                                                                                          final SourceSet sourceSet) {\n        String taskName = sourceSet.getTaskName(\"generate\", \"avroProtocol\");\n        return project.getTasks().register(taskName, GenerateAvroProtocolTask.class, task -> {\n            task.setDescription(\n                String.format(\"Generates %s Avro protocol definition files from IDL files.\", sourceSet.getName()));\n            task.setGroup(Constants.GROUP_SOURCE_GENERATION);\n            task.source(getAvroSourceDir(project, sourceSet));\n            task.include(\"**/*.\" + Constants.IDL_EXTENSION);\n            task.setClasspath(project.getConfigurations().getByName(RUNTIME_CLASSPATH_CONFIGURATION_NAME));\n            task.getOutputDir().convention(getGeneratedOutputDir(project, sourceSet, Constants.PROTOCOL_EXTENSION));\n        });\n    }\n\n    private static TaskProvider<GenerateAvroJavaTask> configureJavaGenerationTask(final Project project, final SourceSet sourceSet,\n                                                                    TaskProvider<GenerateAvroProtocolTask> protoTaskProvider) {\n        String taskName = sourceSet.getTaskName(\"generate\", \"avroJava\");\n        TaskProvider<GenerateAvroJavaTask> javaTaskProvider = project.getTasks().register(taskName, GenerateAvroJavaTask.class, task -> {\n            task.setDescription(String.format(\"Generates %s Avro Java source files from schema/protocol definition files.\",\n                sourceSet.getName()));\n            task.setGroup(Constants.GROUP_SOURCE_GENERATION);\n            task.source(getAvroSourceDir(project, sourceSet));\n            task.source(protoTaskProvider);\n            task.include(\"**/*.\" + Constants.SCHEMA_EXTENSION, \"**/*.\" + Constants.PROTOCOL_EXTENSION);\n            task.getOutputDir().convention(getGeneratedOutputDir(project, sourceSet, Constants.JAVA_EXTENSION));\n\n            sourceSet.getJava().srcDir(task.getOutputDir());\n\n            JavaCompile compileJavaTask = project.getTasks().named(sourceSet.getCompileJavaTaskName(), JavaCompile.class).get();\n            task.getOutputCharacterEncoding().convention(project.provider(() ->\n                Optional.ofNullable(compileJavaTask.getOptions().getEncoding()).orElse(Charset.defaultCharset().name())));\n        });\n        project.getTasks().named(sourceSet.getCompileJavaTaskName(), JavaCompile.class, compileJavaTask -> {\n            compileJavaTask.source(javaTaskProvider);\n        });\n        // When the Gradle's JVM plugin's withSourcesJar capability is used, it automatically includes all directories listed in the\n        // SourceSet's `allSource`.  However, in Gradle 7.1, they started including a warning that execution optimizations are disabled\n        // unless you explicitly declare what task produced the directory you're using.  Gradle doesn't currently have a way to declare a\n        // source directory and the task that creates it, so for now we need to manually declare the task dependency.\n        project.getTasks()\n            .matching(task -> GradleCompatibility.getSourcesJarTaskName(sourceSet).equals(task.getName()))\n            .configureEach(sourcesJarTask -> sourcesJarTask.dependsOn(javaTaskProvider));\n        return javaTaskProvider;\n    }\n\n    private static void configureTaskDependencies(final Project project, final SourceSet sourceSet,\n                                                  final TaskProvider<GenerateAvroJavaTask> javaTaskProvider) {\n        project.getPluginManager().withPlugin(\"org.jetbrains.kotlin.jvm\", appliedPlugin ->\n            project.getTasks()\n                .withType(SourceTask.class)\n                .matching(task -> sourceSet.getCompileTaskName(\"kotlin\").equals(task.getName()))\n                .configureEach(task ->\n                    task.source(javaTaskProvider.get().getOutputs())\n                )\n        );\n    }\n\n    private static File getAvroSourceDir(Project project, SourceSet sourceSet) {\n        return project.file(String.format(\"src/%s/avro\", sourceSet.getName()));\n    }\n\n    private static Provider<Directory> getGeneratedOutputDir(Project project, SourceSet sourceSet, String extension) {\n        String generatedOutputDirName = String.format(\"generated-%s-avro-%s\", sourceSet.getName(), extension);\n        return project.getLayout().getBuildDirectory().dir(generatedOutputDirName);\n    }\n\n    private static SourceSetContainer getSourceSets(Project project) {\n        return project.getExtensions().getByType(SourceSetContainer.class);\n    }\n\n    private static SourceSet getMainSourceSet(Project project) {\n        return getSourceSets(project).getByName(SourceSet.MAIN_SOURCE_SET_NAME);\n    }\n\n    private static SourceSet getTestSourceSet(Project project) {\n        return getSourceSets(project).getByName(SourceSet.TEST_SOURCE_SET_NAME);\n    }\n\n    private static class NonGeneratedDirectoryFileFilter implements FileFilter {\n        @Override\n        public boolean accept(File file) {\n            return file.isDirectory() && !file.getName().startsWith(\"generated-\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/AvroUtils.java",
    "content": "package com.github.davidmc24.gradle.plugin.avro;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.regex.Pattern;\nimport org.apache.avro.Protocol;\nimport org.apache.avro.Schema;\n\n/**\n * Utility method for working with Avro objects.\n */\nclass AvroUtils {\n    /**\n     * The namespace separator.\n     */\n    private static final String NAMESPACE_SEPARATOR = \".\";\n\n    /**\n     * The extension separator.\n     */\n    private static final String EXTENSION_SEPARATOR = \".\";\n\n    /**\n     * The Unix separator.\n     */\n    private static final String UNIX_SEPARATOR = \"/\";\n\n    /**\n     * Not intended for instantiation.\n     */\n    private AvroUtils() { }\n\n    /**\n     * Assembles a file path based on the namespace and name of the provided {@link Schema}.\n     *\n     * @param schema the schema for which to assemble a path\n     * @return a file path\n     */\n    static String assemblePath(Schema schema) {\n        return assemblePath(schema.getNamespace(), schema.getName(), Constants.SCHEMA_EXTENSION);\n    }\n\n    /**\n     * Assembles a file path based on the namespace and name of the provided {@link Protocol}.\n     *\n     * @param protocol the protocol for which to assemble a path\n     * @return a file path\n     */\n    static String assemblePath(Protocol protocol) {\n        return assemblePath(protocol.getNamespace(), protocol.getName(), Constants.PROTOCOL_EXTENSION);\n    }\n\n    /**\n     * Assembles a file path based on the provided arguments.\n     *\n     * @param namespace the namespace for the path; may be null\n     * @param name the name for the path; will result in an exception if null or empty\n     * @param extension the extension for the path\n     * @return the assembled path\n     */\n    private static String assemblePath(String namespace, String name, String extension) {\n        Strings.requireNotEmpty(name, \"Path cannot be assembled for nameless objects\");\n        List<String> parts = new ArrayList<>();\n        if (Strings.isNotEmpty(namespace)) {\n            parts.add(namespace.replaceAll(Pattern.quote(NAMESPACE_SEPARATOR), UNIX_SEPARATOR));\n        }\n        parts.add(name + EXTENSION_SEPARATOR + extension);\n        return String.join(UNIX_SEPARATOR, parts);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/Constants.java",
    "content": "/**\n * Copyright © 2013-2015 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport org.apache.avro.Conversion;\nimport org.apache.avro.LogicalTypes;\nimport org.apache.avro.compiler.specific.SpecificCompiler.FieldVisibility;\nimport org.apache.avro.generic.GenericData.StringType;\nimport org.gradle.api.reflect.TypeOf;\n\n/**\n * Various constants needed by the plugin.\n *\n * <p>The default values from {@code avro-compiler} aren't exposed in a way that's easily accessible, so even default\n * values that we want to match are still reproduced here.</p>\n */\nclass Constants {\n    static final String UTF8_ENCODING = \"UTF-8\";\n\n    static final String DEFAULT_STRING_TYPE = StringType.String.name();\n    static final String DEFAULT_FIELD_VISIBILITY = FieldVisibility.PRIVATE.name();\n    static final boolean DEFAULT_CREATE_SETTERS = true;\n    static final boolean DEFAULT_CREATE_OPTIONAL_GETTERS = false;\n    static final boolean DEFAULT_GETTERS_RETURN_OPTIONAL = false;\n    static final boolean DEFAULT_OPTIONAL_GETTERS_FOR_NULLABLE_FIELDS_ONLY = false;\n    static final boolean DEFAULT_ENABLE_DECIMAL_LOGICAL_TYPE = true;\n    static final Map<String, Class<? extends LogicalTypes.LogicalTypeFactory>> DEFAULT_LOGICAL_TYPE_FACTORIES = Collections.emptyMap();\n    static final Map<String, String> DEFAULT_LOGICAL_TYPE_FACTORY_CLASS_NAMES = Collections.emptyMap();\n    static final List<Class<? extends Conversion<?>>> DEFAULT_CUSTOM_CONVERSIONS = Collections.emptyList();\n    static final List<String> DEFAULT_CUSTOM_CONVERSION_CLASS_NAMES = Collections.emptyList();\n\n    static final String SCHEMA_EXTENSION = \"avsc\";\n    static final String PROTOCOL_EXTENSION = \"avpr\";\n    static final String IDL_EXTENSION = \"avdl\";\n    static final String JAVA_EXTENSION = \"java\";\n\n    static final String GROUP_SOURCE_GENERATION = \"Source Generation\";\n\n    static final String AVRO_EXTENSION_NAME = \"avro\";\n\n    static final String OPTION_FIELD_VISIBILITY = \"fieldVisibility\";\n    static final String OPTION_STRING_TYPE = \"stringType\";\n\n    static final TypeOf<Class<? extends LogicalTypes.LogicalTypeFactory>> LOGICAL_TYPE_FACTORY_TYPE =\n        new TypeOf<Class<? extends LogicalTypes.LogicalTypeFactory>>() { };\n\n    static final TypeOf<Class<? extends Conversion<?>>> CONVERSION_TYPE =\n        new TypeOf<Class<? extends Conversion<?>>>() { };\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/DefaultAvroExtension.java",
    "content": "/**\n * Copyright © 2013-2019 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.nio.charset.Charset;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport javax.inject.Inject;\nimport org.apache.avro.Conversion;\nimport org.apache.avro.LogicalTypes;\nimport org.apache.avro.compiler.specific.SpecificCompiler;\nimport org.apache.avro.generic.GenericData;\nimport org.gradle.api.Project;\nimport org.gradle.api.file.ConfigurableFileCollection;\nimport org.gradle.api.model.ObjectFactory;\nimport org.gradle.api.provider.ListProperty;\nimport org.gradle.api.provider.MapProperty;\nimport org.gradle.api.provider.Property;\nimport org.gradle.api.provider.Provider;\nimport org.gradle.api.tasks.Input;\nimport org.gradle.api.tasks.Optional;\n\n@SuppressWarnings({\"unused\", \"WeakerAccess\"})\npublic class DefaultAvroExtension implements AvroExtension {\n    private final Property<String> outputCharacterEncoding;\n    private final Property<String> stringType;\n    private final Property<String> fieldVisibility;\n    private final Property<String> templateDirectory;\n    private final ListProperty<String> additionalVelocityToolClasses;\n    private final Property<Boolean> createSetters;\n    private final Property<Boolean> createOptionalGetters;\n    private final Property<Boolean> gettersReturnOptional;\n    private final Property<Boolean> optionalGettersForNullableFieldsOnly;\n    private final Property<Boolean> enableDecimalLogicalType;\n    private final ConfigurableFileCollection conversionsAndTypeFactoriesClasspath;\n    private final MapProperty<String, Class<? extends LogicalTypes.LogicalTypeFactory>> logicalTypeFactories;\n    private final MapProperty<String, String> logicalTypeFactoryClassNames;\n    private final ListProperty<Class<? extends Conversion<?>>> customConversions;\n    private final ListProperty<String> customConversionClassNames;\n\n    @Inject\n    public DefaultAvroExtension(Project project, ObjectFactory objects) {\n        this.outputCharacterEncoding = objects.property(String.class);\n        this.stringType = objects.property(String.class).convention(Constants.DEFAULT_STRING_TYPE);\n        this.fieldVisibility = objects.property(String.class).convention(Constants.DEFAULT_FIELD_VISIBILITY);\n        this.templateDirectory = objects.property(String.class);\n        this.additionalVelocityToolClasses =\n                objects.listProperty(String.class).convention(Collections.emptyList());\n        this.createSetters = objects.property(Boolean.class).convention(Constants.DEFAULT_CREATE_SETTERS);\n        this.createOptionalGetters = objects.property(Boolean.class).convention(Constants.DEFAULT_CREATE_OPTIONAL_GETTERS);\n        this.gettersReturnOptional = objects.property(Boolean.class).convention(Constants.DEFAULT_GETTERS_RETURN_OPTIONAL);\n        this.optionalGettersForNullableFieldsOnly = objects.property(Boolean.class)\n            .convention(Constants.DEFAULT_OPTIONAL_GETTERS_FOR_NULLABLE_FIELDS_ONLY);\n        this.enableDecimalLogicalType = objects.property(Boolean.class).convention(Constants.DEFAULT_ENABLE_DECIMAL_LOGICAL_TYPE);\n        this.conversionsAndTypeFactoriesClasspath = GradleCompatibility.createConfigurableFileCollection(project);\n        this.logicalTypeFactories = objects.mapProperty(String.class, Constants.LOGICAL_TYPE_FACTORY_TYPE.getConcreteClass())\n            .convention(Constants.DEFAULT_LOGICAL_TYPE_FACTORIES);\n        this.logicalTypeFactoryClassNames = objects.mapProperty(String.class, String.class)\n            .convention(Constants.DEFAULT_LOGICAL_TYPE_FACTORY_CLASS_NAMES);\n        this.customConversions =\n            objects.listProperty(Constants.CONVERSION_TYPE.getConcreteClass()).convention(Constants.DEFAULT_CUSTOM_CONVERSIONS);\n        this.customConversionClassNames =\n            objects.listProperty(String.class).convention(Constants.DEFAULT_CUSTOM_CONVERSION_CLASS_NAMES);\n    }\n\n    @Override\n    public Property<String> getOutputCharacterEncoding() {\n        return outputCharacterEncoding;\n    }\n\n    public void setOutputCharacterEncoding(String outputCharacterEncoding) {\n        this.outputCharacterEncoding.set(outputCharacterEncoding);\n    }\n\n    public void setOutputCharacterEncoding(Charset outputCharacterEncoding) {\n        setOutputCharacterEncoding(outputCharacterEncoding.name());\n    }\n\n    @Override\n    public Property<String> getStringType() {\n        return stringType;\n    }\n\n    public void setStringType(String stringType) {\n        this.stringType.set(stringType);\n    }\n\n    public void setStringType(GenericData.StringType stringType) {\n        setStringType(stringType.name());\n    }\n\n    @Override\n    public Property<String> getFieldVisibility() {\n        return fieldVisibility;\n    }\n\n    public void setFieldVisibility(String fieldVisibility) {\n        this.fieldVisibility.set(fieldVisibility);\n    }\n\n    public void setFieldVisibility(SpecificCompiler.FieldVisibility fieldVisibility) {\n        setFieldVisibility(fieldVisibility.name());\n    }\n\n    @Override\n    public Property<String> getTemplateDirectory() {\n        return templateDirectory;\n    }\n\n    public void setTemplateDirectory(String templateDirectory) {\n        this.templateDirectory.set(templateDirectory);\n    }\n\n    @Optional\n    @Input\n    public ListProperty<String> getAdditionalVelocityToolClasses() {\n        return additionalVelocityToolClasses;\n    }\n\n    public void setAdditionalVelocityToolClasses(List<String> additionalVelocityToolClasses) {\n        this.additionalVelocityToolClasses.set(additionalVelocityToolClasses);\n    }\n\n    @Override\n    public Property<Boolean> isCreateSetters() {\n        return createSetters;\n    }\n\n    public void setCreateSetters(String createSetters) {\n        setCreateSetters(Boolean.parseBoolean(createSetters));\n    }\n\n    public void setCreateSetters(boolean createSetters) {\n        this.createSetters.set(createSetters);\n    }\n\n    @Override\n    public Property<Boolean> isCreateOptionalGetters() {\n        return createOptionalGetters;\n    }\n\n    public void setCreateOptionalGetters(String createOptionalGetters) {\n        setCreateOptionalGetters(Boolean.parseBoolean(createOptionalGetters));\n    }\n\n    public void setCreateOptionalGetters(boolean createOptionalGetters) {\n        this.createOptionalGetters.set(createOptionalGetters);\n    }\n\n    @Override\n    public Property<Boolean> isGettersReturnOptional() {\n        return gettersReturnOptional;\n    }\n\n    public void setGettersReturnOptional(String gettersReturnOptional) {\n        setGettersReturnOptional(Boolean.parseBoolean(gettersReturnOptional));\n    }\n\n    public void setGettersReturnOptional(boolean gettersReturnOptional) {\n        this.gettersReturnOptional.set(gettersReturnOptional);\n    }\n\n    @Override\n    public Property<Boolean> isOptionalGettersForNullableFieldsOnly() {\n        return optionalGettersForNullableFieldsOnly;\n    }\n\n    public void setOptionalGettersForNullableFieldsOnly(String optionalGettersForNullableFieldsOnly) {\n        setOptionalGettersForNullableFieldsOnly(Boolean.parseBoolean(optionalGettersForNullableFieldsOnly));\n    }\n\n    public void setOptionalGettersForNullableFieldsOnly(boolean optionalGettersForNullableFieldsOnly) {\n        this.optionalGettersForNullableFieldsOnly.set(optionalGettersForNullableFieldsOnly);\n    }\n\n    @Override\n    public Property<Boolean> isEnableDecimalLogicalType() {\n        return enableDecimalLogicalType;\n    }\n\n    public void setEnableDecimalLogicalType(String enableDecimalLogicalType) {\n        setEnableDecimalLogicalType(Boolean.parseBoolean(enableDecimalLogicalType));\n    }\n\n    public void setEnableDecimalLogicalType(boolean enableDecimalLogicalType) {\n        this.enableDecimalLogicalType.set(enableDecimalLogicalType);\n    }\n\n    @Override\n    public ConfigurableFileCollection getConversionsAndTypeFactoriesClasspath() {\n        return conversionsAndTypeFactoriesClasspath;\n    }\n\n    /**\n     * @deprecated use {@link #getLogicalTypeFactoryClassNames()} instead\n     */\n    @Deprecated\n    @Override\n    public MapProperty<String, Class<? extends LogicalTypes.LogicalTypeFactory>> getLogicalTypeFactories() {\n        return logicalTypeFactories;\n    }\n\n    /**\n     * @deprecated use {@link #setLogicalTypeFactoryClassNames(Provider)} ()} instead\n     */\n    @Deprecated\n    public void setLogicalTypeFactories(Provider<? extends Map<? extends String,\n        ? extends Class<? extends LogicalTypes.LogicalTypeFactory>>> provider) {\n        this.logicalTypeFactories.set(provider);\n    }\n\n    /**\n     * @deprecated use {@link #setLogicalTypeFactoryClassNames(Map)} ()} instead\n     */\n    @Deprecated\n    public void setLogicalTypeFactories(Map<? extends String,\n        ? extends Class<? extends LogicalTypes.LogicalTypeFactory>> logicalTypeFactories) {\n        this.logicalTypeFactories.set(logicalTypeFactories);\n    }\n\n    @Override\n    public MapProperty<String, String> getLogicalTypeFactoryClassNames() {\n        return logicalTypeFactoryClassNames;\n    }\n\n    public void setLogicalTypeFactoryClassNames(Provider<? extends Map<? extends String,\n        ? extends String>> provider) {\n        this.logicalTypeFactoryClassNames.set(provider);\n    }\n\n    public void setLogicalTypeFactoryClassNames(Map<? extends String,\n        ? extends String> logicalTypeFactoryClassNames) {\n        this.logicalTypeFactoryClassNames.set(logicalTypeFactoryClassNames);\n    }\n\n    /**\n     * @deprecated use {@link #getCustomConversionClassNames()} instead\n     */\n    @Deprecated\n    @Override\n    public ListProperty<Class<? extends Conversion<?>>> getCustomConversions() {\n        return customConversions;\n    }\n\n    /**\n     * @deprecated use {@link #setCustomConversionClassNames(Provider)} ()} instead\n     */\n    @Deprecated\n    public void setCustomConversions(Provider<Iterable<Class<? extends Conversion<?>>>> provider) {\n        this.customConversions.set(provider);\n    }\n\n    /**\n     * @deprecated use {@link #setCustomConversionClassNames(Iterable)} instead\n     */\n    @Deprecated\n    public void setCustomConversions(Iterable<Class<? extends Conversion<?>>> customConversions) {\n        this.customConversions.set(customConversions);\n    }\n\n    public ListProperty<String> getCustomConversionClassNames() {\n        return customConversionClassNames;\n    }\n\n    public void setCustomConversionClassNames(Provider<Iterable<String>> provider) {\n        this.customConversionClassNames.set(provider);\n    }\n\n    public void setCustomConversionClassNames(Iterable<String> customConversionClassNames) {\n        this.customConversionClassNames.set(customConversionClassNames);\n    }\n\n    /**\n     * @deprecated use {@link #logicalTypeFactory(String, String)} ()} instead\n     */\n    @Deprecated\n    @Override\n    public AvroExtension logicalTypeFactory(String typeName, Class<? extends LogicalTypes.LogicalTypeFactory> typeFactoryClass) {\n        logicalTypeFactories.put(typeName, typeFactoryClass);\n        return this;\n    }\n\n    @Override\n    public AvroExtension logicalTypeFactory(String typeName, String typeFactoryClassName) {\n        logicalTypeFactoryClassNames.put(typeName, typeFactoryClassName);\n        return this;\n    }\n\n    /**\n     * @deprecated use {@link #customConversion(String)} ()} instead\n     */\n    @Deprecated\n    @Override\n    public AvroExtension customConversion(Class<? extends Conversion<?>> conversionClass) {\n        customConversions.add(conversionClass);\n        return this;\n    }\n\n    @Override\n    public AvroExtension customConversion(String conversionClassName) {\n        customConversionClassNames.add(conversionClassName);\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/Enums.java",
    "content": "/**\n * Copyright © 2015 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.util.Arrays;\n\nclass Enums {\n    static <T extends Enum<T>> T parseCaseInsensitive(String label, T[] values, String input) {\n        for (T value : values) {\n            if (value.name().equalsIgnoreCase(input)) {\n                return value;\n            }\n        }\n        throw new IllegalArgumentException(String.format(\"Invalid %s '%s'.  Value values are: %s\",\n                label, input, Arrays.asList(values)));\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/FileExtensionSpec.java",
    "content": "/**\n * Copyright © 2013-2015 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Set;\nimport org.gradle.api.specs.Spec;\n\nclass FileExtensionSpec implements Spec<File> {\n    private final Set<String> extensions;\n\n    FileExtensionSpec(String... extensions) {\n        this.extensions = new HashSet<>(Arrays.asList(extensions));\n    }\n\n    FileExtensionSpec(Collection<String> extensions) {\n        this.extensions = new HashSet<>(extensions);\n    }\n\n    @Override\n    public boolean isSatisfiedBy(File file) {\n        return extensions.contains(FilenameUtils.getExtension(file.getName()));\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/FileState.java",
    "content": "/**\n * Copyright © 2015 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.io.File;\nimport java.util.Set;\nimport java.util.TreeSet;\n\nclass FileState implements Comparable<FileState> {\n    private final File file;\n    private final String path;\n    private String errorMessage;\n    private Set<String> duplicateTypeNames = new TreeSet<>();\n\n    FileState(File file, String path) {\n        this.file = file;\n        this.path = path;\n    }\n\n    File getFile() {\n        return file;\n    }\n\n    Set<String> getDuplicateTypeNames() {\n        return duplicateTypeNames;\n    }\n\n    void clearError() {\n        errorMessage = null;\n    }\n\n    void setError(Throwable ex) {\n        this.errorMessage = ex.getMessage();\n    }\n\n    void addDuplicateTypeName(String typeName) {\n        duplicateTypeNames.add(typeName);\n    }\n\n    public boolean containsDuplicateTypeName(String typeName) {\n        return duplicateTypeNames.contains(typeName);\n    }\n\n    public String getPath() {\n        return path;\n    }\n\n    public String getErrorMessage() {\n        return errorMessage;\n    }\n\n    @Override\n    public int compareTo(FileState o) {\n        return path.compareTo(o.getPath());\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n        FileState fileState = (FileState) o;\n        return path.equals(fileState.path);\n    }\n\n    @Override\n    public int hashCode() {\n        return path.hashCode();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/FileUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * 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, 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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.file.Path;\nimport org.gradle.api.file.ProjectLayout;\n\n/**\n * General file manipulation utilities.\n *\n * <p>This copy from Apache Commons IO has been pruned down to just what is needed for gradle-avro-plugin.</p>\n *\n * <p>\n * Facilities are provided in the following areas:\n * <ul>\n * <li>writing to a file\n * <li>reading from a file\n * <li>make a directory including parent directories\n * <li>copying files and directories\n * <li>deleting files and directories\n * <li>converting to and from a URL\n * <li>listing files and directories by filter and extension\n * <li>comparing file content\n * <li>file last changed date\n * <li>calculating a checksum\n * </ul>\n * <p>\n * Origin of code: Excalibur, Alexandria, Commons-Utils\n *\n * @author <a href=\"mailto:burton@relativity.yi.org\">Kevin A. Burton</A>\n * @author <a href=\"mailto:sanders@apache.org\">Scott Sanders</a>\n * @author <a href=\"mailto:dlr@finemaltcoding.com\">Daniel Rall</a>\n * @author <a href=\"mailto:Christoph.Reck@dlr.de\">Christoph.Reck</a>\n * @author <a href=\"mailto:peter@apache.org\">Peter Donald</a>\n * @author <a href=\"mailto:jefft@apache.org\">Jeff Turner</a>\n * @author Matthew Hawthorne\n * @author <a href=\"mailto:jeremias@apache.org\">Jeremias Maerki</a>\n * @author Stephen Colebourne\n * @author Ian Springer\n * @author Chris Eldredge\n * @author Jim Harrington\n * @author Niall Pemberton\n * @author Sandy McArthur\n * @version $Id: FileUtils.java 507684 2007-02-14 20:38:25Z bayard $\n */\nclass FileUtils {\n    /**\n     * Opens a {@link FileOutputStream} for the specified file, checking and\n     * creating the parent directory if it does not exist.\n     * <p>\n     * At the end of the method either the stream will be successfully opened,\n     * or an exception will have been thrown.\n     * <p>\n     * The parent directory will be created if it does not exist.\n     * The file will be created if it does not exist.\n     * An exception is thrown if the file object exists but is a directory.\n     * An exception is thrown if the file exists but cannot be written to.\n     * An exception is thrown if the parent directory cannot be created.\n     *\n     * @param file  the file to open for output, must not be <code>null</code>\n     * @return a new {@link FileOutputStream} for the specified file\n     * @throws IOException if the file object is a directory\n     * @throws IOException if the file cannot be written to\n     * @throws IOException if a parent directory needs creating but that fails\n     * @since Commons IO 1.3\n     */\n    private static FileOutputStream openOutputStream(File file) throws IOException {\n        if (file.exists()) {\n            if (file.isDirectory()) {\n                throw new IOException(\"File '\" + file + \"' exists but is a directory\");\n            }\n            if (!file.canWrite()) {\n                throw new IOException(\"File '\" + file + \"' cannot be written to\");\n            }\n        } else {\n            File parent = file.getParentFile();\n            if (parent != null && !parent.exists()) {\n                if (!parent.mkdirs()) {\n                    throw new IOException(\"File '\" + file + \"' could not be created\");\n                }\n            }\n        }\n        return new FileOutputStream(file);\n    }\n\n    /**\n     * Writes a String to a file creating the file if it does not exist.\n     *\n     * NOTE: As from v1.3, the parent directories of the file will be created\n     * if they do not exist.\n     *\n     * @param file  the file to write\n     * @param data  the content to write to the file\n     * @param encoding  the encoding to use, <code>null</code> means platform default\n     * @throws IOException in case of an I/O error\n     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM\n     */\n    @SuppressWarnings(\"SameParameterValue\")\n    private static void writeStringToFile(File file, String data, String encoding) throws IOException {\n        if (encoding == null) {\n            throw new IllegalArgumentException(\"Must specify encoding\");\n        }\n        try (OutputStream out = openOutputStream(file)) {\n            if (data != null) {\n                out.write(data.getBytes(encoding));\n            }\n        }\n    }\n\n    /**\n     * Writes a file in a manner appropriate for a JSON file.  UTF-8 will be used, as it is the default encoding for JSON, and should be\n     * maximally interoperable.\n     *\n     * @see <a href=\"https://tools.ietf.org/html/rfc7159#section-8.1\">JSON Character Encoding</a>\n     */\n    static void writeJsonFile(File file, String data) throws IOException {\n        writeStringToFile(file, data, Constants.UTF8_ENCODING);\n    }\n\n    /**\n     * Acts as a replacement for {@link org.gradle.api.Project#relativePath(Object)}, as Configuration Cache support doesn't allow\n     * maintaining references to the {@link org.gradle.api.Project}.\n     */\n    static String projectRelativePath(ProjectLayout projectLayout, File file) {\n        Path path = file.toPath();\n        if (path.isAbsolute()) {\n            Path projectDirectoryPath = projectLayout.getProjectDirectory().getAsFile().toPath();\n            path = projectDirectoryPath.relativize(path);\n        } else {\n            path = file.toPath();\n        }\n        return path.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/FilenameUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * 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, 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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\n/**\n * General filename and filepath manipulation utilities.\n *\n * <p>This copy from Apache Commons IO has been pruned down to just what is needed for gradle-avro-plugin.</p>\n *\n * <p>\n * When dealing with filenames you can hit problems when moving from a Windows\n * based development machine to a Unix based production machine.\n * This class aims to help avoid those problems.\n * <p>\n * <b>NOTE</b>: You may be able to avoid using this class entirely simply by\n * using JDK {@link java.io.File File} objects and the two argument constructor\n * {@link java.io.File#File(java.io.File, java.lang.String) File(File,String)}.\n * <p>\n * Most methods on this class are designed to work the same on both Unix and Windows.\n * Those that don't include 'System', 'Unix' or 'Windows' in their name.\n * <p>\n * Most methods recognise both separators (forward and back), and both\n * sets of prefixes. See the javadoc of each method for details.\n * <p>\n * This class defines six components within a filename\n * (example C:\\dev\\project\\file.txt):\n * <ul>\n * <li>the prefix - C:\\</li>\n * <li>the path - dev\\project\\</li>\n * <li>the full path - C:\\dev\\project\\</li>\n * <li>the name - file.txt</li>\n * <li>the base name - file</li>\n * <li>the extension - txt</li>\n * </ul>\n * Note that this class works best if directory filenames end with a separator.\n * If you omit the last separator, it is impossible to determine if the filename\n * corresponds to a file or a directory. As a result, we have chosen to say\n * it corresponds to a file.\n * <p>\n * This class only supports Unix and Windows style names.\n * Prefixes are matched as follows:\n * <pre>\n * Windows:\n * a\\b\\c.txt           --> \"\"          --> relative\n * \\a\\b\\c.txt          --> \"\\\"         --> current drive absolute\n * C:a\\b\\c.txt         --> \"C:\"        --> drive relative\n * C:\\a\\b\\c.txt        --> \"C:\\\"       --> absolute\n * \\\\server\\a\\b\\c.txt  --> \"\\\\server\\\" --> UNC\n *\n * Unix:\n * a/b/c.txt           --> \"\"          --> relative\n * /a/b/c.txt          --> \"/\"         --> absolute\n * ~/a/b/c.txt         --> \"~/\"        --> current user\n * ~                   --> \"~/\"        --> current user (slash added)\n * ~user/a/b/c.txt     --> \"~user/\"    --> named user\n * ~user               --> \"~user/\"    --> named user (slash added)\n * </pre>\n * Both prefix styles are matched always, irrespective of the machine that you are\n * currently running on.\n * <p>\n * Origin of code: Excalibur, Alexandria, Tomcat, Commons-Utils.\n *\n * @author <a href=\"mailto:burton@relativity.yi.org\">Kevin A. Burton</A>\n * @author <a href=\"mailto:sanders@apache.org\">Scott Sanders</a>\n * @author <a href=\"mailto:dlr@finemaltcoding.com\">Daniel Rall</a>\n * @author <a href=\"mailto:Christoph.Reck@dlr.de\">Christoph.Reck</a>\n * @author <a href=\"mailto:peter@apache.org\">Peter Donald</a>\n * @author <a href=\"mailto:jefft@apache.org\">Jeff Turner</a>\n * @author Matthew Hawthorne\n * @author Martin Cooper\n * @author <a href=\"mailto:jeremias@apache.org\">Jeremias Maerki</a>\n * @author Stephen Colebourne\n * @version $Id: FilenameUtils.java 490424 2006-12-27 01:20:43Z bayard $\n * @since Commons IO 1.1\n */\nclass FilenameUtils {\n    /**\n     * The extension separator character.\n     */\n    private static final char EXTENSION_SEPARATOR = '.';\n\n    /**\n     * The Unix separator character.\n     */\n    private static final char UNIX_SEPARATOR = '/';\n\n    /**\n     * The Windows separator character.\n     */\n    private static final char WINDOWS_SEPARATOR = '\\\\';\n\n    /**\n     * Returns the index of the last directory separator character.\n     * <p>\n     * This method will handle a file in either Unix or Windows format.\n     * The position of the last forward or backslash is returned.\n     * <p>\n     * The output will be the same irrespective of the machine that the code is running on.\n     *\n     * @param filename  the filename to find the last path separator in, null returns -1\n     * @return the index of the last separator character, or -1 if there\n     * is no such character\n     */\n    private static int indexOfLastSeparator(String filename) {\n        if (filename == null) {\n            return -1;\n        }\n        int lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR);\n        int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR);\n        return Math.max(lastUnixPos, lastWindowsPos);\n    }\n\n    /**\n     * Returns the index of the last extension separator character, which is a dot.\n     * <p>\n     * This method also checks that there is no directory separator after the last dot.\n     * To do this it uses {@link #indexOfLastSeparator(String)} which will\n     * handle a file in either Unix or Windows format.\n     * <p>\n     * The output will be the same irrespective of the machine that the code is running on.\n     *\n     * @param filename  the filename to find the last path separator in, null returns -1\n     * @return the index of the last separator character, or -1 if there\n     * is no such character\n     */\n    private static int indexOfExtension(String filename) {\n        if (filename == null) {\n            return -1;\n        }\n        int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR);\n        int lastSeparator = indexOfLastSeparator(filename);\n        return lastSeparator > extensionPos ? -1 : extensionPos;\n    }\n\n    /**\n     * Gets the name minus the path from a full filename.\n     * <p>\n     * This method will handle a file in either Unix or Windows format.\n     * The text after the last forward or backslash is returned.\n     * <pre>\n     * a/b/c.txt --> c.txt\n     * a.txt     --> a.txt\n     * a/b/c     --> c\n     * a/b/c/    --> \"\"\n     * </pre>\n     * <p>\n     * The output will be the same irrespective of the machine that the code is running on.\n     *\n     * @param filename  the filename to query, null returns null\n     * @return the name of the file without the path, or an empty string if none exists\n     */\n    private static String getName(String filename) {\n        if (filename == null) {\n            return null;\n        }\n        int index = indexOfLastSeparator(filename);\n        return filename.substring(index + 1);\n    }\n\n    /**\n     * Gets the base name, minus the full path and extension, from a full filename.\n     * <p>\n     * This method will handle a file in either Unix or Windows format.\n     * The text after the last forward or backslash and before the last dot is returned.\n     * <pre>\n     * a/b/c.txt --> c\n     * a.txt     --> a\n     * a/b/c     --> c\n     * a/b/c/    --> \"\"\n     * </pre>\n     * <p>\n     * The output will be the same irrespective of the machine that the code is running on.\n     *\n     * @param filename  the filename to query, null returns null\n     * @return the name of the file without the path, or an empty string if none exists\n     */\n    static String getBaseName(String filename) {\n        return removeExtension(getName(filename));\n    }\n\n    /**\n     * Gets the extension of a filename.\n     * <p>\n     * This method returns the textual part of the filename after the last dot.\n     * There must be no directory separator after the dot.\n     * <pre>\n     * foo.txt      --> \"txt\"\n     * a/b/c.jpg    --> \"jpg\"\n     * a/b.txt/c    --> \"\"\n     * a/b/c        --> \"\"\n     * </pre>\n     * <p>\n     * The output will be the same irrespective of the machine that the code is running on.\n     *\n     * @param filename the filename to retrieve the extension of.\n     * @return the extension of the file or an empty string if none exists.\n     */\n    static String getExtension(String filename) {\n        if (filename == null) {\n            return null;\n        }\n        int index = indexOfExtension(filename);\n        if (index == -1) {\n            return \"\";\n        } else {\n            return filename.substring(index + 1);\n        }\n    }\n\n    //-----------------------------------------------------------------------\n    /**\n     * Removes the extension from a filename.\n     * <p>\n     * This method returns the textual part of the filename before the last dot.\n     * There must be no directory separator after the dot.\n     * <pre>\n     * foo.txt    --> foo\n     * a\\b\\c.jpg  --> a\\b\\c\n     * a\\b\\c      --> a\\b\\c\n     * a.b\\c      --> a.b\\c\n     * </pre>\n     * <p>\n     * The output will be the same irrespective of the machine that the code is running on.\n     *\n     * @param filename  the filename to query, null returns null\n     * @return the filename minus the extension\n     */\n    private static String removeExtension(String filename) {\n        if (filename == null) {\n            return null;\n        }\n        int index = indexOfExtension(filename);\n        if (index == -1) {\n            return filename;\n        } else {\n            return filename.substring(0, index);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/GenerateAvroJavaTask.java",
    "content": "/**\n * Copyright © 2013-2019 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.InvocationTargetException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.nio.charset.Charset;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport javax.inject.Inject;\nimport org.apache.avro.Conversion;\nimport org.apache.avro.LogicalTypes;\nimport org.apache.avro.LogicalTypes.LogicalTypeFactory;\nimport org.apache.avro.Protocol;\nimport org.apache.avro.Schema;\nimport org.apache.avro.compiler.specific.SpecificCompiler;\nimport org.apache.avro.compiler.specific.SpecificCompiler.FieldVisibility;\nimport org.apache.avro.generic.GenericData;\nimport org.apache.avro.generic.GenericData.StringType;\nimport org.gradle.api.GradleException;\nimport org.gradle.api.file.ConfigurableFileCollection;\nimport org.gradle.api.file.FileCollection;\nimport org.gradle.api.file.ProjectLayout;\nimport org.gradle.api.model.ObjectFactory;\nimport org.gradle.api.provider.ListProperty;\nimport org.gradle.api.provider.MapProperty;\nimport org.gradle.api.provider.Property;\nimport org.gradle.api.provider.Provider;\nimport org.gradle.api.specs.NotSpec;\nimport org.gradle.api.tasks.CacheableTask;\nimport org.gradle.api.tasks.Classpath;\nimport org.gradle.api.tasks.Input;\nimport org.gradle.api.tasks.Optional;\nimport org.gradle.api.tasks.TaskAction;\n\n/**\n * Task to generate Java source files based on Avro protocol files and Avro schema files using {@link Protocol} and\n * {@link SpecificCompiler}.\n */\n@SuppressWarnings(\"WeakerAccess\")\n@CacheableTask\npublic class GenerateAvroJavaTask extends OutputDirTask {\n    private static Set<String> SUPPORTED_EXTENSIONS =\n        new SetBuilder<String>().add(Constants.PROTOCOL_EXTENSION).add(Constants.SCHEMA_EXTENSION).build();\n\n    private final Property<String> outputCharacterEncoding;\n    private final Property<String> stringType;\n    private final Property<String> fieldVisibility;\n    private final Property<String> templateDirectory;\n    private final ListProperty<String> additionalVelocityToolClasses;\n    private final Property<Boolean> createOptionalGetters;\n    private final Property<Boolean> gettersReturnOptional;\n    private final Property<Boolean> optionalGettersForNullableFieldsOnly;\n    private final Property<Boolean> createSetters;\n    private final Property<Boolean> enableDecimalLogicalType;\n    private FileCollection classpath;\n    private final ConfigurableFileCollection conversionsAndTypeFactoriesClasspath;\n    private final MapProperty<String, Class<? extends LogicalTypes.LogicalTypeFactory>> logicalTypeFactories;\n    private final MapProperty<String, String> logicalTypeFactoryClassNames;\n    private final ListProperty<Class<? extends Conversion<?>>> customConversions;\n    private final ListProperty<String> customConversionClassNames;\n\n    private final Provider<StringType> stringTypeProvider;\n    private final Provider<FieldVisibility> fieldVisibilityProvider;\n\n    private final ProjectLayout projectLayout;\n    private final SchemaResolver resolver;\n\n    @Inject\n    public GenerateAvroJavaTask(ObjectFactory objects) {\n        super();\n        this.outputCharacterEncoding = objects.property(String.class);\n        this.stringType = objects.property(String.class).convention(Constants.DEFAULT_STRING_TYPE);\n        this.fieldVisibility = objects.property(String.class).convention(Constants.DEFAULT_FIELD_VISIBILITY);\n        this.templateDirectory = objects.property(String.class);\n        this.additionalVelocityToolClasses =\n                objects.listProperty(String.class).convention(Collections.emptyList());\n        this.createOptionalGetters = objects.property(Boolean.class).convention(Constants.DEFAULT_CREATE_OPTIONAL_GETTERS);\n        this.gettersReturnOptional = objects.property(Boolean.class).convention(Constants.DEFAULT_GETTERS_RETURN_OPTIONAL);\n        this.optionalGettersForNullableFieldsOnly = objects.property(Boolean.class)\n            .convention(Constants.DEFAULT_OPTIONAL_GETTERS_FOR_NULLABLE_FIELDS_ONLY);\n        this.createSetters = objects.property(Boolean.class).convention(Constants.DEFAULT_CREATE_SETTERS);\n        this.enableDecimalLogicalType = objects.property(Boolean.class).convention(Constants.DEFAULT_ENABLE_DECIMAL_LOGICAL_TYPE);\n        this.classpath = GradleCompatibility.createConfigurableFileCollection(getProject());\n        this.conversionsAndTypeFactoriesClasspath = GradleCompatibility.createConfigurableFileCollection(getProject());\n        this.logicalTypeFactories = objects.mapProperty(String.class, Constants.LOGICAL_TYPE_FACTORY_TYPE.getConcreteClass())\n            .convention(Constants.DEFAULT_LOGICAL_TYPE_FACTORIES);\n        this.logicalTypeFactoryClassNames = objects.mapProperty(String.class, String.class)\n            .convention(Constants.DEFAULT_LOGICAL_TYPE_FACTORY_CLASS_NAMES);\n        this.customConversions =\n            objects.listProperty(Constants.CONVERSION_TYPE.getConcreteClass()).convention(Constants.DEFAULT_CUSTOM_CONVERSIONS);\n        this.customConversionClassNames =\n            objects.listProperty(String.class).convention(Constants.DEFAULT_CUSTOM_CONVERSION_CLASS_NAMES);\n        this.stringTypeProvider = getStringType()\n            .map(input -> Enums.parseCaseInsensitive(Constants.OPTION_STRING_TYPE, StringType.values(), input));\n        this.fieldVisibilityProvider = getFieldVisibility()\n            .map(input -> Enums.parseCaseInsensitive(Constants.OPTION_FIELD_VISIBILITY, FieldVisibility.values(), input));\n        this.projectLayout = getProject().getLayout();\n        this.resolver = new SchemaResolver(projectLayout, getLogger());\n    }\n\n    public void setClasspath(FileCollection classpath) {\n        this.classpath = classpath;\n    }\n\n    public void classpath(Object... paths) {\n        this.classpath.plus(getProject().files(paths));\n    }\n\n    @Classpath\n    public FileCollection getClasspath() {\n        return this.classpath;\n    }\n\n    @Optional\n    @Input\n    public Property<String> getOutputCharacterEncoding() {\n        return outputCharacterEncoding;\n    }\n\n    public void setOutputCharacterEncoding(String outputCharacterEncoding) {\n        this.outputCharacterEncoding.set(outputCharacterEncoding);\n    }\n\n    public void setOutputCharacterEncoding(Charset outputCharacterEncoding) {\n        setOutputCharacterEncoding(outputCharacterEncoding.name());\n    }\n\n    @Input\n    public Property<String> getStringType() {\n        return stringType;\n    }\n\n    public void setStringType(GenericData.StringType stringType) {\n        setStringType(stringType.name());\n    }\n\n    public void setStringType(String stringType) {\n        this.stringType.set(stringType);\n    }\n\n    @Input\n    public Property<String> getFieldVisibility() {\n        return fieldVisibility;\n    }\n\n    public void setFieldVisibility(String fieldVisibility) {\n        this.fieldVisibility.set(fieldVisibility);\n    }\n\n    public void setFieldVisibility(SpecificCompiler.FieldVisibility fieldVisibility) {\n        setFieldVisibility(fieldVisibility.name());\n    }\n\n    @Optional\n    @Input\n    public Property<String> getTemplateDirectory() {\n        return templateDirectory;\n    }\n\n    public void setTemplateDirectory(String templateDirectory) {\n        this.templateDirectory.set(templateDirectory);\n    }\n\n    @Optional\n    @Input\n    public ListProperty<String> getAdditionalVelocityToolClasses() {\n        return additionalVelocityToolClasses;\n    }\n\n    public void setAdditionalVelocityToolClasses(List<String> additionalVelocityToolClasses) {\n        this.additionalVelocityToolClasses.set(additionalVelocityToolClasses);\n    }\n\n    public Property<Boolean> isCreateSetters() {\n        return createSetters;\n    }\n\n    @Input\n    public Property<Boolean> getCreateSetters() {\n        return createSetters;\n    }\n\n    public void setCreateSetters(String createSetters) {\n        this.createSetters.set(Boolean.parseBoolean(createSetters));\n    }\n\n    public Property<Boolean> isCreateOptionalGetters() {\n        return createOptionalGetters;\n    }\n\n    @Input\n    public Property<Boolean> getCreateOptionalGetters() {\n        return createOptionalGetters;\n    }\n\n    public void setCreateOptionalGetters(String createOptionalGetters) {\n        this.createOptionalGetters.set(Boolean.parseBoolean(createOptionalGetters));\n    }\n\n    public Property<Boolean> isGettersReturnOptional() {\n        return gettersReturnOptional;\n    }\n\n    @Input\n    public Property<Boolean> getGettersReturnOptional() {\n        return gettersReturnOptional;\n    }\n\n    public void setGettersReturnOptional(String gettersReturnOptional) {\n        this.gettersReturnOptional.set(Boolean.parseBoolean(gettersReturnOptional));\n    }\n\n    public Property<Boolean> isOptionalGettersForNullableFieldsOnly() {\n        return optionalGettersForNullableFieldsOnly;\n    }\n\n    @Input\n    public Property<Boolean> getOptionalGettersForNullableFieldsOnly() {\n        return optionalGettersForNullableFieldsOnly;\n    }\n\n    public void setOptionalGettersForNullableFieldsOnly(String optionalGettersForNullableFieldsOnly) {\n        this.optionalGettersForNullableFieldsOnly.set(Boolean.parseBoolean(optionalGettersForNullableFieldsOnly));\n    }\n\n    public Property<Boolean> isEnableDecimalLogicalType() {\n        return enableDecimalLogicalType;\n    }\n\n    @Input\n    public Property<Boolean> getEnableDecimalLogicalType() {\n        return enableDecimalLogicalType;\n    }\n\n    public void setEnableDecimalLogicalType(String enableDecimalLogicalType) {\n        this.enableDecimalLogicalType.set(Boolean.parseBoolean(enableDecimalLogicalType));\n    }\n\n    @Optional\n    @Classpath\n    public ConfigurableFileCollection getConversionsAndTypeFactoriesClasspath() {\n        return conversionsAndTypeFactoriesClasspath;\n    }\n\n    /**\n     * @deprecated use {@link #getLogicalTypeFactoryClassNames()} ()} instead\n     */\n    @Deprecated\n    @Optional\n    @Input\n    public MapProperty<String, Class<? extends LogicalTypes.LogicalTypeFactory>> getLogicalTypeFactories() {\n        return logicalTypeFactories;\n    }\n\n    /**\n     * @deprecated use {@link #setLogicalTypeFactoryClassNames(Provider)} ()} instead\n     */\n    @Deprecated\n    public void setLogicalTypeFactories(Provider<? extends Map<? extends String,\n        ? extends Class<? extends LogicalTypes.LogicalTypeFactory>>> provider) {\n        this.logicalTypeFactories.set(provider);\n    }\n\n    /**\n     * @deprecated use {@link #setLogicalTypeFactoryClassNames(Map)} ()} instead\n     */\n    @Deprecated\n    public void setLogicalTypeFactories(Map<? extends String,\n        ? extends Class<? extends LogicalTypes.LogicalTypeFactory>> logicalTypeFactories) {\n        this.logicalTypeFactories.set(logicalTypeFactories);\n    }\n\n    @Input\n    @Optional\n    public MapProperty<String, String> getLogicalTypeFactoryClassNames() {\n        return logicalTypeFactoryClassNames;\n    }\n\n    public void setLogicalTypeFactoryClassNames(Provider<? extends Map<? extends String,\n        ? extends String>> provider) {\n        this.logicalTypeFactoryClassNames.set(provider);\n    }\n\n    public void setLogicalTypeFactoryClassNames(Map<? extends String,\n        ? extends String> logicalTypeFactoryClassNames) {\n        this.logicalTypeFactoryClassNames.set(logicalTypeFactoryClassNames);\n    }\n\n    /**\n     * @deprecated use {@link #getCustomConversions()} ()} instead\n     */\n    @Deprecated\n    @Optional\n    @Input\n    public ListProperty<Class<? extends Conversion<?>>> getCustomConversions() {\n        return customConversions;\n    }\n\n    /**\n     * @deprecated use {@link #setCustomConversionClassNames(Provider)} ()} instead\n     */\n    @Deprecated\n    public void setCustomConversions(Provider<Iterable<Class<? extends Conversion<?>>>> provider) {\n        this.customConversions.set(provider);\n    }\n\n    /**\n     * @deprecated use {@link #setCustomConversionClassNames(Iterable)} ()} instead\n     */\n    @Deprecated\n    public void setCustomConversions(Iterable<Class<? extends Conversion<?>>> customConversions) {\n        this.customConversions.set(customConversions);\n    }\n\n    @Optional\n    @Input\n    public ListProperty<String> getCustomConversionClassNames() {\n        return customConversionClassNames;\n    }\n\n    public void setCustomConversionClassNames(Provider<Iterable<String>> provider) {\n        this.customConversionClassNames.set(provider);\n    }\n\n    public void setCustomConversionClassNames(Iterable<String> customConversionClassNames) {\n        this.customConversionClassNames.set(customConversionClassNames);\n    }\n\n    @TaskAction\n    protected void process() {\n        getLogger().debug(\"Using outputCharacterEncoding {}\", getOutputCharacterEncoding().getOrNull());\n        getLogger().debug(\"Using stringType {}\", stringTypeProvider.get().name());\n        getLogger().debug(\"Using fieldVisibility {}\", fieldVisibilityProvider.get().name());\n        getLogger().debug(\"Using templateDirectory '{}'\", getTemplateDirectory().getOrNull());\n        getLogger().debug(\"Using additionalVelocityToolClasses '{}'\", getAdditionalVelocityToolClasses().getOrNull());\n        getLogger().debug(\"Using createSetters {}\", isCreateSetters().get());\n        getLogger().debug(\"Using createOptionalGetters {}\", isCreateOptionalGetters().get());\n        getLogger().debug(\"Using gettersReturnOptional {}\", isGettersReturnOptional().get());\n        getLogger().debug(\"Using optionalGettersForNullableFieldsOnly {}\", isOptionalGettersForNullableFieldsOnly().get());\n        getLogger().debug(\"Using enableDecimalLogicalType {}\", isEnableDecimalLogicalType().get());\n        getLogger().debug(\"Using logicalTypeFactories {}\",\n            logicalTypeFactories.get().entrySet().stream().collect(Collectors.toMap(\n                Map.Entry::getKey,\n                (Map.Entry<String, Class<? extends LogicalTypes.LogicalTypeFactory>> e) -> e.getValue().getName()\n            )));\n        getLogger().debug(\"Using customConversions {}\",\n            customConversions.get().stream().map(v -> ((Class) v).getName()).collect(Collectors.toList()));\n        getLogger().info(\"Found {} files\", getInputs().getSourceFiles().getFiles().size());\n        failOnUnsupportedFiles();\n        processFiles();\n    }\n\n    private void failOnUnsupportedFiles() {\n        FileCollection unsupportedFiles = filterSources(new NotSpec<>(new FileExtensionSpec(SUPPORTED_EXTENSIONS)));\n        if (!unsupportedFiles.isEmpty()) {\n            throw new GradleException(\n                String.format(\"Unsupported file extension for the following files: %s\", unsupportedFiles));\n        }\n    }\n\n    private void processFiles() {\n        registerLogicalTypes();\n        int processedFileCount = 0;\n        processedFileCount += processProtoFiles();\n        processedFileCount += processSchemaFiles();\n        setDidWork(processedFileCount > 0);\n    }\n\n    private int processProtoFiles() {\n        int processedFileCount = 0;\n        for (File sourceFile : filterSources(new FileExtensionSpec(Constants.PROTOCOL_EXTENSION))) {\n            processProtoFile(sourceFile);\n            processedFileCount++;\n        }\n        return processedFileCount;\n    }\n\n    private void processProtoFile(File sourceFile) {\n        getLogger().info(\"Processing {}\", sourceFile);\n        try {\n            compile(new SpecificCompiler(Protocol.parse(sourceFile)), sourceFile);\n        } catch (IOException ex) {\n            throw new GradleException(String.format(\"Failed to compile protocol definition file %s\", sourceFile), ex);\n        }\n    }\n\n    private int processSchemaFiles() {\n        Set<File> files = filterSources(new FileExtensionSpec(Constants.SCHEMA_EXTENSION)).getFiles();\n        ProcessingState processingState = resolver.resolve(files);\n        for (File file : files) {\n            String path = FileUtils.projectRelativePath(projectLayout, file);\n            for (Schema schema : processingState.getSchemasForLocation(path)) {\n                try {\n                    compile(new SpecificCompiler(schema), file);\n                } catch (IOException ex) {\n                    throw new GradleException(String.format(\"Failed to compile schema definition file %s\", path), ex);\n                }\n            }\n        }\n        return processingState.getProcessedTotal();\n    }\n\n    private void compile(SpecificCompiler compiler, File sourceFile) throws IOException {\n        compiler.setOutputCharacterEncoding(getOutputCharacterEncoding().getOrNull());\n        compiler.setStringType(stringTypeProvider.get());\n        compiler.setFieldVisibility(fieldVisibilityProvider.get());\n        if (getTemplateDirectory().isPresent()) {\n            compiler.setTemplateDir(getTemplateDirectory().get());\n        }\n        if (getAdditionalVelocityToolClasses().isPresent()) {\n            ClassLoader loader = assembleClassLoader();\n            List<Object> tools = getAdditionalVelocityToolClasses().get().stream()\n                    .map(s -> {\n                        try {\n                            return Class.forName(s, true, loader);\n                        } catch (ClassNotFoundException e) {\n                            throw new RuntimeException(\"unable to load velocity tool class \" + s, e);\n                        }\n                    })\n                    .map(aClass -> {\n                        try {\n                            return aClass.getConstructor().newInstance();\n                        } catch (InstantiationException\n                                 | NoSuchMethodException\n                                 | InvocationTargetException\n                                 | IllegalAccessException e) {\n                            throw new RuntimeException(\"Unable to instantiate velocity tool class using default constructor: \" + aClass, e);\n                        }\n                    }).collect(Collectors.toList());\n            compiler.setAdditionalVelocityTools(tools);\n        }\n        compiler.setCreateOptionalGetters(createOptionalGetters.get());\n        compiler.setGettersReturnOptional(gettersReturnOptional.get());\n        compiler.setOptionalGettersForNullableFieldsOnly(optionalGettersForNullableFieldsOnly.get());\n        compiler.setCreateSetters(isCreateSetters().get());\n        compiler.setEnableDecimalLogicalType(isEnableDecimalLogicalType().get());\n        registerCustomConversions(compiler);\n\n        compiler.compileToDestination(sourceFile, getOutputDir().get().getAsFile());\n    }\n\n    /**\n     * Registers the logical types to be used in this run.\n     * This must be called before the Schemas are parsed, or they will not be applied correctly.\n     * Since {@link LogicalTypes} is a static registry, this may result in side-effects.\n     */\n    private void registerLogicalTypes() {\n        Map<String, Class<? extends LogicalTypes.LogicalTypeFactory>> logicalTypeFactoryMap = resolveLocalTypeFactories();\n        Set<Map.Entry<String, Class<? extends LogicalTypes.LogicalTypeFactory>>> logicalTypeFactoryEntries =\n            logicalTypeFactoryMap.entrySet();\n        for (Map.Entry<String, Class<? extends LogicalTypes.LogicalTypeFactory>> entry : logicalTypeFactoryEntries) {\n            String logicalTypeName = entry.getKey();\n            Class<? extends LogicalTypes.LogicalTypeFactory> logicalTypeFactoryClass = entry.getValue();\n            try {\n                LogicalTypes.LogicalTypeFactory logicalTypeFactory = logicalTypeFactoryClass.getDeclaredConstructor().newInstance();\n                LogicalTypes.register(logicalTypeName, logicalTypeFactory);\n            } catch (ReflectiveOperationException ex) {\n                getLogger().error(\"Could not instantiate logicalTypeFactory class \\\"\" + logicalTypeFactoryClass.getName() + \"\\\"\");\n            }\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Map<String, Class<? extends LogicalTypes.LogicalTypeFactory>> resolveLocalTypeFactories() {\n        Map<String, Class<? extends LogicalTypes.LogicalTypeFactory>> result = new HashMap<>();\n        if (logicalTypeFactoryClassNames.isPresent()) {\n            ClassLoader typeFactoriesClassLoader = createConversionsAndTypeFactoriesClassLoader();\n            for (Entry<String, String> entry : logicalTypeFactoryClassNames.get().entrySet()) {\n                String logicalTypeFactoryClassName = entry.getValue();\n                try {\n                    Class<?> aClass = Class.forName(logicalTypeFactoryClassName, true, typeFactoriesClassLoader);\n                    result.put(entry.getKey(), (Class<? extends LogicalTypeFactory>) aClass);\n                } catch (ClassNotFoundException e) {\n                    throw new RuntimeException(\"Unable to load logical type factory class \" + logicalTypeFactoryClassName, e);\n                }\n            }\n        }\n        result.putAll(logicalTypeFactories.get());\n        return result;\n    }\n\n    private void registerCustomConversions(SpecificCompiler compiler) {\n        loadCustomConversionClasses().forEach(compiler::addCustomConversion);\n        customConversions.get().forEach(compiler::addCustomConversion);\n    }\n\n    private List<Class<?>> loadCustomConversionClasses() {\n        if (customConversionClassNames.isPresent()) {\n            ClassLoader customConversionsClassLoader = createConversionsAndTypeFactoriesClassLoader();\n            return customConversionClassNames.get().stream()\n                .map(conversionClassName -> {\n                    try {\n                        return Class.forName(conversionClassName, true, customConversionsClassLoader);\n                    } catch (ClassNotFoundException e) {\n                        throw new RuntimeException(\"Unable to load custom conversion class \" + conversionClassName, e);\n                    }\n                }).collect(Collectors.toList());\n        } else {\n            return Collections.emptyList();\n        }\n    }\n\n    private ClassLoader createConversionsAndTypeFactoriesClassLoader() {\n        URL[] urls = conversionsAndTypeFactoriesClasspath.getFiles().stream()\n            .map(File::toURI)\n            .map(uri -> {\n                try {\n                    return uri.toURL();\n                } catch (MalformedURLException e) {\n                    throw new RuntimeException(\"Unable to resolve URL in conversions and type factories classpath\", e);\n                }\n            })\n            .toArray(URL[]::new);\n\n        return new URLClassLoader(urls, getClass().getClassLoader());\n    }\n\n    private ClassLoader assembleClassLoader() {\n        getLogger().debug(\"Using additional classpath: {}\", classpath.getFiles());\n        List<URL> urls = new LinkedList<>();\n        for (File file : classpath) {\n            try {\n                urls.add(file.toURI().toURL());\n            } catch (MalformedURLException e) {\n                getLogger().debug(e.getMessage());\n            }\n        }\n        return new URLClassLoader(urls.toArray(new URL[0]), Thread.currentThread().getContextClassLoader());\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/GenerateAvroProtocolTask.java",
    "content": "/**\n * Copyright © 2013-2019 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Set;\nimport org.apache.avro.Protocol;\nimport org.apache.avro.compiler.idl.Idl;\nimport org.apache.avro.compiler.idl.ParseException;\nimport org.gradle.api.GradleException;\nimport org.gradle.api.file.FileCollection;\nimport org.gradle.api.specs.NotSpec;\nimport org.gradle.api.tasks.CacheableTask;\nimport org.gradle.api.tasks.Classpath;\nimport org.gradle.api.tasks.TaskAction;\n\nimport static com.github.davidmc24.gradle.plugin.avro.Constants.IDL_EXTENSION;\n\n/**\n * Task to convert Avro IDL files into Avro protocol files using {@link Idl}.\n */\n@CacheableTask\npublic class GenerateAvroProtocolTask extends OutputDirTask {\n\n    private FileCollection classpath;\n    private Set<String> processedFiles;\n\n    public GenerateAvroProtocolTask() {\n        super();\n        this.classpath = GradleCompatibility.createConfigurableFileCollection(getProject());\n        this.processedFiles = new HashSet<String>();\n    }\n\n    public void setClasspath(FileCollection classpath) {\n        this.classpath = classpath;\n    }\n\n    public void classpath(Object... paths) {\n        this.classpath.plus(getProject().files(paths));\n    }\n\n    @Classpath\n    public FileCollection getClasspath() {\n        return this.classpath;\n    }\n\n    @TaskAction\n    protected void process() {\n        getLogger().info(\"Found {} files\", getSource().getFiles().size());\n        failOnUnsupportedFiles();\n        processFiles();\n    }\n\n    private void failOnUnsupportedFiles() {\n        FileCollection unsupportedFiles = filterSources(new NotSpec<>(new FileExtensionSpec(IDL_EXTENSION)));\n        if (!unsupportedFiles.isEmpty()) {\n            throw new GradleException(\n                    String.format(\"Unsupported file extension for the following files: %s\", unsupportedFiles));\n        }\n    }\n\n    private void processFiles() {\n        int processedFileCount = 0;\n        ClassLoader loader = assembleClassLoader();\n        for (File sourceFile : filterSources(new FileExtensionSpec(IDL_EXTENSION))) {\n            processIDLFile(sourceFile, loader);\n            processedFileCount++;\n        }\n        setDidWork(processedFileCount > 0);\n    }\n\n    private void processIDLFile(File idlFile, ClassLoader loader) {\n        getLogger().info(\"Processing {}\", idlFile);\n        try (Idl idl = new Idl(idlFile, loader)) {\n            File outputDir = getOutputDir().get().getAsFile();\n            Protocol protocol = idl.CompilationUnit();\n            String filePath = AvroUtils.assemblePath(protocol);\n            if (!processedFiles.add(filePath)) {\n                throw new GradleException(\"File already processed with same namespace and protocol name.\");\n            }\n            File protoFile = new File(outputDir, filePath);\n            String protoJson = protocol.toString(true);\n            FileUtils.writeJsonFile(protoFile, protoJson);\n            getLogger().debug(\"Wrote {}\", protoFile.getPath());\n        } catch (IOException | ParseException | GradleException ex) {\n            throw new GradleException(String.format(\"Failed to compile IDL file %s\", idlFile), ex);\n        }\n    }\n\n    private ClassLoader assembleClassLoader() {\n        getLogger().debug(\"Using classpath: {}\", classpath.getFiles());\n        List<URL> urls = new LinkedList<>();\n        for (File file : classpath) {\n            try {\n                urls.add(file.toURI().toURL());\n            } catch (MalformedURLException e) {\n                getLogger().debug(e.getMessage());\n            }\n        }\n        // No parent classloader; either it's in the specified classpath or it shouldn't be resolved.\n        return new URLClassLoader(urls.toArray(new URL[0]), null);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/GenerateAvroSchemaTask.java",
    "content": "/*\n * Copyright © 2018-2019 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.io.File;\nimport java.io.IOException;\nimport org.apache.avro.Protocol;\nimport org.apache.avro.Schema;\nimport org.gradle.api.GradleException;\nimport org.gradle.api.file.FileCollection;\nimport org.gradle.api.specs.NotSpec;\nimport org.gradle.api.tasks.CacheableTask;\nimport org.gradle.api.tasks.TaskAction;\n\n@CacheableTask\npublic class GenerateAvroSchemaTask extends OutputDirTask {\n    @TaskAction\n    protected void process() {\n        getLogger().info(\"Found {} files\", getSource().getFiles().size());\n        failOnUnsupportedFiles();\n        processFiles();\n    }\n\n    private void failOnUnsupportedFiles() {\n        FileCollection unsupportedFiles = filterSources(new NotSpec<>(new FileExtensionSpec(Constants.PROTOCOL_EXTENSION)));\n        if (!unsupportedFiles.isEmpty()) {\n            throw new GradleException(\n                String.format(\"Unsupported file extension for the following files: %s\", unsupportedFiles));\n        }\n    }\n\n    private void processFiles() {\n        int processedFileCount = 0;\n        for (File sourceFile : filterSources(new FileExtensionSpec(Constants.PROTOCOL_EXTENSION))) {\n            processProtoFile(sourceFile);\n            processedFileCount++;\n        }\n        setDidWork(processedFileCount > 0);\n    }\n\n    private void processProtoFile(File sourceFile) {\n        getLogger().info(\"Processing {}\", sourceFile);\n        try {\n            Protocol protocol = Protocol.parse(sourceFile);\n            for (Schema schema : protocol.getTypes()) {\n                File schemaFile = new File(getOutputDir().get().getAsFile(), AvroUtils.assemblePath(schema));\n                String schemaJson = schema.toString(true);\n                FileUtils.writeJsonFile(schemaFile, schemaJson);\n                getLogger().debug(\"Wrote {}\", schemaFile.getPath());\n            }\n        } catch (IOException ex) {\n            throw new GradleException(String.format(\"Failed to process protocol definition file %s\", sourceFile), ex);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/GradleCompatibility.java",
    "content": "/*\n * Copyright © 2019 Commerce Technologies, LLC.\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 */\n\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.io.File;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport org.gradle.api.Project;\nimport org.gradle.api.file.ConfigurableFileCollection;\nimport org.gradle.api.tasks.SourceSet;\nimport org.gradle.plugins.ide.idea.model.IdeaModule;\n\nclass GradleCompatibility {\n    private static final Class<?>[] NO_PARAMETERS = {};\n    private static final Object[] NO_ARGUMENTS = {};\n\n    static <T> T createExtensionWithObjectFactory(Project project, String extensionName, Class<T> extensionType) {\n        if (GradleFeatures.projectIntoExtensionInjection.isSupported()) {\n            return project.getExtensions().create(extensionName, extensionType);\n        } else {\n            return project.getExtensions().create(extensionName, extensionType, project, project.getObjects());\n        }\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    static ConfigurableFileCollection createConfigurableFileCollection(Project project) {\n        if (GradleFeatures.objectFactoryFileCollection.isSupported()) {\n            return project.getObjects().fileCollection();\n        } else {\n            Class<?>[] parameterTypes = {Object[].class};\n            Object[] args = {new Object[0]};\n            return invokeMethod(project.getLayout(), \"configurableFiles\", parameterTypes, args);\n        }\n    }\n\n    static String getSourcesJarTaskName(SourceSet sourceSet) {\n        if (GradleFeatures.getSourcesJarTaskName.isSupported()) {\n            return sourceSet.getSourcesJarTaskName();\n        } else {\n            return sourceSet.getTaskName(null, \"sourcesJar\");\n        }\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    static void addTestSources(IdeaModule module, File... files) {\n        if (GradleFeatures.ideaModuleTestSources.isSupported()) {\n            // Can't use these methods directly as they didn't exist until 7.4\n            Object testSources = invokeMethod(module, \"getTestSources\", NO_PARAMETERS, NO_ARGUMENTS);\n            Class<?>[] parameterTypes = {Object[].class};\n            Object[] args = {files};\n            invokeMethod(testSources, \"from\", parameterTypes, args);\n        } else {\n            // Deprecated in 7.6\n            module.setTestSourceDirs(new SetBuilder<File>()\n                .addAll(module.getTestSourceDirs())\n                .addAll(files)\n                .build());\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private static <T> T invokeMethod(Object object, String methodName, Class<?>[] parameterTypes, Object[] args) {\n        try {\n            Method method = object.getClass().getMethod(methodName, parameterTypes);\n            return (T) method.invoke(object, args);\n        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {\n            throw new RuntimeException(\"Failed to invoke method via reflection\", ex);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/GradleFeatures.java",
    "content": "/*\n * Copyright © 2019 Commerce Technologies, LLC.\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 */\n\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport org.gradle.util.GradleVersion;\n\nenum GradleFeatures {\n    projectIntoExtensionInjection() {\n        boolean isSupportedBy(GradleVersion version) {\n            return version.compareTo(GradleVersions.v7_1) >= 0;\n        }\n    },\n    objectFactoryFileCollection() {\n        @Override\n        boolean isSupportedBy(GradleVersion version) {\n            return version.compareTo(GradleVersions.v5_3) >= 0;\n        }\n    },\n    configCache() {\n        @Override\n        boolean isSupportedBy(GradleVersion version) {\n            return version.compareTo(GradleVersions.v6_6) >= 0;\n        }\n    },\n    getSourcesJarTaskName() {\n        @Override\n        boolean isSupportedBy(GradleVersion version) {\n            return version.compareTo(GradleVersions.v6_0) >= 0;\n        }\n    },\n    ideaModuleTestSources() {\n        @Override\n        boolean isSupportedBy(GradleVersion version) {\n            return version.compareTo(GradleVersions.v7_4) >= 0;\n        }\n    };\n\n    abstract boolean isSupportedBy(GradleVersion version);\n    boolean isSupported() {\n        return isSupportedBy(GradleVersion.current());\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/GradleVersions.java",
    "content": "/*\n * Copyright © 2019 Commerce Technologies, LLC.\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 */\n\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport org.gradle.util.GradleVersion;\n\nclass GradleVersions {\n    static final GradleVersion v5_3 = GradleVersion.version(\"5.3\");\n    static final GradleVersion v6_0 = GradleVersion.version(\"6.0\");\n    static final GradleVersion v6_6 = GradleVersion.version(\"6.6\");\n    static final GradleVersion v7_1 = GradleVersion.version(\"7.1\");\n    static final GradleVersion v7_4 = GradleVersion.version(\"7.4\");\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/MapUtils.java",
    "content": "/**\n * Copyright © 2015 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nclass MapUtils {\n    /**\n     * Returns the map of all entries present in the first map but not present in the second map (by key).\n     */\n    static <K, V> Map<K, V> asymmetricDifference(Map<K, V> a, Map<K, V> b) {\n        if (b == null || b.isEmpty()) {\n            return a;\n        }\n        Map<K, V> result = new LinkedHashMap<>(a);\n        result.keySet().removeAll(b.keySet());\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/OutputDirTask.java",
    "content": "/**\n * Copyright © 2013-2019 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.io.File;\nimport javax.annotation.Nonnull;\nimport org.gradle.api.file.DirectoryProperty;\nimport org.gradle.api.file.FileCollection;\nimport org.gradle.api.file.FileTree;\nimport org.gradle.api.specs.Spec;\nimport org.gradle.api.tasks.OutputDirectory;\nimport org.gradle.api.tasks.PathSensitive;\nimport org.gradle.api.tasks.SourceTask;\n\nimport static org.gradle.api.tasks.PathSensitivity.RELATIVE;\n\nclass OutputDirTask extends SourceTask {\n    private final DirectoryProperty outputDir;\n\n    OutputDirTask() {\n        this.outputDir = getProject().getObjects().directoryProperty();\n    }\n\n    public void setOutputDir(File outputDir) {\n        this.outputDir.set(outputDir);\n        getOutputs().dir(outputDir);\n    }\n\n    @Nonnull\n    @PathSensitive(value = RELATIVE)\n    public FileTree getSource() {\n        return super.getSource();\n    }\n\n    @OutputDirectory\n    protected DirectoryProperty getOutputDir() {\n        return outputDir;\n    }\n\n    FileCollection filterSources(Spec<? super File> spec) {\n        return getSource().filter(spec);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/ProcessingState.java",
    "content": "/**\n * Copyright © 2015 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.io.File;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.LinkedList;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport org.apache.avro.Schema;\nimport org.gradle.api.file.ProjectLayout;\n\nclass ProcessingState {\n    private final Map<String, TypeState> typeStates = new HashMap<>();\n    private final Set<FileState> delayedFiles = new LinkedHashSet<>();\n    private final Queue<FileState> filesToProcess = new LinkedList<>();\n    private int processedTotal;\n\n    ProcessingState(Iterable<File> files, ProjectLayout projectLayout) {\n        for (File file : files) {\n            filesToProcess.add(new FileState(file, FileUtils.projectRelativePath(projectLayout, file)));\n        }\n    }\n\n    Map<String, Schema> determineParserTypes(FileState fileState) {\n        Set<String> duplicateTypeNames = fileState.getDuplicateTypeNames();\n        Map<String, Schema> types = new HashMap<>();\n        for (TypeState typeState : typeStates.values()) {\n            String typeName = typeState.getName();\n            if (!duplicateTypeNames.contains(typeName)) {\n                types.put(typeState.getName(), typeState.getSchema());\n            }\n        }\n        return types;\n    }\n\n    void processTypeDefinitions(FileState fileState, Map<String, Schema> newTypes) {\n        String path = fileState.getPath();\n        for (Map.Entry<String, Schema> entry : newTypes.entrySet()) {\n            String typeName = entry.getKey();\n            Schema schema = entry.getValue();\n            getTypeState(typeName).processTypeDefinition(path, schema);\n        }\n        fileState.clearError();\n        processedTotal++;\n        queueDelayedFilesForProcessing();\n    }\n\n    Set<FileState> getFailedFiles() {\n        return delayedFiles;\n    }\n\n    private TypeState getTypeState(String typeName) {\n        TypeState typeState = typeStates.get(typeName);\n        if (typeState == null) {\n            typeState = new TypeState(typeName);\n            typeStates.put(typeName, typeState);\n        }\n        return typeState;\n    }\n\n    void queueForProcessing(FileState fileState) {\n        filesToProcess.add(fileState);\n    }\n\n    void queueForDelayedProcessing(FileState fileState) {\n        delayedFiles.add(fileState);\n    }\n\n    private void queueDelayedFilesForProcessing() {\n        filesToProcess.addAll(delayedFiles);\n        delayedFiles.clear();\n    }\n\n    FileState nextFileState() {\n        return filesToProcess.poll();\n    }\n\n    boolean isWorkRemaining() {\n        return !filesToProcess.isEmpty();\n    }\n\n    int getProcessedTotal() {\n        return processedTotal;\n    }\n\n    Iterable<? extends Schema> getSchemasForLocation(String path) {\n        return typeStates.values().stream().filter(it -> it.hasLocation(path)).map(TypeState::getSchema).collect(Collectors.toList());\n    }\n\n    Iterable<? extends Schema> getSchemas() {\n        return typeStates.values().stream().map(TypeState::getSchema).collect(Collectors.toSet());\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/ResolveAvroDependenciesTask.java",
    "content": "package com.github.davidmc24.gradle.plugin.avro;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Set;\nimport org.apache.avro.Schema;\nimport org.gradle.api.GradleException;\nimport org.gradle.api.file.FileCollection;\nimport org.gradle.api.specs.NotSpec;\nimport org.gradle.api.tasks.CacheableTask;\nimport org.gradle.api.tasks.TaskAction;\n\n/**\n * Task to read Avro schema files, resolve their dependencies, and write out dependency-free Avro schema files.\n */\n@CacheableTask\npublic class ResolveAvroDependenciesTask extends OutputDirTask {\n    private final SchemaResolver resolver = new SchemaResolver(getProject().getLayout(), getLogger());\n\n    @TaskAction\n    protected void process() {\n        getLogger().info(\"Found {} files\", getInputs().getSourceFiles().getFiles().size());\n        failOnUnsupportedFiles();\n        processFiles();\n    }\n\n    private void failOnUnsupportedFiles() {\n        FileCollection unsupportedFiles = filterSources(new NotSpec<>(new FileExtensionSpec(Constants.SCHEMA_EXTENSION)));\n        if (!unsupportedFiles.isEmpty()) {\n            throw new GradleException(\n                String.format(\"Unsupported file extension for the following files: %s\", unsupportedFiles));\n        }\n    }\n\n    private void processFiles() {\n        int processedFileCount = processSchemaFiles();\n        setDidWork(processedFileCount > 0);\n    }\n\n    private int processSchemaFiles() {\n        Set<File> inputFiles = filterSources(new FileExtensionSpec(Constants.SCHEMA_EXTENSION)).getFiles();\n        ProcessingState processingState = resolver.resolve(inputFiles);\n        for (Schema schema : processingState.getSchemas()) {\n            try {\n                File outputFile = new File(getOutputDir().get().getAsFile(), AvroUtils.assemblePath(schema));\n                String schemaJson = schema.toString(true);\n                FileUtils.writeJsonFile(outputFile, schemaJson);\n                getLogger().debug(\"Wrote {}\", outputFile.getPath());\n            } catch (IOException ex) {\n                throw new GradleException(String.format(\"Failed to write resolved schema definition for %s\", schema.getFullName()), ex);\n            }\n        }\n        return processingState.getProcessedTotal();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/SchemaResolver.java",
    "content": "package com.github.davidmc24.gradle.plugin.avro;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport org.apache.avro.Schema;\nimport org.apache.avro.SchemaParseException;\nimport org.gradle.api.GradleException;\nimport org.gradle.api.file.ProjectLayout;\nimport org.gradle.api.logging.Logger;\n\nclass SchemaResolver {\n    private static Pattern ERROR_UNKNOWN_TYPE = Pattern.compile(\"(?i).*(undefined name|not a defined name|type not supported).*\");\n    private static Pattern ERROR_DUPLICATE_TYPE = Pattern.compile(\"Can't redefine: (.*)\");\n\n    private final ProjectLayout projectLayout;\n    private final Logger logger;\n\n    SchemaResolver(ProjectLayout projectLayout, Logger logger) {\n        this.projectLayout = projectLayout;\n        this.logger = logger;\n    }\n\n    ProcessingState resolve(Iterable<File> files) {\n        ProcessingState processingState = new ProcessingState(files, projectLayout);\n        while (processingState.isWorkRemaining()) {\n            processSchemaFile(processingState, processingState.nextFileState());\n        }\n        Set<FileState> failedFiles = processingState.getFailedFiles();\n        if (!failedFiles.isEmpty()) {\n            StringBuilder errorMessage = new StringBuilder(\"Could not resolve schema definition files:\");\n            for (FileState fileState : failedFiles) {\n                String path = fileState.getPath();\n                String fileErrorMessage = fileState.getErrorMessage();\n                errorMessage.append(System.lineSeparator()).append(\"* \").append(path).append(\": \").append(fileErrorMessage);\n            }\n            throw new GradleException(errorMessage.toString());\n        }\n        return processingState;\n    }\n\n    private void processSchemaFile(ProcessingState processingState, FileState fileState) {\n        String path = fileState.getPath();\n        logger.debug(\"Processing {}, excluding types {}\", path, fileState.getDuplicateTypeNames());\n        File sourceFile = fileState.getFile();\n        Map<String, Schema> parserTypes = processingState.determineParserTypes(fileState);\n        try {\n            Schema.Parser parser = new Schema.Parser();\n            parser.addTypes(parserTypes);\n            parser.parse(sourceFile);\n            Map<String, Schema> typesDefinedInFile = MapUtils.asymmetricDifference(parser.getTypes(), parserTypes);\n            processingState.processTypeDefinitions(fileState, typesDefinedInFile);\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"Processed {}; contained types {}\", path, typesDefinedInFile.keySet());\n            } else {\n                logger.info(\"Processed {}\", path);\n            }\n        } catch (SchemaParseException ex) {\n            String errorMessage = ex.getMessage();\n            Matcher unknownTypeMatcher = ERROR_UNKNOWN_TYPE.matcher(errorMessage);\n            Matcher duplicateTypeMatcher = ERROR_DUPLICATE_TYPE.matcher(errorMessage);\n            if (unknownTypeMatcher.matches()) {\n                fileState.setError(ex);\n                processingState.queueForDelayedProcessing(fileState);\n                logger.debug(\"Found undefined name in {} ({}); will try again\", path, errorMessage);\n            } else if (duplicateTypeMatcher.matches()) {\n                String typeName = duplicateTypeMatcher.group(1);\n                if (fileState.containsDuplicateTypeName(typeName)) {\n                    throw new GradleException(\n                        String.format(\"Failed to resolve schema definition file %s; contains duplicate type definition %s\", path, typeName),\n                        ex);\n                } else {\n                    fileState.setError(ex);\n                    fileState.addDuplicateTypeName(typeName);\n                    processingState.queueForProcessing(fileState);\n                    logger.debug(\"Identified duplicate type {} in {}; will re-process excluding it\", typeName, path);\n                }\n            } else {\n                throw new GradleException(String.format(\"Failed to resolve schema definition file %s\", path), ex);\n            }\n        } catch (IOException ex) {\n            throw new GradleException(String.format(\"Failed to resolve schema definition file %s\", path), ex);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/SetBuilder.java",
    "content": "/**\n * Copyright © 2013-2015 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\n\n@SuppressWarnings(\"UnusedReturnValue\")\nclass SetBuilder<T> {\n    private Set<T> set = new HashSet<T>();\n\n    SetBuilder<T> add(T e) {\n        set.add(e);\n        return this;\n    }\n\n    final SetBuilder<T> addAll(T[] c) {\n        Collections.addAll(set, c);\n        return this;\n    }\n\n    SetBuilder<T> addAll(Collection<? extends T> c) {\n        set.addAll(c);\n        return this;\n    }\n\n    SetBuilder<T> remove(T e) {\n        set.remove(e);\n        return this;\n    }\n\n    Set<T> build() {\n        return set;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/Strings.java",
    "content": "package com.github.davidmc24.gradle.plugin.avro;\n\n/**\n * Utility methods for working with {@link String}s.\n */\nclass Strings {\n    /**\n     * Not intended for instantiation.\n     */\n    private Strings() { }\n\n    /**\n     * Checks if a {@link String} is empty ({@code \"\"}) or {@code null}.\n     *\n     * @param str the String to check, may be {@code null}\n     * @return true if the String is empty or {@code null}\n     */\n    static boolean isEmpty(String str) {\n        return str == null || str.isEmpty();\n    }\n\n    /**\n     * Checks if a {@link String} is not empty ({@code \"\"}) and not {@code null}.\n     *\n     * @param str the String to check, may be {@code null}\n     * @return true if the String is not empty and not {@code null}\n     */\n    static boolean isNotEmpty(String str) {\n        return !isEmpty(str);\n    }\n\n    /**\n     * Requires that a {@link String} is not empty ({@code \"\"}) and not {@code null}.\n     * If the requirement is violated, an {@link IllegalArgumentException} will be thrown.\n     *\n     * @param str the String to check, may be {@code null}\n     * @param message the message to include in\n     * @return the String, if the requirement was not violated\n     * @throws IllegalArgumentException if the requirement was violated\n     */\n    @SuppressWarnings({\"UnusedReturnValue\", \"SameParameterValue\"})\n    static String requireNotEmpty(String str, String message) {\n        if (isEmpty(str)) {\n            throw new IllegalArgumentException(message);\n        }\n        return str;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/github/davidmc24/gradle/plugin/avro/TypeState.java",
    "content": "/**\n * Copyright © 2015 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro;\n\nimport java.util.Set;\nimport java.util.TreeSet;\nimport org.apache.avro.Schema;\nimport org.gradle.api.GradleException;\n\nclass TypeState {\n    private final String name;\n    private final Set<String> locations = new TreeSet<>();\n    private Schema schema;\n\n    TypeState(String name) {\n        this.name = name;\n    }\n\n    void processTypeDefinition(String path, Schema schemaToProcess) {\n        locations.add(path);\n        if (this.schema == null) {\n            this.schema = schemaToProcess;\n        } else if (!this.schema.equals(schemaToProcess)) {\n            throw new GradleException(String.format(\"Found conflicting definition of type %s in %s\", name, locations));\n        } // Otherwise duplicate declaration of identical schema; nothing to do\n    }\n\n    String getName() {\n        return name;\n    }\n\n    Schema getSchema() {\n        return schema;\n    }\n\n    boolean hasLocation(String location) {\n        return locations.contains(location);\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/AvroBasePluginFunctionalSpec.groovy",
    "content": "/*\n * Copyright © 2018 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro\n\nimport com.github.davidmc24.gradle.plugin.avro.test.custom.CommentGenerator\nimport com.github.davidmc24.gradle.plugin.avro.test.custom.TimestampGenerator\n\nimport static org.gradle.testkit.runner.TaskOutcome.SUCCESS\n\nclass AvroBasePluginFunctionalSpec extends FunctionalSpec {\n    def \"setup\"() {\n        applyAvroBasePlugin()\n    }\n\n    def \"can generate java files from json schema\"() {\n        given:\n        buildFile << \"\"\"\n        |tasks.register(\"generateAvroJava\", com.github.davidmc24.gradle.plugin.avro.GenerateAvroJavaTask) {\n        |    source file(\"src/main/avro\")\n        |    include(\"**/*.avsc\")\n        |    outputDir = file(\"build/generated-main-avro-java\")\n        |}\n        |\"\"\".stripMargin()\n\n        copyResource(\"user.avsc\", avroDir)\n\n        when:\n        def result = run(\"generateAvroJava\")\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        projectFile(\"build/generated-main-avro-java/example/avro/User.java\").file\n    }\n\n    def \"can generate json schema files from json protocol\"() {\n        given:\n        buildFile << \"\"\"\n        |tasks.register(\"generateSchema\", com.github.davidmc24.gradle.plugin.avro.GenerateAvroSchemaTask) {\n        |    source file(\"src/main/avro\")\n        |    include(\"**/*.avpr\")\n        |    outputDir = file(\"build/generated-main-avro-avsc\")\n        |}\n        |\"\"\".stripMargin()\n\n        copyResource(\"mail.avpr\", avroDir)\n\n        when:\n        def result = run(\"generateSchema\")\n\n        then:\n        result.task(\":generateSchema\").outcome == SUCCESS\n        def expectedFileContents = getClass().getResource(\"Message.avsc\").text.trim()\n        def generateFileContents = projectFile(\"build/generated-main-avro-avsc/org/apache/avro/test/Message.avsc\").text.trim()\n        expectedFileContents == generateFileContents\n    }\n\n    def \"can generate json schema files from IDL\"() {\n        given:\n        buildFile << \"\"\"\n        |tasks.register(\"generateProtocol\", com.github.davidmc24.gradle.plugin.avro.GenerateAvroProtocolTask) {\n        |    source file(\"src/main/avro\")\n        |    outputDir = file(\"build/generated-avro-main-avpr\")\n        |}\n        |tasks.register(\"generateSchema\", com.github.davidmc24.gradle.plugin.avro.GenerateAvroSchemaTask) {\n        |    dependsOn generateProtocol\n        |    source file(\"build/generated-avro-main-avpr\")\n        |    include(\"**/*.avpr\")\n        |    outputDir = file(\"build/generated-main-avro-avsc\")\n        |}\n        |\"\"\".stripMargin()\n\n        copyResource(\"interop.avdl\", avroDir)\n\n        when:\n        def result = run(\"generateSchema\")\n\n        then:\n        result.task(\":generateSchema\").outcome == SUCCESS\n        projectFile(\"build/generated-main-avro-avsc/org/apache/avro/Foo.avsc\").file\n        projectFile(\"build/generated-main-avro-avsc/org/apache/avro/Kind.avsc\").file\n        projectFile(\"build/generated-main-avro-avsc/org/apache/avro/MD5.avsc\").file\n        projectFile(\"build/generated-main-avro-avsc/org/apache/avro/Node.avsc\").file\n        projectFile(\"build/generated-main-avro-avsc/org/apache/avro/Interop.avsc\").file\n    }\n\n    def \"example of converting both IDL and json protocol simultaneously\"() {\n        given:\n        buildFile << \"\"\"\n        |tasks.register(\"generateProtocol\", com.github.davidmc24.gradle.plugin.avro.GenerateAvroProtocolTask) {\n        |    source file(\"src/main/avro\")\n        |    include(\"**/*.avdl\")\n        |    outputDir = file(\"build/generated-avro-main-avpr\")\n        |}\n        |tasks.register(\"generateSchema\", com.github.davidmc24.gradle.plugin.avro.GenerateAvroSchemaTask) {\n        |    dependsOn generateProtocol\n        |    source file(\"src/main/avro\")\n        |    source file(\"build/generated-avro-main-avpr\")\n        |    include(\"**/*.avpr\")\n        |    outputDir = file(\"build/generated-main-avro-avsc\")\n        |}\n        |\"\"\".stripMargin()\n\n        copyResource(\"mail.avpr\", avroDir)\n        copyResource(\"interop.avdl\", avroDir)\n\n        when:\n        def result = run(\"generateSchema\")\n\n        then:\n        result.task(\":generateSchema\").outcome == SUCCESS\n        projectFile(\"build/generated-main-avro-avsc/org/apache/avro/test/Message.avsc\").file\n        projectFile(\"build/generated-main-avro-avsc/org/apache/avro/Foo.avsc\").file\n        projectFile(\"build/generated-main-avro-avsc/org/apache/avro/Kind.avsc\").file\n        projectFile(\"build/generated-main-avro-avsc/org/apache/avro/MD5.avsc\").file\n        projectFile(\"build/generated-main-avro-avsc/org/apache/avro/Node.avsc\").file\n        projectFile(\"build/generated-main-avro-avsc/org/apache/avro/Interop.avsc\").file\n    }\n\n    def \"supports classpath property for instantiating of velocity tools\"() {\n        given:\n        copyAvroTools(\"src/main/java\")\n        def templatesDir = projectFolder(\"templates\")\n        copyResource(\"user.avsc\", avroDir)\n        copyResource(\"record-tools.vm\", templatesDir, \"record.vm\")\n        applyPlugin(\"java\")\n        buildFile << \"\"\"\n        |avro {\n        |    templateDirectory = \"${templatesDir.toString().replace('\\\\', '\\\\\\\\')}/\"\n        |    additionalVelocityToolClasses = ['com.github.davidmc24.gradle.plugin.avro.test.custom.TimestampGenerator',\n        |                                     'com.github.davidmc24.gradle.plugin.avro.test.custom.CommentGenerator']\n        |}\n        |tasks.register(\"compileTools\", JavaCompile) {\n        |   source = sourceSets.main.java\n        |   classpath = sourceSets.main.compileClasspath\n        |   destinationDir = file(\"build/classes/java/main\")\n        |}\n        |tasks.register(\"generateAvro\", com.github.davidmc24.gradle.plugin.avro.GenerateAvroJavaTask) {\n        |    dependsOn compileTools\n        |    classpath = files(\"build/classes/java/main\")\n        |    source file(\"src/main/avro\")\n        |    include(\"**/*.avsc\")\n        |    outputDir = file(\"build/generated-main-avro-java\")\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = run(\"generateAvro\")\n\n        then: \"the task succeeds\"\n        result.task(\":generateAvro\").outcome == SUCCESS\n        def content = projectFile(\"build/generated-main-avro-java/example/avro/User.java\").text\n\n        and: \"the velocity tools have been applied\"\n        content.contains(CommentGenerator.CUSTOM_COMMENT)\n        content.contains(TimestampGenerator.MESSAGE_PREFIX)\n    }\n\n    private void copyAvroTools(String destDir) {\n        copyFile(\"src/test/java\", destDir,\n            \"com/github/davidmc24/gradle/plugin/avro/test/custom/CommentGenerator.java\")\n        copyFile(\"src/test/java\", destDir,\n            \"com/github/davidmc24/gradle/plugin/avro/test/custom/TimestampGenerator.java\")\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/AvroPluginFunctionalSpec.groovy",
    "content": "/*\n * Copyright © 2015-2017 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro\n\nimport static org.gradle.testkit.runner.TaskOutcome.FAILED\nimport static org.gradle.testkit.runner.TaskOutcome.SUCCESS\n\nclass AvroPluginFunctionalSpec extends FunctionalSpec {\n    def \"setup\"() {\n        applyAvroPlugin()\n        addDefaultRepository()\n        addAvroDependency()\n    }\n\n    def \"can generate and compile java files from json schema\"() {\n        given:\n        copyResource(\"user.avsc\", avroDir)\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"example/avro/User.class\")).file\n    }\n\n    def \"can generate and compile java files from json protocol\"() {\n        given:\n        addAvroIpcDependency()\n        copyResource(\"mail.avpr\", avroDir)\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"org/apache/avro/test/Mail.class\")).file\n        projectFile(buildOutputClassPath(\"org/apache/avro/test/Message.class\")).file\n    }\n\n    def \"can generate and compile java files from IDL\"() {\n        given:\n        copyResource(interopIDLResourceName, avroDir)\n\n        when:\n        def result = run()\n        def interopJavaContent = projectFile(\"build/generated-main-avro-java/org/apache/avro/Interop.java\").text\n\n        then:\n        result.task(\":generateAvroProtocol\").outcome == SUCCESS\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(\"build/generated-main-avro-java/org/apache/avro/Interop.java\").file\n        projectFile(buildOutputClassPath(\"org/apache/avro/Foo.class\")).file\n        projectFile(buildOutputClassPath(\"org/apache/avro/Interop.class\")).file\n        projectFile(buildOutputClassPath(\"org/apache/avro/Kind.class\")).file\n        projectFile(buildOutputClassPath(\"org/apache/avro/MD5.class\")).file\n        projectFile(buildOutputClassPath(\"org/apache/avro/Node.class\")).file\n        interopJavaContent\n        interopJavaContent.contains(\"int intField\")\n        interopJavaContent.contains(\"long longField\")\n        interopJavaContent.contains(\"String stringField\")\n        interopJavaContent.contains(\"boolean boolField\")\n        interopJavaContent.contains(\"float floatField\")\n        interopJavaContent.contains(\"double doubleField\")\n        interopJavaContent.contains(\"java.lang.Void nullField\")\n        interopJavaContent.contains(\"java.util.List<java.lang.Double> arrayField\")\n        interopJavaContent =~ /Map<java.lang.String,.*Foo> mapField/\n        interopJavaContent.contains(\"Object unionField\")\n        interopJavaContent.contains(\"Kind enumField\")\n        interopJavaContent.contains(\"MD5 fixedField\")\n        interopJavaContent.contains(\"Node recordField\")\n        interopJavaContent.contains(\"BigDecimal decimalField\")\n        interopJavaContent.contains(\"LocalDate dateField\")\n        interopJavaContent.contains(\"LocalTime timeField\")\n        interopJavaContent.contains(\"Instant timeStampField\")\n        if (localTimestampConversionSupported) {\n            interopJavaContent.contains(\"LocalDateTime localTimeStampField\")\n        }\n    }\n\n    def \"supports json schema files in subdirectories\"() {\n        given:\n        copyResource(\"user.avsc\", avroSubDir)\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"example/avro/User.class\")).file\n    }\n\n    def \"supports json protocol files in subdirectories\"() {\n        given:\n        addAvroIpcDependency()\n        copyResource(\"mail.avpr\", avroSubDir)\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"org/apache/avro/test/Mail.class\")).file\n        projectFile(buildOutputClassPath(\"org/apache/avro/test/Message.class\")).file\n    }\n\n    def \"supports IDL files in subdirectories\"() {\n        given:\n        copyResource(interopIDLResourceName, avroSubDir)\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroProtocol\").outcome == SUCCESS\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"org/apache/avro/Foo.class\")).file\n        projectFile(buildOutputClassPath(\"org/apache/avro/Interop.class\")).file\n        projectFile(buildOutputClassPath(\"org/apache/avro/Kind.class\")).file\n        projectFile(buildOutputClassPath(\"org/apache/avro/MD5.class\")).file\n        projectFile(buildOutputClassPath(\"org/apache/avro/Node.class\")).file\n    }\n\n    def \"gives a meaningful error message when presented a malformed schema file\"() {\n        given:\n        copyResource(\"enumMalformed.avsc\", avroDir)\n        def errorFilePath = new File(\"src/main/avro/enumMalformed.avsc\").path\n\n        when:\n        def result = runAndFail()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == FAILED\n        result.output.contains(\"> Could not resolve schema definition files:\")\n        result.output.contains(\"* $errorFilePath: \\\"enum\\\" is not a defined name. The type of the \\\"gender\\\" \" +\n                \"field must be a defined name or a {\\\"type\\\": ...} expression.\")\n    }\n\n    @SuppressWarnings([\"GStringExpressionWithinString\"])\n    def \"avro plugin correctly uses task configuration avoidance\"() {\n        given:\n        buildFile << \"\"\"\n        |def configuredTasks = []\n        |tasks.configureEach {\n        |    println \"Configured task: \\${it.path}\"\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = run(\"help\")\n\n        then:\n        def expectedConfiguredTasks = [\":help\"]\n        if (GradleFeatures.configCache.isSupportedBy(gradleVersion)) {\n            // Not sure why, but when configuration caching was introduced, the base plugin started configuring the\n            // clean task even if it wasn't called.\n            expectedConfiguredTasks << \":clean\"\n        }\n        def actualConfiguredTasks = []\n        result.output.findAll(/(?m)^Configured task: (.*)$/) { match, taskPath -> actualConfiguredTasks << taskPath }\n        actualConfiguredTasks == expectedConfiguredTasks\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/AvroPluginSpec.groovy",
    "content": "/*\n * Copyright © 2013-2019 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro\n\nimport org.gradle.api.Project\nimport org.gradle.testfixtures.ProjectBuilder\nimport spock.lang.Specification\n\nclass AvroPluginSpec extends Specification {\n    Project project = ProjectBuilder.builder().build()\n\n    def \"avro protocol generation tasks are registered\"() {\n        when: \"the plugin is applied to a project\"\n        project.apply(plugin: AvroPlugin)\n\n        then: \"avro protocol generation tasks are registered with the project with appropriate configuration\"\n        project.tasks.withType(GenerateAvroProtocolTask)*.name.sort() == [\"generateAvroProtocol\", \"generateTestAvroProtocol\"]\n        mainGenerateAvroProtoTask.description == \"Generates main Avro protocol definition files from IDL files.\"\n        testGenerateAvroProtoTask.description == \"Generates test Avro protocol definition files from IDL files.\"\n        mainGenerateAvroProtoTask.group == Constants.GROUP_SOURCE_GENERATION\n        testGenerateAvroProtoTask.group == Constants.GROUP_SOURCE_GENERATION\n        // Can't easily test the sources\n        mainGenerateAvroProtoTask.outputDir.get().asFile == project.file(\"build/generated-main-avro-avpr\")\n        testGenerateAvroProtoTask.outputDir.get().asFile == project.file(\"build/generated-test-avro-avpr\")\n    }\n\n    def \"avro java generation tasks are registered\"() {\n        when: \"the plugin is applied to a project\"\n        project.apply(plugin: AvroPlugin)\n\n        then: \"avro java generation tasks are registered with the project with appropriate configuration\"\n        project.tasks.withType(GenerateAvroJavaTask)*.name.sort() == [\"generateAvroJava\", \"generateTestAvroJava\"]\n        mainGenerateAvroJavaTask.description == \"Generates main Avro Java source files from schema/protocol definition files.\"\n        testGenerateAvroJavaTask.description == \"Generates test Avro Java source files from schema/protocol definition files.\"\n        mainGenerateAvroJavaTask.group == Constants.GROUP_SOURCE_GENERATION\n        testGenerateAvroJavaTask.group == Constants.GROUP_SOURCE_GENERATION\n        // Can't easily test the sources\n        mainGenerateAvroJavaTask.outputDir.get().asFile == project.file(\"build/generated-main-avro-java\")\n        testGenerateAvroJavaTask.outputDir.get().asFile == project.file(\"build/generated-test-avro-java\")\n    }\n\n    OutputDirTask getTask(String name) {\n        return project.tasks.getByName(name) as OutputDirTask\n    }\n\n    OutputDirTask getMainGenerateAvroProtoTask() {\n        return getTask(\"generateAvroProtocol\")\n    }\n\n    OutputDirTask getTestGenerateAvroProtoTask() {\n        return getTask(\"generateTestAvroProtocol\")\n    }\n\n    OutputDirTask getMainGenerateAvroJavaTask() {\n        return getTask(\"generateAvroJava\")\n    }\n\n    OutputDirTask getTestGenerateAvroJavaTask() {\n        return getTask(\"generateTestAvroJava\")\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/AvroUtilsSpec.groovy",
    "content": "package com.github.davidmc24.gradle.plugin.avro\n\nimport org.apache.avro.Protocol\nimport org.apache.avro.Schema\nimport spock.lang.Specification\nimport spock.lang.Subject\nimport spock.lang.Unroll\n\nimport static Constants.PROTOCOL_EXTENSION\nimport static Constants.SCHEMA_EXTENSION\n\n@Subject(AvroUtils)\nclass AvroUtilsSpec extends Specification {\n    private static final String EMPTY_STRING = \"\"\n    private static final String SINGLE_LEVEL_NAMESPACE = \"avro\"\n    private static final String MULTI_LEVEL_NAMESPACE = \"org.example\"\n    private static final String MULTI_LEVEL_NAMESPACE_PATH = \"org/example\"\n    private static final String SCHEMA_NAME = \"SchemaName\"\n    private static final String PROTOCOL_NAME = \"ProtocolName\"\n\n    @SuppressWarnings(\"ParameterName\")\n    @Unroll\n    def \"assemblePath rejects unnamed arguments (#arg)\"(def arg, def _) {\n        when:\n        //noinspection GroovyAssignabilityCheck\n        AvroUtils.assemblePath(arg)\n        then:\n        def ex = thrown(IllegalArgumentException)\n        ex.message == \"Path cannot be assembled for nameless objects\"\n        where:\n        arg                                    | _\n        createSchema(null, null, true)         | _\n        createSchema(null, EMPTY_STRING, true) | _\n        createProtocol(null, EMPTY_STRING)     | _\n    }\n\n    @Unroll\n    def \"assemblePath(#arg)\"(def arg, String expectedPath) {\n        when:\n        //noinspection GroovyAssignabilityCheck\n        def actualPath = AvroUtils.assemblePath(arg)\n        then:\n        actualPath == expectedPath\n        where:\n        arg                                                   | expectedPath\n        createSchema(null, SCHEMA_NAME)                       | \"${SCHEMA_NAME}.${SCHEMA_EXTENSION}\"\n        createSchema(EMPTY_STRING, SCHEMA_NAME)               | \"${SCHEMA_NAME}.${SCHEMA_EXTENSION}\"\n        createSchema(SINGLE_LEVEL_NAMESPACE, SCHEMA_NAME)     | \"${SINGLE_LEVEL_NAMESPACE}/${SCHEMA_NAME}.${SCHEMA_EXTENSION}\"\n        createSchema(MULTI_LEVEL_NAMESPACE, SCHEMA_NAME)      | \"${MULTI_LEVEL_NAMESPACE_PATH}/${SCHEMA_NAME}.${SCHEMA_EXTENSION}\"\n        createProtocol(null, PROTOCOL_NAME)                   | \"${PROTOCOL_NAME}.${PROTOCOL_EXTENSION}\"\n        createProtocol(EMPTY_STRING, PROTOCOL_NAME)           | \"${PROTOCOL_NAME}.${PROTOCOL_EXTENSION}\"\n        createProtocol(SINGLE_LEVEL_NAMESPACE, PROTOCOL_NAME) | \"${SINGLE_LEVEL_NAMESPACE}/${PROTOCOL_NAME}.${PROTOCOL_EXTENSION}\"\n        createProtocol(MULTI_LEVEL_NAMESPACE, PROTOCOL_NAME)  | \"${MULTI_LEVEL_NAMESPACE_PATH}/${PROTOCOL_NAME}.${PROTOCOL_EXTENSION}\"\n    }\n\n    Schema createSchema(String namespace, String name, boolean disableNameValidation = false) {\n        if (disableNameValidation) {\n            Schema.validateNames.set(false)\n        }\n        def schema = Schema.createRecord(name, null, namespace, false, Collections.emptyList())\n        if (disableNameValidation) {\n            Schema.validateNames.set(true)\n        }\n        return schema\n    }\n\n    Protocol createProtocol(String namespace, String name) {\n        return new Protocol(name, null, namespace)\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/BuildCacheSupportFunctionalSpec.groovy",
    "content": "/*\n * Copyright © 2018 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro\n\nimport spock.lang.IgnoreIf\nimport spock.util.environment.OperatingSystem\n\nimport static org.gradle.testkit.runner.TaskOutcome.FROM_CACHE\n\n/**\n * Testing for <a href=\"https://docs.gradle.org/current/userguide/build_cache.html\">Build Cache</a> feature support.\n */\nclass BuildCacheSupportFunctionalSpec extends FunctionalSpec {\n    def \"setup\"() {\n        applyAvroPlugin()\n        addDefaultRepository()\n        addAvroDependency()\n    }\n\n    def \"supports build cache for schema/protocol java source generation\"() {\n        given: \"a project is built once with build cache enabled\"\n        copyResource(\"user.avsc\", avroDir)\n        copyResource(\"mail.avpr\", avroDir)\n        addAvroIpcDependency()\n        run(\"build\", \"--build-cache\")\n\n        and: \"the project is cleaned\"\n        run(\"clean\")\n\n        when: \"the project is built again with build cache enabled\"\n        def result = run(\"build\", \"--build-cache\")\n\n        then: \"the expected outputs were produced from the build cache\"\n        result.task(\":generateAvroJava\").outcome == FROM_CACHE\n        result.task(\":compileJava\").outcome == FROM_CACHE\n        projectFile(\"build/generated-main-avro-java/example/avro/User.java\").file\n        projectFile(\"build/generated-main-avro-java/org/apache/avro/test/Mail.java\").file\n        projectFile(buildOutputClassPath(\"example/avro/User.class\")).file\n        projectFile(buildOutputClassPath(\"org/apache/avro/test/Mail.class\")).file\n    }\n\n    /**\n     * This test appears to fail on Windows due to clean being unable to delete interop.avpr.\n     */\n    @IgnoreIf({ OperatingSystem.current.windows })\n    def \"supports build cache for IDL to protocol conversion\"() {\n        given: \"a project is built once with build cache enabled\"\n        copyResource(interopIDLResourceName, avroDir)\n        run(\"build\", \"--build-cache\")\n\n        and: \"the project is cleaned\"\n        run(\"clean\")\n\n        when: \"the project is built again with build cache enabled\"\n        def result = run(\"build\", \"--build-cache\")\n\n        then: \"the expected outputs were produced from the build cache\"\n        result.task(\":generateAvroProtocol\").outcome == FROM_CACHE\n        result.task(\":generateAvroJava\").outcome == FROM_CACHE\n        result.task(\":compileJava\").outcome == FROM_CACHE\n        projectFile(\"build/generated-main-avro-avpr/org/apache/avro/InteropProtocol.avpr\").file\n        projectFile(\"build/generated-main-avro-java/org/apache/avro/Interop.java\").file\n        projectFile(buildOutputClassPath(\"org/apache/avro/Interop.class\")).file\n    }\n\n    def \"supports build cache for protocol to schema conversion\"() {\n        given: \"a project is built once with build cache enabled\"\n        copyResource(\"mail.avpr\", avroDir)\n        buildFile << \"\"\"\n        |tasks.register(\"generateSchema\", com.github.davidmc24.gradle.plugin.avro.GenerateAvroSchemaTask) {\n        |    source file(\"src/main/avro\")\n        |    include(\"**/*.avpr\")\n        |    outputDir = file(\"build/generated-main-avro-avsc\")\n        |}\n        |\"\"\".stripMargin()\n        run(\"generateSchema\", \"--build-cache\")\n\n        and: \"the project is cleaned\"\n        run(\"clean\")\n\n        when: \"the project is built again with build cache enabled\"\n        def result = run(\"generateSchema\", \"--build-cache\")\n\n        then: \"the expected outputs were produced from the build cache\"\n        result.task(\":generateSchema\").outcome == FROM_CACHE\n        projectFile(\"build/generated-main-avro-avsc/org/apache/avro/test/Message.avsc\").file\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/CustomConversionFunctionalSpec.groovy",
    "content": "/*\n * Copyright © 2019 David M. Carr\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 */\npackage com.github.davidmc24.gradle.plugin.avro\n\nimport static org.gradle.testkit.runner.TaskOutcome.SUCCESS\n\nclass CustomConversionFunctionalSpec extends FunctionalSpec {\n    private void copyCustomConversion(String destDir) {\n        copyFile(\"src/test/java\", destDir,\n            \"com/github/davidmc24/gradle/plugin/avro/test/custom/TimeZoneConversion.java\")\n        copyFile(\"src/test/java\", destDir,\n            \"com/github/davidmc24/gradle/plugin/avro/test/custom/TimeZoneLogicalType.java\")\n        copyFile(\"src/test/java\", destDir,\n            \"com/github/davidmc24/gradle/plugin/avro/test/custom/TimeZoneLogicalTypeFactory.java\")\n    }\n\n    def \"can use a custom conversion when generating java from a schema with stringType = \\\"String\\\"\"() {\n        // since Avro 1.9.2 https://issues.apache.org/jira/browse/AVRO-2548 is fixed\n        // This is a behavior of the buildscript version of avro rather than the compile-time one,\n        // so our version compatibility tests won't cover the difference\n        given:\n        copyResource(\"customConversion.avsc\", avroDir)\n        // This functionality doesn't work with the plugins DSL syntax.\n        // To load files from the buildSrc classpath you need to load the plugin from the buildscript classpath.\n        buildFile << \"\"\"\n        |buildscript {\n        |    dependencies {\n        |        classpath files(${readPluginClasspath()})\n        |    }\n        |}\n        |apply plugin: \"com.github.davidmc24.gradle.plugin.avro\"\n        |import com.github.davidmc24.gradle.plugin.avro.test.custom.*\n        |avro {\n        |    stringType = \"String\"\n        |    logicalTypeFactory(\"timezone\", TimeZoneLogicalTypeFactory)\n        |    customConversion(TimeZoneConversion)\n        |}\n        |\"\"\".stripMargin()\n        addDefaultRepository()\n        addAvroDependency()\n        projectFile(\"buildSrc/build.gradle\") << \"\"\"\n        |repositories {\n        |    mavenCentral()\n        |}\n        |dependencies {\n        |    implementation \"org.apache.avro:avro:${avroVersion}\"\n        |}\n        |\"\"\".stripMargin()\n        copyCustomConversion(\"buildSrc/src/main/java\")\n        copyCustomConversion(\"src/main/java\")\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"test/Event.class\")).file\n        def javaSource = projectFile(\"build/generated-main-avro-java/test/Event.java\").text\n        javaSource.contains(\"java.time.Instant start;\")\n        javaSource.contains(\"java.util.TimeZone timezone;\")\n    }\n\n    def \"can use a custom conversion when generating java from a schema\"() {\n        // As of Avro 1.9.1, custom conversions have an undesirable interaction with stringType=String.\n        // See https://issues.apache.org/jira/browse/AVRO-2548\n        given:\n        copyResource(\"customConversion.avsc\", avroDir)\n        // This functionality doesn't work with the plugins DSL syntax.\n        // To load files from the buildSrc classpath you need to load the plugin from the buildscript classpath.\n        buildFile << \"\"\"\n        |buildscript {\n        |    dependencies {\n        |        classpath files(${readPluginClasspath()})\n        |    }\n        |}\n        |apply plugin: \"com.github.davidmc24.gradle.plugin.avro\"\n        |import com.github.davidmc24.gradle.plugin.avro.test.custom.*\n        |avro {\n        |    stringType = \"CharSequence\"\n        |    logicalTypeFactory(\"timezone\", TimeZoneLogicalTypeFactory)\n        |    customConversion(TimeZoneConversion)\n        |}\n        |\"\"\".stripMargin()\n        addDefaultRepository()\n        addAvroDependency()\n        projectFile(\"buildSrc/build.gradle\") << \"\"\"\n        |repositories {\n        |    mavenCentral()\n        |}\n        |dependencies {\n        |    implementation \"org.apache.avro:avro:${avroVersion}\"\n        |}\n        |\"\"\".stripMargin()\n        copyCustomConversion(\"buildSrc/src/main/java\")\n        copyCustomConversion(\"src/main/java\")\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"test/Event.class\")).file\n        def javaSource = projectFile(\"build/generated-main-avro-java/test/Event.java\").text\n        javaSource.contains(\"java.time.Instant start;\")\n        javaSource.contains(\"java.util.TimeZone timezone;\")\n    }\n\n    def \"can use a custom conversion when generating java from a protocol\"() {\n        // As of Avro 1.9.1, custom conversions have an undesirable interaction with stringType=String.\n        // See https://issues.apache.org/jira/browse/AVRO-2548\n        given:\n        copyResource(\"customConversion.avpr\", avroDir)\n        // This functionality doesn't work with the plugins DSL syntax.\n        // To load files from the buildSrc classpath you need to load the plugin from the buildscript classpath.\n        buildFile << \"\"\"\n        |buildscript {\n        |    dependencies {\n        |        classpath files(${readPluginClasspath()})\n        |    }\n        |}\n        |apply plugin: \"com.github.davidmc24.gradle.plugin.avro\"\n        |import com.github.davidmc24.gradle.plugin.avro.test.custom.*\n        |avro {\n        |    stringType = \"CharSequence\"\n        |    logicalTypeFactory(\"timezone\", TimeZoneLogicalTypeFactory)\n        |    customConversion(TimeZoneConversion)\n        |}\n        |\"\"\".stripMargin()\n        addDefaultRepository()\n        addAvroDependency()\n        projectFile(\"buildSrc/build.gradle\") << \"\"\"\n        |repositories {\n        |    mavenCentral()\n        |}\n        |dependencies {\n        |    implementation \"org.apache.avro:avro:${avroVersion}\"\n        |}\n        |\"\"\".stripMargin()\n        copyCustomConversion(\"buildSrc/src/main/java\")\n        copyCustomConversion(\"src/main/java\")\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"test/Event.class\")).file\n        def javaSource = projectFile(\"build/generated-main-avro-java/test/Event.java\").text\n        javaSource.contains(\"java.time.Instant start;\")\n        javaSource.contains(\"java.util.TimeZone timezone;\")\n    }\n\n    def \"can use a custom conversion from outside of the build classpath when generating java from a protocol\"() {\n        given:\n        copyResource(\"customConversion.avpr\", avroDir)\n        applyAvroPlugin()\n        buildFile << \"\"\"\n        |configurations {\n        |    customConversions\n        |    implementation.extendsFrom(customConversions)\n        |}\n        |dependencies {\n        |    customConversions(project(\":custom-conversions\"))\n        |}\n        |avro {\n        |    stringType = \"CharSequence\"\n        |    conversionsAndTypeFactoriesClasspath.from(configurations.customConversions)\n        |    logicalTypeFactory(\"timezone\", \"com.github.davidmc24.gradle.plugin.avro.test.custom.TimeZoneLogicalTypeFactory\")\n        |    customConversion(\"com.github.davidmc24.gradle.plugin.avro.test.custom.TimeZoneConversion\")\n        |}\n        |\"\"\".stripMargin()\n        addDefaultRepository()\n        addAvroDependency()\n        projectFile(\"custom-conversions/build.gradle\") << \"\"\"\n        |plugins {\n        |   id \"java-library\"\n        |}\n        |repositories {\n        |    mavenCentral()\n        |}\n        |dependencies {\n        |    implementation \"org.apache.avro:avro:${avroVersion}\"\n        |}\n        |\"\"\".stripMargin()\n        projectFile(\"settings.gradle\") << \"\"\"\n        |include(\"custom-conversions\")\n        |\"\"\".stripMargin()\n        copyCustomConversion(\"custom-conversions/src/main/java\")\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"test/Event.class\")).file\n        def javaSource = projectFile(\"build/generated-main-avro-java/test/Event.java\").text\n        javaSource.contains(\"java.time.Instant start;\")\n        javaSource.contains(\"java.util.TimeZone timezone;\")\n    }\n\n    def \"can use a custom logical type while generating a schema from a protocol\"() {\n        given:\n        copyResource(\"customConversion.avpr\", avroDir)\n        applyAvroPlugin()\n        buildFile << \"\"\"\n        |tasks.register(\"generateSchema\", com.github.davidmc24.gradle.plugin.avro.GenerateAvroSchemaTask) {\n        |    source file(\"src/main/avro\")\n        |    include(\"**/*.avpr\")\n        |    outputDir = file(\"build/generated-main-avro-avsc\")\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = run(\"generateSchema\")\n\n        then:\n        result.task(\":generateSchema\").outcome == SUCCESS\n        def schemaFile = projectFile(\"build/generated-main-avro-avsc/test/Event.avsc\").text\n        schemaFile.contains('\"logicalType\" : \"timestamp-millis\"')\n        schemaFile.contains('\"logicalType\" : \"timezone\"')\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/DuplicateHandlingFunctionalSpec.groovy",
    "content": "/*\n * Copyright © 2015-2016 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro\n\nimport static org.gradle.testkit.runner.TaskOutcome.FAILED\nimport static org.gradle.testkit.runner.TaskOutcome.SUCCESS\n\n/**\n * Functional tests related to handling of duplicate type definitions.\n *\n * <p>This situation is generally encountered when schema files define records with inline record/enum definitions, and those inline types\n * are used in more than one file.</p>\n */\nclass DuplicateHandlingFunctionalSpec extends FunctionalSpec {\n    def \"setup\"() {\n        applyAvroPlugin()\n        addDefaultRepository()\n        addAvroDependency()\n    }\n\n    def \"Duplicate record definition succeeds if definition identical\"() {\n        given:\n        copyIdenticalRecord()\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"example/Person.class\")).file\n        projectFile(buildOutputClassPath(\"example/Fish.class\")).file\n        projectFile(buildOutputClassPath(\"example/Gender.class\")).file\n    }\n\n    def \"Duplicate enum definition succeeds if definition identical\"() {\n        given:\n        copyIdenticalEnum()\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"example/Person.class\")).file\n        projectFile(buildOutputClassPath(\"example/Cat.class\")).file\n        projectFile(buildOutputClassPath(\"example/Gender.class\")).file\n    }\n\n    def \"Duplicate fixed definition succeeds if definition identical\"() {\n        given:\n        copyIdenticalFixed()\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"example/ContainsFixed1.class\")).file\n        projectFile(buildOutputClassPath(\"example/ContainsFixed2.class\")).file\n        projectFile(buildOutputClassPath(\"example/Picture.class\")).file\n    }\n\n    def \"Duplicate record definition fails if definition differs\"() {\n        given:\n        copyDifferentRecord()\n        def errorFilePath1 = new File(\"src/main/avro/duplicate/Person.avsc\").path\n        def errorFilePath2 = new File(\"src/main/avro/duplicate/Spider.avsc\").path\n        when:\n        def result = runAndFail()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == FAILED\n        result.output.contains(\"Found conflicting definition of type example.Person in \"\n            + \"[$errorFilePath1, $errorFilePath2]\")\n    }\n\n    def \"Duplicate enum definition fails if definition differs\"() {\n        given:\n        copyDifferentEnum()\n        def errorFilePath1 = new File(\"src/main/avro/duplicate/Dog.avsc\").path\n        def errorFilePath2 = new File(\"src/main/avro/duplicate/Person.avsc\").path\n\n        when:\n        def result = runAndFail()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == FAILED\n        result.output.contains(\"Found conflicting definition of type example.Gender in \"\n            + \"[$errorFilePath1, $errorFilePath2]\")\n    }\n\n    def \"Duplicate fixed definition fails if definition differs\"() {\n        given:\n        copyDifferentFixed()\n        def errorFilePath1 = new File(\"src/main/avro/duplicate/ContainsFixed1.avsc\").path\n        def errorFilePath2 = new File(\"src/main/avro/duplicate/ContainsFixed3.avsc\").path\n\n        when:\n        def result = runAndFail()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == FAILED\n        result.output.contains(\"Found conflicting definition of type example.Picture in \"\n            + \"[$errorFilePath1, $errorFilePath2]\")\n    }\n\n    def \"Duplicate record definition in single file fails with clear error\"() {\n        given:\n        copyResource(\"duplicate/duplicateInSingleFile.avsc\", avroDir)\n        def errorFilePath = new File(\"src/main/avro/duplicate/duplicateInSingleFile.avsc\").path\n\n        when:\n        def result = runAndFail()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == FAILED\n        result.output.contains(\"Failed to resolve schema definition file $errorFilePath; \" +\n            \"contains duplicate type definition example.avro.date\")\n    }\n\n    private void copyIdenticalRecord() {\n        copyResource(\"duplicate/Person.avsc\", avroDir)\n        copyResource(\"duplicate/Fish.avsc\", avroDir)\n    }\n\n    private void copyIdenticalEnum() {\n        copyResource(\"duplicate/Person.avsc\", avroDir)\n        copyResource(\"duplicate/Cat.avsc\", avroDir)\n    }\n\n    private void copyIdenticalFixed() {\n        copyResource(\"duplicate/ContainsFixed1.avsc\", avroDir)\n        copyResource(\"duplicate/ContainsFixed2.avsc\", avroDir)\n    }\n\n    private void copyDifferentRecord() {\n        copyResource(\"duplicate/Person.avsc\", avroDir)\n        copyResource(\"duplicate/Spider.avsc\", avroDir)\n    }\n\n    private void copyDifferentEnum() {\n        copyResource(\"duplicate/Person.avsc\", avroDir)\n        copyResource(\"duplicate/Dog.avsc\", avroDir)\n    }\n\n    private void copyDifferentFixed() {\n        copyResource(\"duplicate/ContainsFixed1.avsc\", avroDir)\n        copyResource(\"duplicate/ContainsFixed3.avsc\", avroDir)\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/EncodingFunctionalSpec.groovy",
    "content": "/*\n * Copyright © 2015-2016 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro\n\nimport spock.lang.Unroll\n\nimport java.nio.charset.Charset\n\nimport static org.gradle.testkit.runner.TaskOutcome.SUCCESS\n\nclass EncodingFunctionalSpec extends FunctionalSpec {\n    private static final List<String> LANGUAGES = [\"alemán\", \"chino\", \"español\", \"francés\", \"inglés\", \"japonés\"]\n    /* Not all encodings have the characters needed for the test file, and not all encoding may be supported by any given JRE */\n    private static final List<String> AVAILABLE_ENCODINGS =\n        [\"UTF-8\", \"UTF-16\", \"UTF-32\", \"windows-1252\", \"X-MacRoman\"].findAll { Charset.isSupported(it) }\n    private static final String SYSTEM_ENCODING = Charset.defaultCharset().name()\n\n    def \"with convention plugin, default encoding matches default compilation behavior\"() {\n        given:\n        applyAvroPlugin()\n        addDefaultRepository()\n        addAvroDependency()\n        copyResource(\"idioma.avsc\", avroDir)\n\n        when:\n        def result = run()\n\n        then: \"compilation succeeds\"\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n\n        and: \"the system default encoding is used\"\n        def content = projectFile(\"build/generated-main-avro-java/example/avro/Idioma.java\").getText(SYSTEM_ENCODING)\n        LANGUAGES.collect { content.contains(it) }.every { it }\n    }\n\n    @Unroll\n    def \"with convention plugin, configuring Java compilation task with encoding=#encoding will use it for outputCharacterEncoding\"() {\n        given:\n        applyAvroPlugin()\n        addDefaultRepository()\n        addAvroDependency()\n        copyResource(\"idioma.avsc\", avroDir)\n        buildFile << \"\"\"\n        |tasks.named(\"compileJava\").configure {\n        |    options.encoding = '${encoding}'\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = run()\n\n        then: \"compilation succeeds\"\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n\n        and: \"the specified encoding is used\"\n        def content = projectFile(\"build/generated-main-avro-java/example/avro/Idioma.java\").getText(encoding)\n        LANGUAGES.collect { content.contains(it) }.every { it }\n\n        where:\n        encoding << AVAILABLE_ENCODINGS\n    }\n\n    @Unroll\n    def \"with base plugin, configuring outputCharacterEncoding=#outputCharacterEncoding is supported\"() {\n        given:\n        applyAvroBasePlugin()\n        copyResource(\"idioma.avsc\", avroDir)\n        buildFile << \"\"\"\n        |avro {\n        |    outputCharacterEncoding = ${outputCharacterEncoding}\n        |}\n        |tasks.register(\"generateAvroJava\", com.github.davidmc24.gradle.plugin.avro.GenerateAvroJavaTask) {\n        |    source file(\"src/main/avro\")\n        |    include(\"**/*.avsc\")\n        |    outputDir = file(\"build/generated-main-avro-java\")\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = run(\"generateAvroJava\")\n\n        then: \"compilation succeeds\"\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n\n        and: \"the specified encoding is used\"\n        def content = projectFile(\"build/generated-main-avro-java/example/avro/Idioma.java\").getText(expectedEncoding)\n        LANGUAGES.collect { content.contains(it) }.every { it }\n\n        where:\n        outputCharacterEncoding                      | expectedEncoding\n        \"'UTF-16'\"                                   | \"UTF-16\"\n        \"'utf-8'\"                                    | \"UTF-8\"\n        \"java.nio.charset.Charset.forName('UTF-16')\" | \"UTF-16\"\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/EnumHandlingFunctionalSpec.groovy",
    "content": "/*\n * Copyright © 2015-2016 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro\n\nimport static org.gradle.testkit.runner.TaskOutcome.SUCCESS\n\n/**\n * Functional tests relating to handling of enums.\n */\nclass EnumHandlingFunctionalSpec extends FunctionalSpec {\n    def \"setup\"() {\n        applyAvroPlugin()\n        addDefaultRepository()\n        addAvroDependency()\n    }\n\n    def \"supports simple enums\"() {\n        given:\n        copyResource(\"enumSimple.avsc\", avroDir)\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"example/avro/MyEnum.class\")).file\n    }\n\n    def \"supports enums defined within a record field\"() {\n        given:\n        copyResource(\"enumField.avsc\", avroDir)\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"example/avro/Test.class\")).file\n        projectFile(buildOutputClassPath(\"example/avro/Gender.class\")).file\n    }\n\n    def \"supports enums defined within a union\"() {\n        given:\n        copyResource(\"enumUnion.avsc\", avroDir)\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"example/avro/Test.class\")).file\n        projectFile(buildOutputClassPath(\"example/avro/Kind.class\")).file\n    }\n\n    def \"supports using enums defined in a separate schema file\"() {\n        given:\n        copyResource(\"enumSimple.avsc\", avroDir)\n        copyResource(\"enumUseSimple.avsc\", avroDir)\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"example/avro/User.class\")).file\n        projectFile(buildOutputClassPath(\"example/avro/MyEnum.class\")).file\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/ExamplesFunctionalSpec.groovy",
    "content": "/*\n * Copyright © 2018 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro\n\nimport static org.gradle.testkit.runner.TaskOutcome.SUCCESS\n\nclass ExamplesFunctionalSpec extends FunctionalSpec {\n    def \"setup\"() {\n        applyAvroPlugin()\n        addDefaultRepository()\n        addAvroDependency()\n    }\n\n    def \"inline example is valid\"() {\n        given:\n        copyResource(\"/examples/inline/Cat.avsc\", avroDir)\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"example/Cat.class\")).file\n        projectFile(buildOutputClassPath(\"example/Breed.class\")).file\n    }\n\n    def \"separate example is valid\"() {\n        given:\n        copyResource(\"/examples/separate/Breed.avsc\", avroDir)\n        copyResource(\"/examples/separate/Cat.avsc\", avroDir)\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"example/Cat.class\")).file\n        projectFile(buildOutputClassPath(\"example/Breed.class\")).file\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/FunctionalSpec.groovy",
    "content": "/*\n * Copyright © 2015-2018 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro\n\nimport com.vdurmont.semver4j.Semver\nimport org.gradle.testkit.runner.BuildResult\nimport org.gradle.testkit.runner.GradleRunner\nimport org.gradle.util.GradleVersion\nimport spock.lang.Specification\nimport spock.lang.TempDir\n\n@SuppressWarnings([\"Println\"])\nabstract class FunctionalSpec extends Specification {\n    protected Semver getAvroVersion() {\n        def version = System.getProperty(\"avroVersion\")\n        if (!version) {\n            throw new IllegalArgumentException(\"avroVersion project property is required\")\n        }\n        return new Semver(version)\n    }\n    protected GradleVersion getGradleVersion() {\n        def version = System.getProperty(\"gradleVersion\")\n        if (!version) {\n            throw new IllegalArgumentException(\"gradleVersion project property is required\")\n        }\n        return GradleVersion.version(version)\n    }\n\n    @TempDir\n    File testProjectDir\n\n    File buildFile\n    File avroDir\n    File avroSubDir\n\n    def setup() {\n        println \"Testing using Avro version ${avroVersion}.\"\n        println \"Testing using Gradle version ${gradleVersion}.\"\n\n        buildFile = projectFile(\"build.gradle\")\n        avroDir = projectFile(\"src/main/avro\")\n        avroSubDir = projectFile(\"src/main/avro/foo\")\n    }\n\n    protected String readPluginClasspath() {\n        def pluginClasspathResource = getClass().classLoader.findResource(\"plugin-classpath.txt\")\n        if (pluginClasspathResource == null) {\n            throw new IllegalStateException(\"Did not find plugin classpath resource, run `testClasses` build task.\")\n        }\n\n        // escape backslashes in Windows paths and assemble\n        return pluginClasspathResource.readLines()*.replace('\\\\', '\\\\\\\\').collect { \"\\\"$it\\\"\" }.join(\", \")\n    }\n\n    protected void applyAvroPlugin() {\n        applyPlugin(\"com.github.davidmc24.gradle.plugin.avro\")\n    }\n\n    protected void applyAvroBasePlugin() {\n        applyPlugin(\"com.github.davidmc24.gradle.plugin.avro-base\")\n    }\n\n    protected void applyPlugin(String pluginId) {\n        buildFile << \"plugins { id \\\"${pluginId}\\\" }\\n\"\n    }\n\n    protected void applyPlugin(String pluginId, String version) {\n        buildFile << \"plugins { id \\\"${pluginId}\\\" version \\\"${version}\\\" }\\n\"\n    }\n\n    protected void addDefaultRepository() {\n        buildFile << \"repositories { mavenCentral() }\\n\"\n    }\n\n    protected void addImplementationDependency(String dependencySpec) {\n        addDependency(\"implementation\", dependencySpec)\n    }\n\n    protected void addRuntimeDependency(String dependencySpec) {\n        addDependency(\"runtimeOnly\", dependencySpec)\n    }\n\n    protected void addDependency(String configuration, String dependencySpec) {\n        buildFile << \"dependencies { ${configuration} \\\"${dependencySpec}\\\" }\\n\"\n    }\n\n    protected void addAvroDependency() {\n        addImplementationDependency(\"org.apache.avro:avro:${avroVersion}\")\n    }\n\n    protected void addAvroIpcDependency() {\n        addImplementationDependency(\"org.apache.avro:avro-ipc:${avroVersion}\")\n    }\n\n    protected void copyResource(String name, File targetFolder) {\n        copyResource(name, targetFolder, name)\n    }\n\n    protected void copyResource(String name, File targetFolder, String targetName) {\n        def resource = getClass().getResourceAsStream(name)\n        def file = new File(targetFolder, targetName)\n        if (resource == null) {\n            throw new FileNotFoundException(\"Could not resource with name ${name}\")\n        }\n        file.parentFile.mkdirs()\n        file << getClass().getResourceAsStream(name)\n    }\n\n    protected void copyFile(String srcDir, String destDir, String path) {\n        def destFile = new File(projectFile(destDir), path)\n        def srcFile = new File(srcDir, path)\n        destFile.parentFile.mkdirs()\n        destFile << srcFile.bytes\n    }\n\n    protected File projectFile(String path) {\n        File file = new File(testProjectDir, path)\n        file.parentFile.mkdirs()\n        return file\n    }\n\n    protected File projectFolder(String path) {\n        File file = new File(testProjectDir, path)\n        file.mkdirs()\n        return file\n    }\n\n    protected GradleRunner createGradleRunner() {\n//        // Set up code coverage reporting based on https://github.com/koral--/jacoco-gradle-testkit-plugin\n//        copyResource(\"/testkit-gradle.properties\", testProjectDir, \"gradle.properties\")\n        return GradleRunner.create().withProjectDir(testProjectDir).withGradleVersion(gradleVersion.version).withPluginClasspath()\n    }\n\n    protected BuildResult run(String... args = [\"build\"]) {\n        return createGradleRunner().withArguments(determineGradleArguments(args)).build()\n    }\n\n    protected BuildResult runAndFail(String... args = [\"build\"]) {\n        return createGradleRunner().withArguments(determineGradleArguments(args)).buildAndFail()\n    }\n\n    protected String buildOutputClassPath(String suffix) {\n        return \"build/classes/java/main/${suffix}\"\n    }\n\n    private List<String> determineGradleArguments(String... args) {\n        def arguments = [\"--stacktrace\"]\n        arguments.addAll(Arrays.asList(args))\n        if (GradleFeatures.configCache.isSupportedBy(gradleVersion) && !arguments.contains(\"--no-configuration-cache\")) {\n            arguments << \"--configuration-cache\"\n        }\n        return arguments\n    }\n\n    protected boolean isLocalTimestampConversionSupported() {\n        return avroVersion.isGreaterThanOrEqualTo(new Semver(\"1.10.0\"))\n    }\n\n    protected String getInteropIDLResourceName() {\n        return localTimestampConversionSupported ? \"interop.avdl\" : \"interop-1.9.avdl\"\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/GenerateAvroProtocolTaskFunctionalSpec.groovy",
    "content": "/*\n * Copyright © 2019 David M. Carr\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 */\npackage com.github.davidmc24.gradle.plugin.avro\n\nimport spock.lang.Subject\n\nimport static org.gradle.testkit.runner.TaskOutcome.SUCCESS\n\n@Subject(GenerateAvroProtocolTask)\nclass GenerateAvroProtocolTaskFunctionalSpec extends FunctionalSpec {\n    def \"With base plugin, declares input on classpath\"() {\n        given: \"a build that declares another task's output in the classpath\"\n        applyAvroBasePlugin()\n        applyPlugin(\"java\") // Jar task appears to only work with the java plugin applied\n        buildFile << \"\"\"\n        |configurations.create(\"shared\")\n        |tasks.register(\"sharedIdlJar\", Jar) {\n        |    from \"src/shared\"\n        |}\n        |dependencies {\n        |    shared sharedIdlJar.outputs.files  \n        |}\n        |tasks.register(\"generateProtocol\", com.github.davidmc24.gradle.plugin.avro.GenerateAvroProtocolTask) {\n        |    classpath = configurations.shared\n        |    source file(\"src/dependent\")\n        |    outputDir = file(\"build/protocol\")\n        |}\n        |\"\"\".stripMargin()\n\n        copyResource(\"shared.avdl\", projectFolder(\"src/shared\"))\n        copyResource(\"dependent.avdl\", projectFolder(\"src/dependent\"))\n\n        when: \"running the task\"\n        def result = run(\"generateProtocol\")\n\n        then: \"running the generate protocol task occurs after running the producing task\"\n        result.tasks*.path == [\":sharedIdlJar\", \":generateProtocol\"]\n        result.task(\":generateProtocol\").outcome == SUCCESS\n        projectFile(\"build/protocol/com/example/dependent/DependentProtocol.avpr\").file\n    }\n\n    def \"With avro plugin, declares input on classpath (runtime configuration by default)\"() {\n        given: \"a build that declares another task's output in the classpath\"\n        applyAvroPlugin()\n        buildFile << \"\"\"\n        |tasks.register(\"sharedIdlJar\", Jar) {\n        |    from \"src/shared\"\n        |}\n        |dependencies {\n        |    runtimeOnly sharedIdlJar.outputs.files  \n        |}\n        |\"\"\".stripMargin()\n\n        copyResource(\"shared.avdl\", projectFolder(\"src/shared\"))\n        copyResource(\"dependent.avdl\", avroDir)\n\n        when: \"running the task\"\n        def result = run(\"generateAvroProtocol\")\n\n        then: \"running the generate protocol task occurs after running the producing task\"\n        result.tasks*.path == [\":sharedIdlJar\", \":generateAvroProtocol\"]\n        result.task(\":generateAvroProtocol\").outcome == SUCCESS\n        projectFile(\"build/generated-main-avro-avpr/com/example/dependent/DependentProtocol.avpr\").file\n    }\n\n    def \"supports files with the same name in different directories\"() {\n        given: \"a project with two IDL files with the same name, but in different directories\"\n        applyAvroPlugin()\n\n        copyResource(\"namespaced-idl/v1/test.avdl\", projectFolder(\"src/main/avro/v1\"))\n        copyResource(\"namespaced-idl/v2/test.avdl\", projectFolder(\"src/main/avro/v2\"))\n\n        when: \"running the task\"\n        def result = run(\"generateAvroProtocol\")\n\n        then: \"avpr files are generated for each IDL file\"\n        result.task(\":generateAvroProtocol\").outcome == SUCCESS\n        projectFile(\"build/generated-main-avro-avpr/org/example/v1/TestProtocol.avpr\").file\n        projectFile(\"build/generated-main-avro-avpr/org/example/v2/TestProtocol.avpr\").file\n    }\n\n    def \"fails if avpr will be overwritten\"() {\n        given: \"a project with two IDL files with the same protocol name and namespace\"\n        applyAvroPlugin()\n\n        copyResource(\"namespaced-idl/v1/test.avdl\", projectFolder(\"src/main/avro/v1\"))\n        copyResource(\"namespaced-idl/v1/test_same_protocol.avdl\", projectFolder(\"src/main/avro/v1\"))\n\n        when: \"running the task\"\n        run(\"generateAvroProtocol\")\n\n        then:\n        def ex = thrown(Exception)\n        ex.message.contains(\"Failed to compile IDL file\")\n        ex.message.contains(\"File already processed with same namespace and protocol name.\")\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/IntellijFunctionalSpec.groovy",
    "content": "/*\n * Copyright © 2018 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro\n\nimport org.gradle.testkit.runner.BuildResult\n\nimport static org.gradle.testkit.runner.TaskOutcome.SUCCESS\n\nclass IntellijFunctionalSpec extends FunctionalSpec {\n    def \"setup\"() {\n        applyAvroPlugin()\n        applyPlugin(\"idea\")\n    }\n\n    def \"generated intellij project files include source directories for generated source\"() {\n        given:\n        copyResource(\"user.avsc\", avroDir)\n        projectFolder(\"src/main/java\")\n        projectFolder(\"src/test/java\")\n        projectFolder(\"src/test/avro\")\n\n        when:\n        runIdea()\n\n        then:\n        def moduleFile = projectFile(\"${testProjectDir.name}.iml\")\n        def module = new XmlSlurper().parseText(moduleFile.text)\n        module.component.content.sourceFolder.findAll { it.@isTestSource.text() == \"false\" }.@url*.text().sort() == [\n            'file://$MODULE_DIR\\$/build/generated-main-avro-java',\n            'file://$MODULE_DIR\\$/src/main/avro', 'file://$MODULE_DIR\\$/src/main/java',\n        ]\n        module.component.content.sourceFolder.findAll { it.@isTestSource.text() == \"true\" }.@url*.text().sort() == [\n            'file://$MODULE_DIR\\$/build/generated-test-avro-java',\n            'file://$MODULE_DIR\\$/src/test/avro', 'file://$MODULE_DIR\\$/src/test/java',\n        ]\n    }\n\n    def \"generated output directories are created by default\"() {\n        when:\n        def result = runIdea()\n\n        then:\n        result.task(\":idea\").outcome == SUCCESS\n        projectFile(\"build/generated-main-avro-java\").directory\n        projectFile(\"build/generated-test-avro-java\").directory\n    }\n\n    def \"overriding task's outputDir doesn't result in default directory still being created\"() {\n        given:\n        buildFile << \"\"\"\n        |tasks.named(\"generateAvroJava\").configure {\n        |    outputDir = file(\"build/generatedMainAvro\")\n        |}\n        |tasks.named(\"generateTestAvroJava\").configure {\n        |    outputDir = file(\"build/generatedTestAvro\")\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = runIdea()\n\n        then:\n        result.task(\":idea\").outcome == SUCCESS\n        !projectFile(\"build/generated-main-avro-java\").directory\n        !projectFile(\"build/generated-test-avro-java\").directory\n        projectFile(\"build/generatedMainAvro\").directory\n        projectFile(\"build/generatedTestAvro\").directory\n    }\n\n    private BuildResult runIdea() {\n        def args = [\"idea\"]\n        if (GradleFeatures.configCache.isSupportedBy(gradleVersion)) {\n            // As of Gradle 6.7.1, idea plugin doesn't support configuration cache yet.\n            // Thus, don't try to use it in this spec.\n            args << \"--no-configuration-cache\"\n        }\n        return run(args as String[])\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/KotlinDSLCompatibilityFunctionalSpec.groovy",
    "content": "package com.github.davidmc24.gradle.plugin.avro\n\nimport static org.gradle.testkit.runner.TaskOutcome.SUCCESS\n\nclass KotlinDSLCompatibilityFunctionalSpec extends FunctionalSpec {\n    File kotlinBuildFile\n\n    def \"setup\"() {\n        buildFile.delete() // Don't use the Groovy build file created by the superclass\n        kotlinBuildFile = projectFile(\"build.gradle.kts\")\n        kotlinBuildFile << \"\"\"\n        |plugins {\n        |    java\n        |    id(\"com.github.davidmc24.gradle.plugin.avro\")\n        |}\n        |repositories {\n        |    mavenCentral()\n        |}\n        |dependencies {\n        |    implementation(\"org.apache.avro:avro:${avroVersion}\")\n        |}\n        |\"\"\".stripMargin()\n    }\n\n    def \"works with kotlin DSL\"() {\n        given:\n        copyResource(\"user.avsc\", avroDir)\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"example/avro/User.class\")).file\n    }\n\n    def \"extension supports configuring all supported properties\"() {\n        given:\n        copyResource(\"user.avsc\", avroDir)\n        kotlinBuildFile << \"\"\"\n        |avro {\n        |    isCreateSetters.set(true)\n        |    isCreateOptionalGetters.set(false)\n        |    isGettersReturnOptional.set(false)\n        |    isOptionalGettersForNullableFieldsOnly.set(false)\n        |    fieldVisibility.set(\"PUBLIC\")\n        |    outputCharacterEncoding.set(\"UTF-8\")\n        |    stringType.set(\"String\")\n        |    templateDirectory.set(null as String?)\n        |    isEnableDecimalLogicalType.set(true)\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = run()\n\n        then:\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        result.task(\":compileJava\").outcome == SUCCESS\n        projectFile(buildOutputClassPath(\"example/avro/User.class\")).file\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/OptionsFunctionalSpec.groovy",
    "content": "/*\n * Copyright © 2015-2016 Commerce Technologies, LLC.\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 */\npackage com.github.davidmc24.gradle.plugin.avro\n\nimport com.github.davidmc24.gradle.plugin.avro.test.custom.CommentGenerator\nimport com.github.davidmc24.gradle.plugin.avro.test.custom.TimestampGenerator\nimport org.apache.avro.compiler.specific.SpecificCompiler.FieldVisibility\nimport org.apache.avro.generic.GenericData.StringType\nimport spock.lang.Unroll\n\nimport java.nio.ByteBuffer\n\nimport static org.gradle.testkit.runner.TaskOutcome.FAILED\nimport static org.gradle.testkit.runner.TaskOutcome.SUCCESS\n\n/**\n * Functional tests for most functions.  Encoding tests have been pulled out into {@link EncodingFunctionalSpec}\n */\nclass OptionsFunctionalSpec extends FunctionalSpec {\n\n    def \"works with default options\"() {\n        given:\n        copyResource(\"user.avsc\", avroDir)\n        applyAvroPlugin()\n\n        when:\n        def result = run(\"generateAvroJava\")\n\n        then: \"the task succeeds\"\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        def content = projectFile(\"build/generated-main-avro-java/example/avro/User.java\").text\n\n        and: \"the stringType is string\"\n        content.contains(\"public java.lang.String getName()\")\n\n        and: \"the fieldVisibility is PRIVATE\"\n        content.contains(\"private java.lang.String name;\")\n\n        and: \"the default template is used\"\n        !content.contains(\"Custom template\")\n\n        and: \"createSetters is enabled\"\n        content.contains(\"public void setName(java.lang.String value)\")\n\n        and: \"createOptionalGetters is disabled\"\n        !content.contains(\"Optional\")\n\n        and: \"gettersReturnOptional is disabled\"\n        !content.contains(\"Optional\")\n\n        and: \"enableDecimalLogicalType is enabled\"\n        content.contains(\"public void setSalary(${BigDecimal.name} value)\")\n    }\n\n    @Unroll\n    def \"supports configuring stringType to #stringType\"() {\n        given:\n        copyResource(\"user.avsc\", avroDir)\n        applyAvroPlugin()\n        buildFile << \"\"\"\n        |avro {\n        |    stringType = ${stringType}\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = run(\"generateAvroJava\")\n\n        then: \"the task succeeds\"\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        def content = projectFile(\"build/generated-main-avro-java/example/avro/User.java\").text\n        def mainClassContent = getMainClassContent(content)\n\n        and: \"the specified stringType is used\"\n        mainClassContent.contains(expectedContent)\n\n        where:\n        stringType                                     | expectedContent\n        \"'${StringType.String.name()}'\"                | \"public java.lang.String getName()\"\n        \"'${StringType.CharSequence.name()}'\"          | \"public java.lang.CharSequence getName()\"\n        \"'${StringType.Utf8.name()}'\"                  | \"public org.apache.avro.util.Utf8 getName()\"\n        \"'${StringType.Utf8.name().toUpperCase()}'\"    | \"public org.apache.avro.util.Utf8 getName()\"\n        \"'${StringType.Utf8.name().toLowerCase()}'\"    | \"public org.apache.avro.util.Utf8 getName()\"\n        \"${StringType.name}.${StringType.Utf8.name()}\" | \"public org.apache.avro.util.Utf8 getName()\"\n    }\n\n    @Unroll\n    def \"supports configuring fieldVisibility to #fieldVisibility\"() {\n        given:\n        copyResource(\"user.avsc\", avroDir)\n        applyAvroPlugin()\n        buildFile << \"\"\"\n        |avro {\n        |    fieldVisibility = \"${fieldVisibility}\"\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = run(\"generateAvroJava\")\n\n        then: \"the task succeeds\"\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        def content = projectFile(\"build/generated-main-avro-java/example/avro/User.java\").text\n        def mainClassContent = getMainClassContent(content)\n\n        and: \"the specified fieldVisibility is used\"\n        mainClassContent.contains(expectedContent)\n\n        where:\n        fieldVisibility                              | expectedContent\n        FieldVisibility.PRIVATE.name().toLowerCase() | \"private java.lang.String name;\"\n        FieldVisibility.PRIVATE.name()               | \"private java.lang.String name;\"\n        FieldVisibility.PUBLIC.name()                | \"public java.lang.String name;\"\n    }\n\n    @Unroll\n    def \"supports configuring createSetters to #createSetters\"() {\n        given:\n        copyResource(\"user.avsc\", avroDir)\n        applyAvroPlugin()\n        buildFile << \"\"\"\n        |avro {\n        |    createSetters = ${createSetters}\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = run(\"generateAvroJava\")\n\n        then: \"the task succeeds\"\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        def content = projectFile(\"build/generated-main-avro-java/example/avro/User.java\").text\n        def mainClassContent = getMainClassContent(content)\n\n        and: \"the specified createSetters is used\"\n        mainClassContent.contains(\"public void setName(java.lang.String value)\") == expectedPresent\n\n        where:\n        createSetters   | expectedPresent\n        \"Boolean.TRUE\"  | true\n        \"Boolean.FALSE\" | false\n        \"true\"          | true\n        \"false\"         | false\n        \"'true'\"        | true\n        \"'false'\"       | false\n    }\n\n    @Unroll\n    def \"supports configuring createOptionalGetters to #createOptionalGetters\"() {\n        given:\n        copyResource(\"user.avsc\", avroDir)\n        applyAvroPlugin()\n        buildFile << \"\"\"\n        |avro {\n        |    createOptionalGetters = ${createOptionalGetters}\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = run(\"generateAvroJava\")\n\n        then: \"the task succeeds\"\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        def content = projectFile(\"build/generated-main-avro-java/example/avro/User.java\").text\n        def mainClassContent = getMainClassContent(content)\n\n        and: \"the nullable getter is generated\"\n        mainClassContent.contains(\"public java.lang.String getFavoriteColor()\")\n\n        and: \"the specified createOptionalGetters is used\"\n        mainClassContent.contains(\"public Optional<java.lang.String> getOptionalFavoriteColor()\") == expectedPresent\n\n        where:\n        createOptionalGetters | expectedPresent\n        \"Boolean.TRUE\"        | true\n        \"Boolean.FALSE\"       | false\n        \"true\"                | true\n        \"false\"               | false\n        \"'true'\"              | true\n        \"'false'\"             | false\n    }\n\n    @SuppressWarnings(\"LineLength\")\n    @Unroll\n    def \"supports configuring gettersReturnOptional/optionalGettersForNullableFieldsOnly to #gettersReturnOptional/#optionalGettersForNullableFieldsOnly\"() {\n        given:\n        copyResource(\"user.avsc\", avroDir)\n        applyAvroPlugin()\n        buildFile << \"\"\"\n        |avro {\n        |    gettersReturnOptional = ${gettersReturnOptional}\n        |    optionalGettersForNullableFieldsOnly = ${optionalGettersForNullableFieldsOnly}\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = run(\"generateAvroJava\")\n\n        then: \"the task succeeds\"\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        def content = projectFile(\"build/generated-main-avro-java/example/avro/User.java\").text\n        def mainClassContent = getMainClassContent(content)\n\n        and: \"the specified optionalGettersForNullableFieldsOnly is used\"\n        mainClassContent.contains(\"public Optional<java.lang.String> getFavoriteColor()\") == expectedNullableOptionalGetter\n        mainClassContent.contains(\"public java.lang.String getFavoriteColor()\") != expectedNullableOptionalGetter\n        mainClassContent.contains(\"public Optional<java.lang.String> getName()\") == expectedRequiredOptionalGetter\n        mainClassContent.contains(\"public java.lang.String getName()\") != expectedRequiredOptionalGetter\n\n\n        where:\n        gettersReturnOptional | optionalGettersForNullableFieldsOnly | expectedNullableOptionalGetter | expectedRequiredOptionalGetter\n        \"Boolean.TRUE\"        | \"Boolean.TRUE\"                       | true                           | false\n        \"Boolean.TRUE\"        | \"Boolean.FALSE\"                      | true                           | true\n        \"Boolean.FALSE\"       | \"Boolean.TRUE\"                       | false                          | false\n        \"Boolean.FALSE\"       | \"Boolean.FALSE\"                      | false                          | false\n        \"true\"                | \"true\"                               | true                           | false\n        \"true\"                | \"false\"                              | true                           | true\n        \"false\"               | \"true\"                               | false                          | false\n        \"false\"               | \"false\"                              | false                          | false\n        \"'true'\"              | \"'true'\"                             | true                           | false\n        \"'true'\"              | \"'false'\"                            | true                           | true\n        \"'false'\"             | \"'true'\"                             | false                          | false\n        \"'false'\"             | \"'false'\"                            | false                          | false\n    }\n\n    def \"supports configuring templateDirectory\"() {\n        given:\n        def templatesDir = projectFolder(\"templates/alternateTemplates\")\n        copyResource(\"user.avsc\", avroDir)\n        copyResource(\"record.vm\", templatesDir)\n        // This functionality doesn't work with the plugins DSL syntax.\n        // To load files from the buildscript classpath you need to load the plugin from it as well.\n        buildFile << \"\"\"\n        |buildscript {\n        |    dependencies {\n        |        classpath files(${readPluginClasspath()})\n        |        classpath files([\"${templatesDir.parentFile.toURI()}\"])\n        |    }\n        |}\n        |apply plugin: \"com.github.davidmc24.gradle.plugin.avro\"\n        |avro {\n        |    templateDirectory = \"/alternateTemplates/\"\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = run(\"generateAvroJava\")\n\n        then: \"the task succeeds\"\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        def content = projectFile(\"build/generated-main-avro-java/example/avro/User.java\").text\n\n        and: \"the specified templates are used\"\n        content.contains(\"Custom template\")\n    }\n\n    def \"supports configuring velocity tool classes\"() {\n        given:\n        copyAvroTools(\"buildSrc/src/main/java\")\n        copyAvroTools(\"src/main/java\")\n        def templatesDir = projectFolder(\"templates/alternateTemplates\")\n        copyResource(\"user.avsc\", avroDir)\n        copyResource(\"record-tools.vm\", templatesDir, \"record.vm\")\n        // This functionality doesn't work with the plugins DSL syntax.\n        // To load files from the buildscript classpath you need to load the plugin from it as well.\n        buildFile << \"\"\"\n        |buildscript {\n        |    dependencies {\n        |        classpath files(${readPluginClasspath()})\n        |        classpath files([\"${templatesDir.parentFile.toURI()}\"])\n        |    }\n        |}\n        |apply plugin: \"com.github.davidmc24.gradle.plugin.avro\"\n        |avro {\n        |    templateDirectory = \"/alternateTemplates/\"\n        |    additionalVelocityToolClasses = ['com.github.davidmc24.gradle.plugin.avro.test.custom.TimestampGenerator',\n        |                                     'com.github.davidmc24.gradle.plugin.avro.test.custom.CommentGenerator']\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = run(\"generateAvroJava\")\n\n        then: \"the task succeeds\"\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        def content = projectFile(\"build/generated-main-avro-java/example/avro/User.java\").text\n\n        and: \"the velocity tools have been applied\"\n        content.contains(CommentGenerator.CUSTOM_COMMENT)\n        content.contains(TimestampGenerator.MESSAGE_PREFIX)\n    }\n\n    def \"rejects unsupported stringType values\"() {\n        given:\n        copyResource(\"user.avsc\", avroDir)\n        applyAvroPlugin()\n        buildFile << \"\"\"\n        |avro {\n        |    stringType = \"badValue\"\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = runAndFail(\"generateAvroJava\")\n\n        then:\n        result.task(\":generateAvroJava\").outcome == FAILED\n        result.output.contains(\"Invalid stringType 'badValue'.  Value values are: [CharSequence, String, Utf8]\")\n    }\n\n    def \"rejects unsupported fieldVisibility values\"() {\n        given:\n        copyResource(\"user.avsc\", avroDir)\n        applyAvroPlugin()\n        buildFile << \"\"\"\n        |avro {\n        |    fieldVisibility = \"badValue\"\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = runAndFail(\"generateAvroJava\")\n\n        then:\n        result.task(\":generateAvroJava\").outcome == FAILED\n        result.output.contains(\"Invalid fieldVisibility 'badValue'.  Value values are: [PUBLIC, PRIVATE]\")\n    }\n\n    @Unroll\n    def \"supports configuring enableDecimalLogicalType to #enableDecimalLogicalType\"() {\n        given:\n        copyResource(\"user.avsc\", avroDir)\n        applyAvroPlugin()\n        buildFile << \"\"\"\n        |avro {\n        |    enableDecimalLogicalType = $enableDecimalLogicalType\n        |}\n        |\"\"\".stripMargin()\n\n        when:\n        def result = run(\"generateAvroJava\")\n\n        then: \"the task succeeds\"\n        result.task(\":generateAvroJava\").outcome == SUCCESS\n        def content = projectFile(\"build/generated-main-avro-java/example/avro/User.java\").text\n        def mainClassContent = getMainClassContent(content)\n\n        and: \"the specified enableDecimalLogicalType is used\"\n        mainClassContent.contains(\"public void setSalary(${fieldClz.name} value)\")\n\n        where:\n        enableDecimalLogicalType | fieldClz\n        \"Boolean.TRUE\"           | BigDecimal\n        \"Boolean.FALSE\"          | ByteBuffer\n        \"true\"                   | BigDecimal\n        \"false\"                  | ByteBuffer\n        \"'true'\"                 | BigDecimal\n        \"'false'\"                | ByteBuffer\n    }\n\n    /**\n     * Returns just the portion of a file that relates to the main class.\n     * This is used in order to allow assertions on the getters/setters/fields of the generated class itself, as opposed to a Builder.\n     *\n     * @param content the file content for which to get the main content\n     * @return the content of the class, from the start of the class body to the first inner class definition\n     */\n    @SuppressWarnings(\"LineLength\")\n    private static String getMainClassContent(String content) {\n        def className = \"User\"\n        def matcher = content =~ /(?s)public class ${className} extends org\\.apache\\.avro\\.specific\\.\\SpecificRecordBase implements org\\.apache\\.avro\\.specific\\.SpecificRecord \\{(?<mainClassContent>.*)public static class Builder/\n        assert matcher.find()\n        return matcher.group(\"mainClassContent\")\n    }\n\n    private void copyAvroTools(String destDir) {\n        copyFile(\"src/test/java\", destDir,\n                \"com/github/davidmc24/gradle/plugin/avro/test/custom/CommentGenerator.java\")\n        copyFile(\"src/test/java\", destDir,\n                \"com/github/davidmc24/gradle/plugin/avro/test/custom/TimestampGenerator.java\")\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/ResolveAvroDependenciesTaskFunctionalSpec.groovy",
    "content": "package com.github.davidmc24.gradle.plugin.avro\n\nimport org.hamcrest.MatcherAssert\nimport spock.lang.Subject\nimport uk.co.datumedge.hamcrest.json.SameJSONAs\n\nimport static org.gradle.testkit.runner.TaskOutcome.SUCCESS\n\n@Subject(ResolveAvroDependenciesTask)\nclass ResolveAvroDependenciesTaskFunctionalSpec extends FunctionalSpec {\n    def \"resolves dependencies\"() {\n        def srcDir = projectFolder(\"src/avro/normalized\")\n\n        given: \"a build with the task declared\"\n        applyAvroBasePlugin()\n        buildFile << \"\"\"\n        |tasks.register(\"resolveAvroDependencies\", com.github.davidmc24.gradle.plugin.avro.ResolveAvroDependenciesTask) {\n        |    source file(\"src/avro/normalized\")\n        |    outputDir = file(\"build/avro/resolved\")\n        |}\n        |\"\"\".stripMargin()\n\n        and: \"some normalized schema files\"\n        copyResource(\"/examples/separate/Breed.avsc\", srcDir)\n        copyResource(\"/examples/separate/Cat.avsc\", srcDir)\n\n        when: \"running the task\"\n        def result = run(\"resolveAvroDependencies\")\n\n        then: \"the resolved schema files are generated\"\n        result.task(\":resolveAvroDependencies\").outcome == SUCCESS\n        MatcherAssert.assertThat(\n            projectFile(\"build/avro/resolved/example/Cat.avsc\").text,\n            SameJSONAs.sameJSONAs(getClass().getResourceAsStream(\"/examples/inline/Cat.avsc\").text))\n        MatcherAssert.assertThat(\n            projectFile(\"build/avro/resolved/example/Breed.avsc\").text,\n            SameJSONAs.sameJSONAs(getClass().getResourceAsStream(\"/examples/separate/Breed.avsc\").text))\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/SchemaResolverSpec.groovy",
    "content": "package com.github.davidmc24.gradle.plugin.avro\n\nimport org.gradle.api.GradleException\nimport org.gradle.api.Project\nimport org.gradle.testfixtures.ProjectBuilder\nimport spock.lang.Specification\nimport spock.lang.Unroll\n\nclass SchemaResolverSpec extends Specification {\n    private Project project\n    private SchemaResolver resolver\n\n    def setup() {\n        project = ProjectBuilder.builder().build()\n        resolver = new SchemaResolver(project.layout, project.logger)\n    }\n\n    @Unroll\n    def \"Can resolve records that use a separate record type (#resourceNames)\"(List<String> resourceNames) {\n        when:\n        def files = resourceNames.collect { new File(\"src/test/resources/resolver/${it}\") }\n        def processingState = resolver.resolve(files)\n\n        then:\n        noExceptionThrown()\n        processingState.failedFiles.empty\n        processingState.processedTotal == resourceNames.size()\n\n        where:\n        resourceNames << (\n            [\"SimpleRecord.avsc\", \"UseRecord.avsc\"].permutations()\n            + [\"SimpleRecord.avsc\", \"UseRecordWithType.avsc\"].permutations()\n        )\n    }\n\n    @Unroll\n    def \"Can resolve records that use a separate enum type (#resourceNames)\"(List<String> resourceNames) {\n        when:\n        def files = resourceNames.collect { new File(\"src/test/resources/resolver/${it}\") }\n        def processingState = resolver.resolve(files)\n\n        then:\n        noExceptionThrown()\n        processingState.failedFiles.empty\n        processingState.processedTotal == resourceNames.size()\n\n        where:\n        resourceNames << (\n            [\"SimpleEnum.avsc\", \"UseEnum.avsc\"].permutations()\n            + [\"SimpleEnum.avsc\", \"UseEnumWithType.avsc\"].permutations()\n        )\n    }\n\n    @Unroll\n    def \"Can resolve records that use a separate fixed type (#resourceNames)\"(List<String> resourceNames) {\n        when:\n        def files = resourceNames.collect { new File(\"src/test/resources/resolver/${it}\") }\n        def processingState = resolver.resolve(files)\n\n        then:\n        noExceptionThrown()\n        processingState.failedFiles.empty\n        processingState.processedTotal == resourceNames.size()\n\n        where:\n        resourceNames << (\n            [\"SimpleFixed.avsc\", \"UseFixed.avsc\"].permutations()\n            + [\"SimpleFixed.avsc\", \"UseFixedWithType.avsc\"].permutations()\n        )\n    }\n\n    @Unroll\n    def \"Can resolve records that use a separate type in an array (#resourceNames)\"(List<String> resourceNames) {\n        when:\n        def files = resourceNames.collect { new File(\"src/test/resources/resolver/${it}\") }\n        def processingState = resolver.resolve(files)\n\n        then:\n        noExceptionThrown()\n        processingState.failedFiles.empty\n        processingState.processedTotal == resourceNames.size()\n\n        where:\n        resourceNames << (\n            [\"SimpleEnum.avsc\", \"SimpleRecord.avsc\", \"SimpleFixed.avsc\", \"UseArray.avsc\"].permutations()\n            + [\"SimpleEnum.avsc\", \"SimpleRecord.avsc\", \"SimpleFixed.avsc\", \"UseArrayWithType.avsc\"].permutations()\n        )\n    }\n\n    @Unroll\n    def \"Can resolve records that use a separate type in an map value (#resourceNames)\"(List<String> resourceNames) {\n        when:\n        def files = resourceNames.collect { new File(\"src/test/resources/resolver/${it}\") }\n        def processingState = resolver.resolve(files)\n\n        then:\n        noExceptionThrown()\n        processingState.failedFiles.empty\n        processingState.processedTotal == resourceNames.size()\n\n        where:\n        resourceNames << (\n            [\"SimpleEnum.avsc\", \"SimpleRecord.avsc\", \"SimpleFixed.avsc\", \"UseMap.avsc\"].permutations()\n            + [\"SimpleEnum.avsc\", \"SimpleRecord.avsc\", \"SimpleFixed.avsc\", \"UseMapWithType.avsc\"].permutations()\n        )\n    }\n\n    def \"Duplicate record definition succeeds if definition identical\"() {\n        given:\n        def resourceNames = [\"Person.avsc\", \"Fish.avsc\"]\n        def files = resourceNames.collect { new File(\"src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/${it}\") }\n\n        when:\n        def processingState = resolver.resolve(files)\n\n        then:\n        noExceptionThrown()\n        processingState.failedFiles.empty\n        processingState.processedTotal == files.size()\n    }\n\n    def \"Duplicate enum definition succeeds if definition identical\"() {\n        given:\n        def resourceNames = [\"Person.avsc\", \"Cat.avsc\"]\n        def files = resourceNames.collect { new File(\"src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/${it}\") }\n\n        when:\n        def processingState = resolver.resolve(files)\n\n        then:\n        noExceptionThrown()\n        processingState.failedFiles.empty\n        processingState.processedTotal == files.size()\n    }\n\n    def \"Duplicate fixed definition succeeds if definition identical\"() {\n        given:\n        def resourceNames = [\"ContainsFixed1.avsc\", \"ContainsFixed2.avsc\"]\n        def files = resourceNames.collect { new File(\"src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/${it}\") }\n\n        when:\n        def processingState = resolver.resolve(files)\n\n        then:\n        noExceptionThrown()\n        processingState.failedFiles.empty\n        processingState.processedTotal == files.size()\n    }\n\n    def \"Duplicate record definition fails if definition differs\"() {\n        given:\n        def resourceNames = [\"Person.avsc\", \"Spider.avsc\"]\n        def files = resourceNames.collect { new File(\"src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/${it}\") }\n\n        when:\n        resolver.resolve(files)\n\n        then:\n        def ex = thrown(GradleException)\n        ex.message == \"Found conflicting definition of type example.Person in [${files[0].path}, ${files[1].path}]\"\n    }\n\n    def \"Duplicate enum definition fails if definition differs\"() {\n        given:\n        def resourceNames = [\"Dog.avsc\", \"Person.avsc\"]\n        def files = resourceNames.collect { new File(\"src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/${it}\") }\n\n        when:\n        resolver.resolve(files)\n\n        then:\n        def ex = thrown(GradleException)\n        ex.message == \"Found conflicting definition of type example.Gender in [${files[0].path}, ${files[1].path}]\"\n    }\n\n    def \"Duplicate fixed definition fails if definition differs\"() {\n        given:\n        def resourceNames = [\"ContainsFixed1.avsc\", \"ContainsFixed3.avsc\"]\n        def files = resourceNames.collect { new File(\"src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/${it}\") }\n\n        when:\n        resolver.resolve(files)\n\n        then:\n        def ex = thrown(GradleException)\n        ex.message == \"Found conflicting definition of type example.Picture in [${files[0].path}, ${files[1].path}]\"\n    }\n\n    def \"Duplicate record definition in single file fails with clear error\"() {\n        given:\n        def file = new File(\"src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/duplicateInSingleFile.avsc\")\n\n        when:\n        resolver.resolve([file])\n\n        then:\n        def ex = thrown(GradleException)\n        ex.message == \"Failed to resolve schema definition file ${file.path}; contains duplicate type definition example.avro.date\"\n    }\n}\n"
  },
  {
    "path": "src/test/groovy/com/github/davidmc24/gradle/plugin/avro/StringsSpec.groovy",
    "content": "package com.github.davidmc24.gradle.plugin.avro\n\nimport spock.lang.Specification\nimport spock.lang.Subject\nimport spock.lang.Unroll\n\n@Subject(Strings)\nclass StringsSpec extends Specification {\n    @Unroll\n    def \"isEmpty(#str)\"() {\n        when:\n        def actual = Strings.isEmpty(str)\n        then:\n        actual == expected\n        where:\n        str   | expected\n        null  | true\n        \"\"    | true\n        \" \"   | false\n        \"abc\" | false\n    }\n\n    @Unroll\n    def \"isNotEmpty(#str)\"() {\n        when:\n        def actual = Strings.isNotEmpty(str)\n        then:\n        actual == expected\n        where:\n        str   | expected\n        null  | false\n        \"\"    | false\n        \" \"   | true\n        \"abc\" | true\n    }\n\n    @Unroll\n    def \"when not empty, requireNotEmpty returns argument (#str)\"() {\n        def message = \"testMessage\"\n        expect:\n        Strings.requireNotEmpty(str, message) == str\n        where:\n        str << [\" \", \"abc\"]\n    }\n\n    @Unroll\n    def \"when empty, requireNotEmpty throws exception (#str)\"() {\n        def message = \"testMessage\"\n        when:\n        Strings.requireNotEmpty(str, message)\n        then:\n        def ex = thrown(IllegalArgumentException)\n        ex.message == message\n        where:\n        str << [null, \"\"]\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/github/davidmc24/gradle/plugin/avro/test/custom/CommentGenerator.java",
    "content": "package com.github.davidmc24.gradle.plugin.avro.test.custom;\n\npublic class CommentGenerator {\n\n    public static final String CUSTOM_COMMENT = \"/**  Custom generated comment. */\";\n\n    public String generateComment() {\n        return CommentGenerator.CUSTOM_COMMENT;\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/com/github/davidmc24/gradle/plugin/avro/test/custom/TimeZoneConversion.java",
    "content": "/*\n * Copyright © 2019 David M. Carr\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 */\npackage com.github.davidmc24.gradle.plugin.avro.test.custom;\n\nimport java.util.TimeZone;\nimport org.apache.avro.Conversion;\nimport org.apache.avro.LogicalType;\nimport org.apache.avro.Schema;\n\n@SuppressWarnings(\"unused\")\npublic class TimeZoneConversion extends Conversion<TimeZone> {\n    public static final String LOGICAL_TYPE_NAME = \"timezone\";\n\n    @Override\n    public Class<TimeZone> getConvertedType() {\n        return TimeZone.class;\n    }\n\n    @Override\n    public String getLogicalTypeName() {\n        return LOGICAL_TYPE_NAME;\n    }\n\n    @Override\n    public TimeZone fromCharSequence(CharSequence value, Schema schema, LogicalType type) {\n        return TimeZone.getTimeZone(value.toString());\n    }\n\n    @Override\n    public CharSequence toCharSequence(TimeZone value, Schema schema, LogicalType type) {\n        return value.getID();\n    }\n\n    @Override\n    public Schema getRecommendedSchema() {\n        return TimeZoneLogicalType.INSTANCE.addToSchema(Schema.create(Schema.Type.STRING));\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/github/davidmc24/gradle/plugin/avro/test/custom/TimeZoneLogicalType.java",
    "content": "/*\n * Copyright © 2019 David M. Carr\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 */\npackage com.github.davidmc24.gradle.plugin.avro.test.custom;\n\nimport org.apache.avro.LogicalType;\nimport org.apache.avro.Schema;\n\npublic class TimeZoneLogicalType extends LogicalType {\n    static final TimeZoneLogicalType INSTANCE = new TimeZoneLogicalType();\n\n    private TimeZoneLogicalType() {\n        super(TimeZoneConversion.LOGICAL_TYPE_NAME);\n    }\n\n    @Override\n    public void validate(Schema schema) {\n        super.validate(schema);\n        if (schema.getType() != Schema.Type.STRING) {\n            throw new IllegalArgumentException(\"Timezone can only be used with an underlying string type\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/github/davidmc24/gradle/plugin/avro/test/custom/TimeZoneLogicalTypeFactory.java",
    "content": "/*\n * Copyright © 2019 David M. Carr\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 */\npackage com.github.davidmc24.gradle.plugin.avro.test.custom;\n\nimport org.apache.avro.LogicalType;\nimport org.apache.avro.LogicalTypes;\nimport org.apache.avro.Schema;\n\npublic class TimeZoneLogicalTypeFactory implements LogicalTypes.LogicalTypeFactory {\n    @Override\n    public LogicalType fromSchema(Schema schema) {\n        return TimeZoneLogicalType.INSTANCE;\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/github/davidmc24/gradle/plugin/avro/test/custom/TimestampGenerator.java",
    "content": "package com.github.davidmc24.gradle.plugin.avro.test.custom;\n\n\npublic class TimestampGenerator {\n\n    public static final String MESSAGE_PREFIX = \"Current timestamp is\";\n\n    public String generateTimestampMessage() {\n        return String.format(\"%s %s\", MESSAGE_PREFIX,\n                java.time.LocalDateTime.now().format(java.time.format.DateTimeFormatter.ISO_DATE_TIME));\n    }\n\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/Message.avsc",
    "content": "{\n  \"type\" : \"record\",\n  \"name\" : \"Message\",\n  \"namespace\" : \"org.apache.avro.test\",\n  \"fields\" : [ {\n    \"name\" : \"to\",\n    \"type\" : \"string\"\n  }, {\n    \"name\" : \"from\",\n    \"type\" : \"string\"\n  }, {\n    \"name\" : \"body\",\n    \"type\" : \"string\"\n  } ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/customConversion.avpr",
    "content": "{\"namespace\": \"test\",\n \"protocol\": \"CustomConversion\",\n \"types\": [\n     {\"name\": \"Event\", \"type\": \"record\",\n      \"fields\": [\n          {\"name\": \"name\",   \"type\": \"string\"},\n          {\"name\": \"start\", \"type\": {\"type\": \"long\", \"logicalType\": \"timestamp-millis\"} },\n          {\"name\": \"timezone\", \"type\": {\"type\": \"string\", \"logicalType\": \"timezone\"} }\n      ]\n     }\n ],\n \"messages\": { }\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/customConversion.avsc",
    "content": "{\"namespace\": \"test\",\n \"type\": \"record\",\n \"name\": \"Event\",\n \"fields\": [\n     {\"name\": \"name\", \"type\": \"string\"},\n     {\"name\": \"start\", \"type\": {\"type\": \"long\", \"logicalType\": \"timestamp-millis\"} },\n     {\"name\": \"timezone\", \"type\": {\"type\": \"string\", \"logicalType\": \"timezone\"} }\n ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/dependent.avdl",
    "content": "/**\n * Copyright 2019 Paychex, Inc.\n * Licensed pursuant to the terms of the Apache License, Version 2.0 (the \"License\");\n * your use of the Work is subject to the terms and conditions of the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor\n * provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including,\n * without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,\n * MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible\n * for determining the appropriateness of using or redistributing the Work and assume\n * any risks associated with your exercise of permissions under this License.\n */\n\n@namespace(\"com.example.dependent\")\nprotocol DependentProtocol {\n\n  import idl \"shared.avdl\";\n\n  record ThisDependsOnTemporal {\n    com.example.shared.SomethingShared a;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/Cat.avsc",
    "content": "{\n    \"name\": \"Cat\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\": [\n        { \"name\": \"name\", \"type\": \"string\" },\n        {\n            \"name\": \"gender\",\n            \"type\": {\n                \"name\": \"Gender\",\n                \"type\": \"enum\",\n                \"symbols\": [ \"MALE\", \"FEMALE\", \"OTHER\" ]\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/ContainsFixed1.avsc",
    "content": "{\n    \"name\": \"ContainsFixed1\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\": [\n        { \"name\": \"picture\", \"type\": { \"type\": \"fixed\", \"name\": \"Picture\", \"size\": 16 } }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/ContainsFixed2.avsc",
    "content": "{\n    \"name\": \"ContainsFixed2\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\": [\n        { \"name\": \"picture\", \"type\": { \"type\": \"fixed\", \"name\": \"Picture\", \"size\": 16 } }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/ContainsFixed3.avsc",
    "content": "{\n    \"name\": \"ContainsFixed3\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\": [\n        { \"name\": \"picture\", \"type\": { \"type\": \"fixed\", \"name\": \"Picture\", \"size\": 12 } }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/Dog.avsc",
    "content": "{\n    \"name\": \"Dog\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\": [\n        { \"name\": \"name\", \"type\": \"string\" },\n        {\n            \"name\": \"gender\",\n            \"type\": {\n                \"name\": \"Gender\",\n                \"type\": \"enum\",\n                \"symbols\": [ \"MALE\", \"FEMALE\" ]\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/Fish.avsc",
    "content": "{\n    \"name\": \"Fish\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\": [\n        { \"name\": \"name\", \"type\": \"string\" },\n        {\n            \"name\": \"owner\",\n            \"type\": {\n                \"name\": \"Person\",\n                \"type\": \"record\",\n                \"fields\": [\n                    { \"name\": \"name\", \"type\": \"string\" },\n                    {\n                        \"name\": \"gender\",\n                        \"type\": {\n                            \"name\": \"Gender\",\n                            \"type\": \"enum\",\n                            \"symbols\": [ \"MALE\", \"FEMALE\", \"OTHER\" ]\n                        }\n                    }\n                ]\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/Person.avsc",
    "content": "{\n    \"name\": \"Person\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\": [\n        { \"name\": \"name\", \"type\": \"string\" },\n        {\n            \"name\": \"gender\",\n            \"type\": {\n                \"name\": \"Gender\",\n                \"type\": \"enum\",\n                \"symbols\": [ \"MALE\", \"FEMALE\", \"OTHER\" ]\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/Spider.avsc",
    "content": "{\n    \"name\": \"Spider\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\": [\n        { \"name\": \"name\", \"type\": \"string\" },\n        {\n            \"name\": \"owner\",\n            \"type\": {\n                \"name\": \"Person\",\n                \"type\": \"record\",\n                \"fields\": [\n                    { \"name\": \"name\", \"type\": \"string\" }\n                ]\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/duplicate/duplicateInSingleFile.avsc",
    "content": "{\n    \"type\" : \"record\",\n    \"name\" : \"Fail\",\n    \"namespace\" : \"example.avro\",\n    \"fields\" : [ {\n        \"name\" : \"start_date\",\n        \"type\" : {\n            \"type\" : \"record\",\n            \"name\" : \"date\",\n            \"fields\" : [ {\n                \"name\" : \"day\",\n                \"type\" : [\"null\", \"int\"]\n            }, {\n                \"name\" : \"month\",\n                \"type\" : [\"null\", \"int\"]\n            }, {\n                \"name\" : \"year\",\n                \"type\" : [\"null\", \"int\"]\n            } ]\n        }\n    }, {\n        \"name\" : \"end_date\",\n        \"type\" : {\n            \"type\" : \"record\",\n            \"name\" : \"date\",\n            \"fields\" : [ {\n                \"name\" : \"day\",\n                \"type\" : [\"null\", \"int\"]\n            }, {\n                \"name\" : \"month\",\n                \"type\" : [\"null\", \"int\"]\n            }, {\n                \"name\" : \"year\",\n                \"type\" : [\"null\", \"int\"]\n            } ]\n        }\n    } ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/enumField.avsc",
    "content": "{\n    \"namespace\": \"example.avro\",\n    \"type\": \"record\",\n    \"name\": \"Test\",\n    \"fields\": [\n        {\n            \"name\": \"gender\",\n            \"type\": {\n                \"name\": \"Gender\",\n                \"type\": \"enum\",\n                \"symbols\": [\"MALE\", \"FEMALE\"]\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/enumMalformed.avsc",
    "content": "{\n    \"namespace\": \"example.avro\",\n    \"type\": \"record\",\n    \"name\": \"Test\",\n    \"fields\": [\n        {\n            \"name\": \"gender\",\n            \"type\": \"enum\",\n            \"symbols\": [\"MALE\", \"FEMALE\"]\n        }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/enumSimple.avsc",
    "content": "{\n    \"namespace\": \"example.avro\",\n    \"type\": \"enum\",\n    \"symbols\": [\n        \"zero\",\n        \"int\",\n        \"two\",\n        \"three\"\n    ],\n    \"name\": \"MyEnum\"\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/enumUnion.avsc",
    "content": "{\n    \"namespace\": \"example.avro\",\n    \"type\": \"record\",\n    \"name\": \"Test\",\n    \"fields\": [\n        {\n            \"name\": \"kind\",\n            \"type\": [\n                \"null\",\n                {\n                    \"name\": \"Kind\",\n                    \"type\": \"enum\",\n                    \"symbols\": [\"X\", \"Y\", \"Z\"]\n                }\n            ]\n        }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/enumUseSimple.avsc",
    "content": "{\"namespace\": \"example.avro\",\n \"type\": \"record\",\n \"name\": \"User\",\n \"fields\": [\n     {\"name\": \"name\", \"type\": \"string\"},\n     {\"name\": \"kind\", \"type\": \"MyEnum\"}\n ]\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/helloWorld.kt",
    "content": "/*\n * Copyright © 2017 Commerce Technologies, LLC.\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 */\n\npackage demo\n\nimport example.avro.User\nimport java.time.LocalDate\n\nfun main(args : Array<String>) {\n    val user = User(\"David\", 24, \"blue\", null, LocalDate.of(2019, 1, 1))\n    println(\"Hello, ${user.getName()}\")\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/idioma.avsc",
    "content": "{\n    \"namespace\": \"example.avro\",\n    \"type\": \"enum\",\n    \"symbols\": [\n        \"alemán\",\n        \"chino\",\n        \"español\",\n        \"francés\",\n        \"inglés\",\n        \"japonés\"\n    ],\n    \"name\": \"Idioma\"\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/interop-1.9.avdl",
    "content": "/**\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, 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\n// Currently genavro only does Protocols.\n@namespace(\"org.apache.avro\")\nprotocol InteropProtocol {\n  record Foo {\n    string label;\n  }\n\n  enum Kind { A, B, C }\n  fixed MD5(16);\n\n  record Node {\n    string label;\n    array<Node> children = [];\n  }\n\n  record Interop {\n    int intField = 1;\n    long longField = -1;\n    string stringField;\n    boolean boolField = false;\n    float floatField = 0.0;\n    double doubleField = -1.0e12;\n    null nullField;\n    array<double> arrayField = [];\n    map<Foo> mapField;\n    union { boolean, double, array<bytes> } unionField;\n    Kind enumField;\n    MD5 fixedField;\n    Node recordField;\n    decimal(10,2) decimalField;\n    date dateField;\n    time_ms timeField;\n    timestamp_ms timeStampField;\n//  local_timestamp_ms is not supported in Avro 1.9.x\n//    local_timestamp_ms localTimeStampField;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/interop.avdl",
    "content": "/**\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, 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\n// Currently genavro only does Protocols.\n@namespace(\"org.apache.avro\")\nprotocol InteropProtocol {\n  record Foo {\n    string label;\n  }\n\n  enum Kind { A, B, C }\n  fixed MD5(16);\n\n  record Node {\n    string label;\n    array<Node> children = [];\n  }\n\n  record Interop {\n    int intField = 1;\n    long longField = -1;\n    string stringField;\n    boolean boolField = false;\n    float floatField = 0.0;\n    double doubleField = -1.0e12;\n    null nullField;\n    array<double> arrayField = [];\n    map<Foo> mapField;\n    union { boolean, double, array<bytes> } unionField;\n    Kind enumField;\n    MD5 fixedField;\n    Node recordField;\n    decimal(10,2) decimalField;\n    date dateField;\n    time_ms timeField;\n    timestamp_ms timeStampField;\n    local_timestamp_ms localTimeStampField;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/mail.avpr",
    "content": "{\"namespace\": \"org.apache.avro.test\",\n \"protocol\": \"Mail\",\n\n \"types\": [\n     {\"name\": \"Message\", \"type\": \"record\",\n      \"fields\": [\n          {\"name\": \"to\",   \"type\": \"string\"},\n          {\"name\": \"from\", \"type\": \"string\"},\n          {\"name\": \"body\", \"type\": \"string\"}\n      ]\n     }\n ],\n\n \"messages\": {\n     \"send\": {\n         \"request\": [{\"name\": \"message\", \"type\": \"Message\"}],\n         \"response\": \"string\"\n     },\n     \"fireandforget\": {\n         \"request\": [{\"name\": \"message\", \"type\": \"Message\"}],\n         \"response\": \"null\",\n         \"one-way\": true\n     }\n\n }\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/namespaced-idl/v1/test.avdl",
    "content": "@namespace(\"org.example.v1\")\nprotocol TestProtocol {\n    record TestRecord {\n        string field1;\n    }\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/namespaced-idl/v1/test_same_protocol.avdl",
    "content": "@namespace(\"org.example.v1\")\nprotocol TestProtocol {\n    record SomeOtherTestRecord {\n        string field1;\n    }\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/namespaced-idl/v2/test.avdl",
    "content": "@namespace(\"org.example.v2\")\nprotocol TestProtocol {\n    record TestRecord {\n        string field1;\n        string field2;\n    }\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/record-tools.vm",
    "content": "##\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##     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#if ($schema.getNamespace())\npackage $this.mangle($schema.getNamespace());\n#end\n\nimport org.apache.avro.generic.GenericArray;\nimport org.apache.avro.specific.SpecificData;\nimport org.apache.avro.util.Utf8;\n#if (!$schema.isError())\nimport org.apache.avro.message.BinaryMessageEncoder;\nimport org.apache.avro.message.BinaryMessageDecoder;\nimport org.apache.avro.message.SchemaStore;\n#end\n#if (${this.gettersReturnOptional} || ${this.createOptionalGetters})import java.util.Optional;#end\n\n#if ($schema.getDoc())\n/** $schema.getDoc() */\n#end\n#foreach ($annotation in $this.javaAnnotations($schema))\n@$annotation\n#end\n/**\n${timestampgenerator.generateTimestampMessage()}\n*/\n@org.apache.avro.specific.AvroGenerated\npublic class ${this.mangle($schema.getName())}#if ($schema.isError()) extends org.apache.avro.specific.SpecificExceptionBase#else extends org.apache.avro.specific.SpecificRecordBase#end implements org.apache.avro.specific.SpecificRecord {\n  ${commentgenerator.generateComment()}\n  private static final long serialVersionUID = ${this.fingerprint64($schema)}L;\n\n#set ($schemaString = $this.javaSplit($schema.toString()))\n#set ($customLogicalTypeFactories = $this.getUsedCustomLogicalTypeFactories($schema).entrySet())\n#if (!$customLogicalTypeFactories.isEmpty())\n  static {\n#foreach ($customLogicalTypeFactory in $customLogicalTypeFactories)\n    org.apache.avro.LogicalTypes.register(\"${customLogicalTypeFactory.getKey()}\", new ${customLogicalTypeFactory.getValue()}());\n#end\n  }\n#end\n\n  public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse($schemaString);\n  public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }\n\n  private static final SpecificData MODEL$ = new SpecificData();\n#set ($usedConversions = $this.getUsedConversionClasses($schema))\n#if (!$usedConversions.isEmpty())\n  static {\n#foreach ($conversion in $usedConversions)\n    MODEL$.addLogicalTypeConversion(new ${conversion}());\n#end\n  }\n#end\n\n#if (!$schema.isError())\n  private static final BinaryMessageEncoder<${this.mangle($schema.getName())}> ENCODER =\n      new BinaryMessageEncoder<${this.mangle($schema.getName())}>(MODEL$, SCHEMA$);\n\n  private static final BinaryMessageDecoder<${this.mangle($schema.getName())}> DECODER =\n      new BinaryMessageDecoder<${this.mangle($schema.getName())}>(MODEL$, SCHEMA$);\n\n  /**\n   * Return the BinaryMessageEncoder instance used by this class.\n   * @return the message encoder used by this class\n   */\n  public static BinaryMessageEncoder<${this.mangle($schema.getName())}> getEncoder() {\n    return ENCODER;\n  }\n\n  /**\n   * Return the BinaryMessageDecoder instance used by this class.\n   * @return the message decoder used by this class\n   */\n  public static BinaryMessageDecoder<${this.mangle($schema.getName())}> getDecoder() {\n    return DECODER;\n  }\n\n  /**\n   * Create a new BinaryMessageDecoder instance for this class that uses the specified {@link SchemaStore}.\n   * @param resolver a {@link SchemaStore} used to find schemas by fingerprint\n   * @return a BinaryMessageDecoder instance for this class backed by the given SchemaStore\n   */\n  public static BinaryMessageDecoder<${this.mangle($schema.getName())}> createDecoder(SchemaStore resolver) {\n    return new BinaryMessageDecoder<${this.mangle($schema.getName())}>(MODEL$, SCHEMA$, resolver);\n  }\n\n  /**\n   * Serializes this ${schema.getName()} to a ByteBuffer.\n   * @return a buffer holding the serialized data for this instance\n   * @throws java.io.IOException if this instance could not be serialized\n   */\n  public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException {\n    return ENCODER.encode(this);\n  }\n\n  /**\n   * Deserializes a ${schema.getName()} from a ByteBuffer.\n   * @param b a byte buffer holding serialized data for an instance of this class\n   * @return a ${schema.getName()} instance decoded from the given buffer\n   * @throws java.io.IOException if the given bytes could not be deserialized into an instance of this class\n   */\n  public static ${this.mangle($schema.getName())} fromByteBuffer(\n      java.nio.ByteBuffer b) throws java.io.IOException {\n    return DECODER.decode(b);\n  }\n#end\n\n#foreach ($field in $schema.getFields())\n#if ($field.doc())\n  /** $field.doc() */\n#end\n#foreach ($annotation in $this.javaAnnotations($field))\n  @$annotation\n#end\n  #if (${this.publicFields()})public#elseif (${this.privateFields()})private#end ${this.javaUnbox($field.schema(), false)} ${this.mangle($field.name(), $schema.isError())};\n#end\n#if ($schema.isError())\n\n  public ${this.mangle($schema.getName())}() {\n    super();\n  }\n\n  public ${this.mangle($schema.getName())}(Object value) {\n    super(value);\n  }\n\n  public ${this.mangle($schema.getName())}(Throwable cause) {\n    super(cause);\n  }\n\n  public ${this.mangle($schema.getName())}(Object value, Throwable cause) {\n    super(value, cause);\n  }\n\n#else\n#if ($schema.getFields().size() > 0)\n\n  /**\n   * Default constructor.  Note that this does not initialize fields\n   * to their default values from the schema.  If that is desired then\n   * one should use <code>newBuilder()</code>.\n   */\n  public ${this.mangle($schema.getName())}() {}\n#if ($this.isCreateAllArgsConstructor())\n\n  /**\n   * All-args constructor.\n#foreach ($field in $schema.getFields())\n#if ($field.doc())   * @param ${this.mangle($field.name())} $field.doc()\n#else   * @param ${this.mangle($field.name())} The new value for ${field.name()}\n#end\n#end\n   */\n  public ${this.mangle($schema.getName())}(#foreach($field in $schema.getFields())${this.javaType($field.schema())} ${this.mangle($field.name())}#if($foreach.count < $schema.getFields().size()), #end#end) {\n#foreach ($field in $schema.getFields())\n    ${this.generateSetterCode($field.schema(), ${this.mangle($field.name())}, ${this.mangle($field.name())})}\n#end\n  }\n#else\n  /**\n   * This schema contains more than 254 fields which exceeds the maximum number\n   * of permitted constructor parameters in the JVM. An all-args constructor\n   * will not be generated. Please use <code>newBuilder()</code> to instantiate\n   * objects instead.\n   */\n#end\n#end\n\n#end\n  public org.apache.avro.specific.SpecificData getSpecificData() { return MODEL$; }\n  public org.apache.avro.Schema getSchema() { return SCHEMA$; }\n  // Used by DatumWriter.  Applications should not call.\n  public java.lang.Object get(int field$) {\n    switch (field$) {\n#set ($i = 0)\n#foreach ($field in $schema.getFields())\n    case $i: return ${this.mangle($field.name(), $schema.isError())};\n#set ($i = $i + 1)\n#end\n    default: throw new IndexOutOfBoundsException(\"Invalid index: \" + field$);\n    }\n  }\n\n#if ($this.hasLogicalTypeField($schema))\n  private static final org.apache.avro.Conversion<?>[] conversions =\n      new org.apache.avro.Conversion<?>[] {\n#foreach ($field in $schema.getFields())\n      ${this.conversionInstance($field.schema())},\n#end\n      null\n  };\n\n  @Override\n  public org.apache.avro.Conversion<?> getConversion(int field) {\n    return conversions[field];\n  }\n\n#end\n  // Used by DatumReader.  Applications should not call.\n  @SuppressWarnings(value=\"unchecked\")\n  public void put(int field$, java.lang.Object value$) {\n    switch (field$) {\n#set ($i = 0)\n#foreach ($field in $schema.getFields())\n    case $i: ${this.mangle($field.name(), $schema.isError())} = #if(${this.javaType($field.schema())} != \"java.lang.Object\" && ${this.javaType($field.schema())} != \"java.lang.String\")(${this.javaType($field.schema())})#{end}value$#if(${this.javaType($field.schema())} == \"java.lang.String\") != null ? value$.toString() : null#{end}; break;\n#set ($i = $i + 1)\n#end\n    default: throw new IndexOutOfBoundsException(\"Invalid index: \" + field$);\n    }\n  }\n\n#foreach ($field in $schema.getFields())\n#if (${this.gettersReturnOptional} && (!${this.optionalGettersForNullableFieldsOnly} || ${field.schema().isNullable()}))\n  /**\n   * Gets the value of the '${this.mangle($field.name(), $schema.isError())}' field as an Optional&lt;${this.javaType($field.schema())}&gt;.\n#if ($field.doc())      * $field.doc()\n#end\n   * @return The value wrapped in an Optional&lt;${this.javaType($field.schema())}&gt;.\n   */\n  public Optional<${this.javaType($field.schema())}> ${this.generateGetMethod($schema, $field)}() {\n    return Optional.<${this.javaType($field.schema())}>ofNullable(${this.mangle($field.name(), $schema.isError())});\n  }\n#else\n  /**\n   * Gets the value of the '${this.mangle($field.name(), $schema.isError())}' field.\n#if ($field.doc())   * @return $field.doc()\n#else   * @return The value of the '${this.mangle($field.name(), $schema.isError())}' field.\n#end\n   */\n  public ${this.javaUnbox($field.schema(), false)} ${this.generateGetMethod($schema, $field)}() {\n    return ${this.mangle($field.name(), $schema.isError())};\n  }\n#end\n\n#if (${this.createOptionalGetters})\n  /**\n   * Gets the value of the '${this.mangle($field.name(), $schema.isError())}' field as an Optional&lt;${this.javaType($field.schema())}&gt;.\n#if ($field.doc())      * $field.doc()\n#end\n   * @return The value wrapped in an Optional&lt;${this.javaType($field.schema())}&gt;.\n   */\n  public Optional<${this.javaType($field.schema())}> ${this.generateGetOptionalMethod($schema, $field)}() {\n    return Optional.<${this.javaType($field.schema())}>ofNullable(${this.mangle($field.name(), $schema.isError())});\n  }\n#end\n\n#if ($this.createSetters)\n  /**\n   * Sets the value of the '${this.mangle($field.name(), $schema.isError())}' field.\n#if ($field.doc())   * $field.doc()\n#end\n   * @param value the value to set.\n   */\n  public void ${this.generateSetMethod($schema, $field)}(${this.javaUnbox($field.schema(), false)} value) {\n    ${this.generateSetterCode($field.schema(), ${this.mangle($field.name(), $schema.isError())}, \"value\")}\n  }\n#end\n\n#end\n  /**\n   * Creates a new ${this.mangle($schema.getName())} RecordBuilder.\n   * @return A new ${this.mangle($schema.getName())} RecordBuilder\n   */\n  public static #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder newBuilder() {\n    return new #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder();\n  }\n\n  /**\n   * Creates a new ${this.mangle($schema.getName())} RecordBuilder by copying an existing Builder.\n   * @param other The existing builder to copy.\n   * @return A new ${this.mangle($schema.getName())} RecordBuilder\n   */\n  public static #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder newBuilder(#if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder other) {\n    if (other == null) {\n      return new #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder();\n    } else {\n      return new #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder(other);\n    }\n  }\n\n  /**\n   * Creates a new ${this.mangle($schema.getName())} RecordBuilder by copying an existing $this.mangle($schema.getName()) instance.\n   * @param other The existing instance to copy.\n   * @return A new ${this.mangle($schema.getName())} RecordBuilder\n   */\n  public static #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder newBuilder(#if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())} other) {\n    if (other == null) {\n      return new #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder();\n    } else {\n      return new #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder(other);\n    }\n  }\n\n  /**\n   * RecordBuilder for ${this.mangle($schema.getName())} instances.\n   */\n  @org.apache.avro.specific.AvroGenerated\n  public static class Builder extends#if ($schema.isError()) org.apache.avro.specific.SpecificErrorBuilderBase<${this.mangle($schema.getName())}>#else org.apache.avro.specific.SpecificRecordBuilderBase<${this.mangle($schema.getName())}>#end\n\n    implements#if ($schema.isError()) org.apache.avro.data.ErrorBuilder<${this.mangle($schema.getName())}>#else org.apache.avro.data.RecordBuilder<${this.mangle($schema.getName())}>#end {\n\n#foreach ($field in $schema.getFields())\n#if ($field.doc())\n    /** $field.doc() */\n#end\n    private ${this.javaUnbox($field.schema(), false)} ${this.mangle($field.name(), $schema.isError())};\n#if (${this.hasBuilder($field.schema())})\n    private ${this.javaUnbox($field.schema(), false)}.Builder ${this.mangle($field.name(), $schema.isError())}Builder;\n#end\n#end\n\n    /** Creates a new Builder */\n    private Builder() {\n      super(SCHEMA$, MODEL$);\n    }\n\n    /**\n     * Creates a Builder by copying an existing Builder.\n     * @param other The existing Builder to copy.\n     */\n    private Builder(#if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder other) {\n      super(other);\n#foreach ($field in $schema.getFields())\n      if (isValidValue(fields()[$field.pos()], other.${this.mangle($field.name(), $schema.isError())})) {\n        this.${this.mangle($field.name(), $schema.isError())} = data().deepCopy(fields()[$field.pos()].schema(), other.${this.mangle($field.name(), $schema.isError())});\n        fieldSetFlags()[$field.pos()] = other.fieldSetFlags()[$field.pos()];\n      }\n#if (${this.hasBuilder($field.schema())})\n      if (other.${this.generateHasBuilderMethod($schema, $field)}()) {\n        this.${this.mangle($field.name(), $schema.isError())}Builder = ${this.javaType($field.schema())}.newBuilder(other.${this.generateGetBuilderMethod($schema, $field)}());\n      }\n#end\n#end\n    }\n\n    /**\n     * Creates a Builder by copying an existing $this.mangle($schema.getName()) instance\n     * @param other The existing instance to copy.\n     */\n    private Builder(#if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())} other) {\n#if ($schema.isError())      super(other)#else\n      super(SCHEMA$, MODEL$)#end;\n#foreach ($field in $schema.getFields())\n      if (isValidValue(fields()[$field.pos()], other.${this.mangle($field.name(), $schema.isError())})) {\n        this.${this.mangle($field.name(), $schema.isError())} = data().deepCopy(fields()[$field.pos()].schema(), other.${this.mangle($field.name(), $schema.isError())});\n        fieldSetFlags()[$field.pos()] = true;\n      }\n#if (${this.hasBuilder($field.schema())})\n      this.${this.mangle($field.name(), $schema.isError())}Builder = null;\n#end\n#end\n    }\n#if ($schema.isError())\n\n    @Override\n    public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder setValue(Object value) {\n      super.setValue(value);\n      return this;\n    }\n\n    @Override\n    public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder clearValue() {\n      super.clearValue();\n      return this;\n    }\n\n    @Override\n    public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder setCause(Throwable cause) {\n      super.setCause(cause);\n      return this;\n    }\n\n    @Override\n    public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder clearCause() {\n      super.clearCause();\n      return this;\n    }\n#end\n\n#foreach ($field in $schema.getFields())\n    /**\n      * Gets the value of the '${this.mangle($field.name(), $schema.isError())}' field.\n#if ($field.doc())      * $field.doc()\n#end\n      * @return The value.\n      */\n    public ${this.javaUnbox($field.schema(), false)} ${this.generateGetMethod($schema, $field)}() {\n      return ${this.mangle($field.name(), $schema.isError())};\n    }\n\n#if (${this.createOptionalGetters})\n    /**\n      * Gets the value of the '${this.mangle($field.name(), $schema.isError())}' field as an Optional&lt;${this.javaType($field.schema())}&gt;.\n#if ($field.doc())      * $field.doc()\n#end\n      * @return The value wrapped in an Optional&lt;${this.javaType($field.schema())}&gt;.\n      */\n    public Optional<${this.javaType($field.schema())}> ${this.generateGetOptionalMethod($schema, $field)}() {\n      return Optional.<${this.javaType($field.schema())}>ofNullable(${this.mangle($field.name(), $schema.isError())});\n    }\n#end\n\n    /**\n      * Sets the value of the '${this.mangle($field.name(), $schema.isError())}' field.\n#if ($field.doc())      * $field.doc()\n#end\n      * @param value The value of '${this.mangle($field.name(), $schema.isError())}'.\n      * @return This builder.\n      */\n    public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder ${this.generateSetMethod($schema, $field)}(${this.javaUnbox($field.schema(), false)} value) {\n      validate(fields()[$field.pos()], value);\n#if (${this.hasBuilder($field.schema())})\n      this.${this.mangle($field.name(), $schema.isError())}Builder = null;\n#end\n      ${this.generateSetterCode($field.schema(), ${this.mangle($field.name(), $schema.isError())}, \"value\")}\n      fieldSetFlags()[$field.pos()] = true;\n      return this;\n    }\n\n    /**\n      * Checks whether the '${this.mangle($field.name(), $schema.isError())}' field has been set.\n#if ($field.doc())      * $field.doc()\n#end\n      * @return True if the '${this.mangle($field.name(), $schema.isError())}' field has been set, false otherwise.\n      */\n    public boolean ${this.generateHasMethod($schema, $field)}() {\n      return fieldSetFlags()[$field.pos()];\n    }\n\n#if (${this.hasBuilder($field.schema())})\n    /**\n     * Gets the Builder instance for the '${this.mangle($field.name(), $schema.isError())}' field and creates one if it doesn't exist yet.\n#if ($field.doc())     * $field.doc()\n#end\n     * @return This builder.\n     */\n    public ${this.javaType($field.schema())}.Builder ${this.generateGetBuilderMethod($schema, $field)}() {\n      if (${this.mangle($field.name(), $schema.isError())}Builder == null) {\n        if (${this.generateHasMethod($schema, $field)}()) {\n          ${this.generateSetBuilderMethod($schema, $field)}(${this.javaType($field.schema())}.newBuilder(${this.mangle($field.name(), $schema.isError())}));\n        } else {\n          ${this.generateSetBuilderMethod($schema, $field)}(${this.javaType($field.schema())}.newBuilder());\n        }\n      }\n      return ${this.mangle($field.name(), $schema.isError())}Builder;\n    }\n\n    /**\n     * Sets the Builder instance for the '${this.mangle($field.name(), $schema.isError())}' field\n#if ($field.doc())     * $field.doc()\n#end\n     * @param value The builder instance that must be set.\n     * @return This builder.\n     */\n\n    public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder ${this.generateSetBuilderMethod($schema, $field)}(${this.javaUnbox($field.schema(), false)}.Builder value) {\n      ${this.generateClearMethod($schema, $field)}();\n      ${this.mangle($field.name(), $schema.isError())}Builder = value;\n      return this;\n    }\n\n    /**\n     * Checks whether the '${this.mangle($field.name(), $schema.isError())}' field has an active Builder instance\n#if ($field.doc())     * $field.doc()\n#end\n     * @return True if the '${this.mangle($field.name(), $schema.isError())}' field has an active Builder instance\n     */\n    public boolean ${this.generateHasBuilderMethod($schema, $field)}() {\n      return ${this.mangle($field.name(), $schema.isError())}Builder != null;\n    }\n#end\n\n    /**\n      * Clears the value of the '${this.mangle($field.name(), $schema.isError())}' field.\n#if ($field.doc())      * $field.doc()\n#end\n      * @return This builder.\n      */\n    public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder ${this.generateClearMethod($schema, $field)}() {\n#if (${this.isUnboxedJavaTypeNullable($field.schema())})\n      ${this.mangle($field.name(), $schema.isError())} = null;\n#end\n#if (${this.hasBuilder($field.schema())})\n      ${this.mangle($field.name(), $schema.isError())}Builder = null;\n#end\n      fieldSetFlags()[$field.pos()] = false;\n      return this;\n    }\n\n#end\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public ${this.mangle($schema.getName())} build() {\n      try {\n        ${this.mangle($schema.getName())} record = new ${this.mangle($schema.getName())}(#if ($schema.isError())getValue(), getCause()#end);\n#foreach ($field in $schema.getFields())\n#if (${this.hasBuilder($field.schema())})\n        if (${this.mangle($field.name(), $schema.isError())}Builder != null) {\n          try {\n            record.${this.mangle($field.name(), $schema.isError())} = this.${this.mangle($field.name(), $schema.isError())}Builder.build();\n          } catch (org.apache.avro.AvroMissingFieldException e) {\n            e.addParentField(record.getSchema().getField(\"${this.mangle($field.name(), $schema.isError())}\"));\n            throw e;\n          }\n        } else {\n          record.${this.mangle($field.name(), $schema.isError())} = fieldSetFlags()[$field.pos()] ? this.${this.mangle($field.name(), $schema.isError())} : #if(${this.javaType($field.schema())} != \"java.lang.Object\")(${this.javaType($field.schema())})#{end} defaultValue(fields()[$field.pos()]);\n        }\n#else\n        record.${this.mangle($field.name(), $schema.isError())} = fieldSetFlags()[$field.pos()] ? this.${this.mangle($field.name(), $schema.isError())} : #if(${this.javaType($field.schema())} != \"java.lang.Object\")(${this.javaType($field.schema())})#{end} defaultValue(fields()[$field.pos()]);\n#end\n#end\n        return record;\n      } catch (org.apache.avro.AvroMissingFieldException e) {\n        throw e;\n      } catch (java.lang.Exception e) {\n        throw new org.apache.avro.AvroRuntimeException(e);\n      }\n    }\n  }\n\n  @SuppressWarnings(\"unchecked\")\n  private static final org.apache.avro.io.DatumWriter<${this.mangle($schema.getName())}>\n    WRITER$ = (org.apache.avro.io.DatumWriter<${this.mangle($schema.getName())}>)MODEL$.createDatumWriter(SCHEMA$);\n\n  @Override public void writeExternal(java.io.ObjectOutput out)\n    throws java.io.IOException {\n    WRITER$.write(this, SpecificData.getEncoder(out));\n  }\n\n  @SuppressWarnings(\"unchecked\")\n  private static final org.apache.avro.io.DatumReader<${this.mangle($schema.getName())}>\n    READER$ = (org.apache.avro.io.DatumReader<${this.mangle($schema.getName())}>)MODEL$.createDatumReader(SCHEMA$);\n\n  @Override public void readExternal(java.io.ObjectInput in)\n    throws java.io.IOException {\n    READER$.read(this, SpecificData.getDecoder(in));\n  }\n\n#if ($this.isCustomCodable($schema))\n  @Override protected boolean hasCustomCoders() { return true; }\n\n  @Override public void customEncode(org.apache.avro.io.Encoder out)\n    throws java.io.IOException\n  {\n#set ($nv = 0)## Counter to ensure unique var-names\n#set ($maxnv = 0)## Holds high-water mark during recursion\n#foreach ($field in $schema.getFields())\n#set ($n = $this.mangle($field.name(), $schema.isError()))\n#set ($s = $field.schema())\n#encodeVar(0 \"this.${n}\" $s)\n\n#set ($nv = $maxnv)\n#end\n  }\n\n  @Override public void customDecode(org.apache.avro.io.ResolvingDecoder in)\n    throws java.io.IOException\n  {\n    org.apache.avro.Schema.Field[] fieldOrder = in.readFieldOrderIfDiff();\n    if (fieldOrder == null) {\n## Common case: order of fields hasn't changed, so read them in a\n## fixed order according to reader's schema\n#set ($nv = 0)## Counter to ensure unique var-names\n#set ($maxnv = 0)## Holds high-water mark during recursion\n#foreach ($field in $schema.getFields())\n#set ($n = $this.mangle($field.name(), $schema.isError()))\n#set ($s = $field.schema())\n#set ($rs = \"SCHEMA$.getField(\"\"${n}\"\").schema()\")\n#decodeVar(2 \"this.${n}\" $s $rs)\n\n#set ($nv = $maxnv)\n#end\n    } else {\n      for (int i = 0; i < $schema.getFields().size(); i++) {\n        switch (fieldOrder[i].pos()) {\n#set ($fieldno = 0)\n#set ($nv = 0)## Counter to ensure unique var-names\n#set ($maxnv = 0)## Holds high-water mark during recursion\n#foreach ($field in $schema.getFields())\n        case $fieldno:\n#set ($n = $this.mangle($field.name(), $schema.isError()))\n#set ($s = $field.schema())\n#set ($rs = \"SCHEMA$.getField(\"\"${n}\"\").schema()\")\n#decodeVar(6 \"this.${n}\" $s $rs)\n          break;\n\n#set ($nv = $maxnv)\n#set ($fieldno = $fieldno + 1)\n#end\n        default:\n          throw new java.io.IOException(\"Corrupt ResolvingDecoder.\");\n        }\n      }\n    }\n  }\n#end\n}\n\n#macro( encodeVar $indent $var $s )\n#set ($I = $this.indent($indent))\n##### Compound types (array, map, and union) require calls\n##### that will recurse back into this encodeVar macro:\n#if ($s.Type.Name.equals(\"array\"))\n#encodeArray($indent $var $s)\n#elseif ($s.Type.Name.equals(\"map\"))\n#encodeMap($indent $var $s)\n#elseif ($s.Type.Name.equals(\"union\"))\n#encodeUnion($indent $var $s)\n##### Use the generated \"encode\" method as fast way to write\n##### (specific) record types:\n#elseif ($s.Type.Name.equals(\"record\"))\n$I    ${var}.customEncode(out);\n##### For rest of cases, generate calls out.writeXYZ:\n#elseif ($s.Type.Name.equals(\"null\"))\n$I    out.writeNull();\n#elseif ($s.Type.Name.equals(\"boolean\"))\n$I    out.writeBoolean(${var});\n#elseif ($s.Type.Name.equals(\"int\"))\n$I    out.writeInt(${var});\n#elseif ($s.Type.Name.equals(\"long\"))\n$I    out.writeLong(${var});\n#elseif ($s.Type.Name.equals(\"float\"))\n$I    out.writeFloat(${var});\n#elseif ($s.Type.Name.equals(\"double\"))\n$I    out.writeDouble(${var});\n#elseif ($s.Type.Name.equals(\"string\"))\n#if ($this.isStringable($s))\n$I    out.writeString(${var}.toString());\n#else\n$I    out.writeString(${var});\n#end\n#elseif ($s.Type.Name.equals(\"bytes\"))\n$I    out.writeBytes(${var});\n#elseif ($s.Type.Name.equals(\"fixed\"))\n$I    out.writeFixed(${var}.bytes(), 0, ${s.FixedSize});\n#elseif ($s.Type.Name.equals(\"enum\"))\n$I    out.writeEnum(${var}.ordinal());\n#else\n## TODO -- singal a code-gen-time error\n#end\n#end\n\n#macro( encodeArray $indent $var $s )\n#set ($I = $this.indent($indent))\n#set ($et = $this.javaType($s.ElementType))\n$I    long size${nv} = ${var}.size();\n$I    out.writeArrayStart();\n$I    out.setItemCount(size${nv});\n$I    long actualSize${nv} = 0;\n$I    for ($et e${nv}: ${var}) {\n$I      actualSize${nv}++;\n$I      out.startItem();\n#set ($var = \"e${nv}\")\n#set ($nv = $nv + 1)\n#set ($maxnv = $nv)\n#set ($indent = $indent + 2)\n#encodeVar($indent $var $s.ElementType)\n#set ($nv = $nv - 1)\n#set ($indent = $indent - 2)\n#set ($I = $this.indent($indent))\n$I    }\n$I    out.writeArrayEnd();\n$I    if (actualSize${nv} != size${nv})\n$I      throw new java.util.ConcurrentModificationException(\"Array-size written was \" + size${nv} + \", but element count was \" + actualSize${nv} + \".\");\n#end\n\n#macro( encodeMap $indent $var $s )\n#set ($I = $this.indent($indent))\n#set ($kt = $this.getStringType($s))\n#set ($vt = $this.javaType($s.ValueType))\n$I    long size${nv} = ${var}.size();\n$I    out.writeMapStart();\n$I    out.setItemCount(size${nv});\n$I    long actualSize${nv} = 0;\n$I    for (java.util.Map.Entry<$kt, $vt> e${nv}: ${var}.entrySet()) {\n$I      actualSize${nv}++;\n$I      out.startItem();\n#if ($this.isStringable($s))\n$I      out.writeString(e${nv}.getKey().toString());\n#else\n$I      out.writeString(e${nv}.getKey());\n#end\n$I      $vt v${nv} = e${nv}.getValue();\n#set ($var = \"v${nv}\")\n#set ($nv = $nv + 1)\n#set ($maxnv = $nv)\n#set ($indent = $indent + 2)\n#encodeVar($indent $var $s.ValueType)\n#set ($nv = $nv - 1)\n#set ($indent = $indent - 2)\n#set ($I = $this.indent($indent))\n$I    }\n$I    out.writeMapEnd();\n$I    if (actualSize${nv} != size${nv})\n      throw new java.util.ConcurrentModificationException(\"Map-size written was \" + size${nv} + \", but element count was \" + actualSize${nv} + \".\");\n#end\n\n#macro( encodeUnion $indent $var $s )\n#set ($I = $this.indent($indent))\n#set ($et = $this.javaType($s.Types.get($this.getNonNullIndex($s))))\n$I    if (${var} == null) {\n$I      out.writeIndex(#if($this.getNonNullIndex($s)==0)1#{else}0#end);\n$I      out.writeNull();\n$I    } else {\n$I      out.writeIndex(${this.getNonNullIndex($s)});\n#set ($indent = $indent + 2)\n#encodeVar($indent $var $s.Types.get($this.getNonNullIndex($s)))\n#set ($indent = $indent - 2)\n#set ($I = $this.indent($indent))\n$I    }\n#end\n\n\n#macro( decodeVar $indent $var $s $rs )\n#set ($I = $this.indent($indent))\n##### Compound types (array, map, and union) require calls\n##### that will recurse back into this decodeVar macro:\n#if ($s.Type.Name.equals(\"array\"))\n#decodeArray($indent $var $s $rs)\n#elseif ($s.Type.Name.equals(\"map\"))\n#decodeMap($indent $var $s $rs)\n#elseif ($s.Type.Name.equals(\"union\"))\n#decodeUnion($indent $var $s $rs)\n##### Use the generated \"decode\" method as fast way to write\n##### (specific) record types:\n#elseif ($s.Type.Name.equals(\"record\"))\n$I    if (${var} == null) {\n$I      ${var} = new ${this.javaType($s)}();\n$I    }\n$I    ${var}.customDecode(in);\n##### For rest of cases, generate calls in.readXYZ:\n#elseif ($s.Type.Name.equals(\"null\"))\n$I    in.readNull();\n#elseif ($s.Type.Name.equals(\"boolean\"))\n$I    $var = in.readBoolean();\n#elseif ($s.Type.Name.equals(\"int\"))\n$I    $var = in.readInt();\n#elseif ($s.Type.Name.equals(\"long\"))\n$I    $var = in.readLong();\n#elseif ($s.Type.Name.equals(\"float\"))\n$I    $var = in.readFloat();\n#elseif ($s.Type.Name.equals(\"double\"))\n$I    $var = in.readDouble();\n#elseif ($s.Type.Name.equals(\"string\"))\n#decodeString( \"$I\" $var $s )\n#elseif ($s.Type.Name.equals(\"bytes\"))\n$I    $var = in.readBytes(${var});\n#elseif ($s.Type.Name.equals(\"fixed\"))\n$I    if (${var} == null) {\n$I      ${var} = new ${this.javaType($s)}();\n$I    }\n$I    in.readFixed(${var}.bytes(), 0, ${s.FixedSize});\n#elseif ($s.Type.Name.equals(\"enum\"))\n$I    $var = ${this.javaType($s)}.values()[in.readEnum()];\n#else\n## TODO -- singal a code-gen-time error\n#end\n#end\n\n#macro( decodeString $II $var $s )\n#set ($st = ${this.getStringType($s)})\n#if ($this.isStringable($s))\n#if ($st.equals(\"java.net.URI\"))\n$II    try {\n$II      ${var} = new ${st}(in.readString());\n$II    } catch (java.net.URISyntaxException e) {\n$II      throw new java.io.IOException(e.getMessage());\n$II    }\n#elseif ($st.equals(\"java.net.URL\"))\n$II    try {\n$II      ${var} = new ${st}(in.readString());\n$II    } catch (java.net.MalformedURLException e) {\n$II      throw new java.io.IOException(e.getMessage());\n$II    }\n#else\n$II    ${var} = new ${st}(in.readString());\n#end\n#elseif ($st.equals(\"java.lang.String\"))\n$II    $var = in.readString();\n#elseif ($st.equals(\"org.apache.avro.util.Utf8\"))\n$II    $var = in.readString(${var});\n#else\n$II    $var = in.readString(${var} instanceof Utf8 ? (Utf8)${var} : null);\n#end\n#end\n\n#macro( decodeArray $indent $var $s $rs )\n#set ($I = $this.indent($indent))\n#set ($t = $this.javaType($s))\n#set ($et = $this.javaType($s.ElementType))\n#set ($gat = \"SpecificData.Array<${et}>\")\n$I    long size${nv} = in.readArrayStart();\n## Need fresh variable name due to limitation of macro system\n$I    $t a${nv} = ${var};\n$I    if (a${nv} == null) {\n$I      a${nv} = new ${gat}((int)size${nv}, ${rs});\n$I      $var = a${nv};\n$I    } else a${nv}.clear();\n$I    $gat ga${nv} = (a${nv} instanceof SpecificData.Array ? (${gat})a${nv} : null);\n$I    for ( ; 0 < size${nv}; size${nv} = in.arrayNext()) {\n$I      for ( ; size${nv} != 0; size${nv}--) {\n$I        $et e${nv} = (ga${nv} != null ? ga${nv}.peek() : null);\n#set ($var = \"e${nv}\")\n#set ($nv = $nv + 1)\n#set ($maxnv = $nv)\n#set ($indent = $indent + 4)\n#decodeVar($indent $var $s.ElementType \"${rs}.getElementType()\")\n#set ($nv = $nv - 1)\n#set ($indent = $indent - 4)\n#set ($I = $this.indent($indent))\n$I        a${nv}.add(e${nv});\n$I      }\n$I    }\n#end\n\n#macro( decodeMap $indent $var $s $rs )\n#set ($I = $this.indent($indent))\n#set ($t = $this.javaType($s))\n#set ($kt = $this.getStringType($s))\n#set ($vt = $this.javaType($s.ValueType))\n$I    long size${nv} = in.readMapStart();\n$I    $t m${nv} = ${var}; // Need fresh name due to limitation of macro system\n$I    if (m${nv} == null) {\n$I      m${nv} = new java.util.HashMap<${kt},${vt}>((int)size${nv});\n$I      $var = m${nv};\n$I    } else m${nv}.clear();\n$I    for ( ; 0 < size${nv}; size${nv} = in.mapNext()) {\n$I      for ( ; size${nv} != 0; size${nv}--) {\n$I        $kt k${nv} = null;\n#decodeString( \"$I    \" \"k${nv}\" $s )\n$I        $vt v${nv} = null;\n#set ($var = \"v${nv}\")\n#set ($nv = $nv + 1)\n#set ($maxnv = $nv)\n#set ($indent = $indent + 4)\n#decodeVar($indent $var $s.ValueType \"${rs}.getValueType()\")\n#set ($nv = $nv - 1)\n#set ($indent = $indent - 4)\n#set ($I = $this.indent($indent))\n$I        m${nv}.put(k${nv}, v${nv});\n$I      }\n$I    }\n#end\n\n#macro( decodeUnion $indent $var $s $rs )\n#set ($I = $this.indent($indent))\n#set ($et = $this.javaType($s.Types.get($this.getNonNullIndex($s))))\n#set ($si = $this.getNonNullIndex($s))\n$I    if (in.readIndex() != ${si}) {\n$I      in.readNull();\n$I      ${var} = null;\n$I    } else {\n#set ($indent = $indent + 2)\n#decodeVar($indent $var $s.Types.get($si) \"${rs}.getTypes().get(${si})\")\n#set ($indent = $indent - 2)\n#set ($I = $this.indent($indent))\n$I    }\n#end\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/record.vm",
    "content": "##\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##     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#if ($schema.getNamespace())\npackage $this.mangle($schema.getNamespace());\n#end\n\nimport org.apache.avro.generic.GenericArray;\nimport org.apache.avro.specific.SpecificData;\nimport org.apache.avro.util.Utf8;\n#if (!$schema.isError())\nimport org.apache.avro.message.BinaryMessageEncoder;\nimport org.apache.avro.message.BinaryMessageDecoder;\nimport org.apache.avro.message.SchemaStore;\n#end\n#if (${this.gettersReturnOptional} || ${this.createOptionalGetters})import java.util.Optional;#end\n\n#if ($schema.getDoc())\n/** $schema.getDoc() */\n#end\n#foreach ($annotation in $this.javaAnnotations($schema))\n@$annotation\n#end\n@org.apache.avro.specific.AvroGenerated\npublic class ${this.mangle($schema.getName())}#if ($schema.isError()) extends org.apache.avro.specific.SpecificExceptionBase#else extends org.apache.avro.specific.SpecificRecordBase#end implements org.apache.avro.specific.SpecificRecord {\n  // Custom template\n  private static final long serialVersionUID = ${this.fingerprint64($schema)}L;\n\n#set ($schemaString = $this.javaSplit($schema.toString()))\n#set ($customLogicalTypeFactories = $this.getUsedCustomLogicalTypeFactories($schema).entrySet())\n#if (!$customLogicalTypeFactories.isEmpty())\n  static {\n#foreach ($customLogicalTypeFactory in $customLogicalTypeFactories)\n    org.apache.avro.LogicalTypes.register(\"${customLogicalTypeFactory.getKey()}\", new ${customLogicalTypeFactory.getValue()}());\n#end\n  }\n#end\n\n  public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse($schemaString);\n  public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }\n\n  private static final SpecificData MODEL$ = new SpecificData();\n#set ($usedConversions = $this.getUsedConversionClasses($schema))\n#if (!$usedConversions.isEmpty())\n  static {\n#foreach ($conversion in $usedConversions)\n    MODEL$.addLogicalTypeConversion(new ${conversion}());\n#end\n  }\n#end\n\n#if (!$schema.isError())\n  private static final BinaryMessageEncoder<${this.mangle($schema.getName())}> ENCODER =\n      new BinaryMessageEncoder<${this.mangle($schema.getName())}>(MODEL$, SCHEMA$);\n\n  private static final BinaryMessageDecoder<${this.mangle($schema.getName())}> DECODER =\n      new BinaryMessageDecoder<${this.mangle($schema.getName())}>(MODEL$, SCHEMA$);\n\n  /**\n   * Return the BinaryMessageEncoder instance used by this class.\n   * @return the message encoder used by this class\n   */\n  public static BinaryMessageEncoder<${this.mangle($schema.getName())}> getEncoder() {\n    return ENCODER;\n  }\n\n  /**\n   * Return the BinaryMessageDecoder instance used by this class.\n   * @return the message decoder used by this class\n   */\n  public static BinaryMessageDecoder<${this.mangle($schema.getName())}> getDecoder() {\n    return DECODER;\n  }\n\n  /**\n   * Create a new BinaryMessageDecoder instance for this class that uses the specified {@link SchemaStore}.\n   * @param resolver a {@link SchemaStore} used to find schemas by fingerprint\n   * @return a BinaryMessageDecoder instance for this class backed by the given SchemaStore\n   */\n  public static BinaryMessageDecoder<${this.mangle($schema.getName())}> createDecoder(SchemaStore resolver) {\n    return new BinaryMessageDecoder<${this.mangle($schema.getName())}>(MODEL$, SCHEMA$, resolver);\n  }\n\n  /**\n   * Serializes this ${schema.getName()} to a ByteBuffer.\n   * @return a buffer holding the serialized data for this instance\n   * @throws java.io.IOException if this instance could not be serialized\n   */\n  public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException {\n    return ENCODER.encode(this);\n  }\n\n  /**\n   * Deserializes a ${schema.getName()} from a ByteBuffer.\n   * @param b a byte buffer holding serialized data for an instance of this class\n   * @return a ${schema.getName()} instance decoded from the given buffer\n   * @throws java.io.IOException if the given bytes could not be deserialized into an instance of this class\n   */\n  public static ${this.mangle($schema.getName())} fromByteBuffer(\n      java.nio.ByteBuffer b) throws java.io.IOException {\n    return DECODER.decode(b);\n  }\n#end\n\n#foreach ($field in $schema.getFields())\n#if ($field.doc())\n  /** $field.doc() */\n#end\n#foreach ($annotation in $this.javaAnnotations($field))\n  @$annotation\n#end\n  #if (${this.publicFields()})public#elseif (${this.privateFields()})private#end ${this.javaUnbox($field.schema(), false)} ${this.mangle($field.name(), $schema.isError())};\n#end\n#if ($schema.isError())\n\n  public ${this.mangle($schema.getName())}() {\n    super();\n  }\n\n  public ${this.mangle($schema.getName())}(Object value) {\n    super(value);\n  }\n\n  public ${this.mangle($schema.getName())}(Throwable cause) {\n    super(cause);\n  }\n\n  public ${this.mangle($schema.getName())}(Object value, Throwable cause) {\n    super(value, cause);\n  }\n\n#else\n#if ($schema.getFields().size() > 0)\n\n  /**\n   * Default constructor.  Note that this does not initialize fields\n   * to their default values from the schema.  If that is desired then\n   * one should use <code>newBuilder()</code>.\n   */\n  public ${this.mangle($schema.getName())}() {}\n#if ($this.isCreateAllArgsConstructor())\n\n  /**\n   * All-args constructor.\n#foreach ($field in $schema.getFields())\n#if ($field.doc())   * @param ${this.mangle($field.name())} $field.doc()\n#else   * @param ${this.mangle($field.name())} The new value for ${field.name()}\n#end\n#end\n   */\n  public ${this.mangle($schema.getName())}(#foreach($field in $schema.getFields())${this.javaType($field.schema())} ${this.mangle($field.name())}#if($foreach.count < $schema.getFields().size()), #end#end) {\n#foreach ($field in $schema.getFields())\n    ${this.generateSetterCode($field.schema(), ${this.mangle($field.name())}, ${this.mangle($field.name())})}\n#end\n  }\n#else\n  /**\n   * This schema contains more than 254 fields which exceeds the maximum number\n   * of permitted constructor parameters in the JVM. An all-args constructor\n   * will not be generated. Please use <code>newBuilder()</code> to instantiate\n   * objects instead.\n   */\n#end\n#end\n\n#end\n  public org.apache.avro.specific.SpecificData getSpecificData() { return MODEL$; }\n  public org.apache.avro.Schema getSchema() { return SCHEMA$; }\n  // Used by DatumWriter.  Applications should not call.\n  public java.lang.Object get(int field$) {\n    switch (field$) {\n#set ($i = 0)\n#foreach ($field in $schema.getFields())\n    case $i: return ${this.mangle($field.name(), $schema.isError())};\n#set ($i = $i + 1)\n#end\n    default: throw new IndexOutOfBoundsException(\"Invalid index: \" + field$);\n    }\n  }\n\n#if ($this.hasLogicalTypeField($schema))\n  private static final org.apache.avro.Conversion<?>[] conversions =\n      new org.apache.avro.Conversion<?>[] {\n#foreach ($field in $schema.getFields())\n      ${this.conversionInstance($field.schema())},\n#end\n      null\n  };\n\n  @Override\n  public org.apache.avro.Conversion<?> getConversion(int field) {\n    return conversions[field];\n  }\n\n#end\n  // Used by DatumReader.  Applications should not call.\n  @SuppressWarnings(value=\"unchecked\")\n  public void put(int field$, java.lang.Object value$) {\n    switch (field$) {\n#set ($i = 0)\n#foreach ($field in $schema.getFields())\n    case $i: ${this.mangle($field.name(), $schema.isError())} = #if(${this.javaType($field.schema())} != \"java.lang.Object\" && ${this.javaType($field.schema())} != \"java.lang.String\")(${this.javaType($field.schema())})#{end}value$#if(${this.javaType($field.schema())} == \"java.lang.String\") != null ? value$.toString() : null#{end}; break;\n#set ($i = $i + 1)\n#end\n    default: throw new IndexOutOfBoundsException(\"Invalid index: \" + field$);\n    }\n  }\n\n#foreach ($field in $schema.getFields())\n#if (${this.gettersReturnOptional} && (!${this.optionalGettersForNullableFieldsOnly} || ${field.schema().isNullable()}))\n  /**\n   * Gets the value of the '${this.mangle($field.name(), $schema.isError())}' field as an Optional&lt;${this.javaType($field.schema())}&gt;.\n#if ($field.doc())      * $field.doc()\n#end\n   * @return The value wrapped in an Optional&lt;${this.javaType($field.schema())}&gt;.\n   */\n  public Optional<${this.javaType($field.schema())}> ${this.generateGetMethod($schema, $field)}() {\n    return Optional.<${this.javaType($field.schema())}>ofNullable(${this.mangle($field.name(), $schema.isError())});\n  }\n#else\n  /**\n   * Gets the value of the '${this.mangle($field.name(), $schema.isError())}' field.\n#if ($field.doc())   * @return $field.doc()\n#else   * @return The value of the '${this.mangle($field.name(), $schema.isError())}' field.\n#end\n   */\n  public ${this.javaUnbox($field.schema(), false)} ${this.generateGetMethod($schema, $field)}() {\n    return ${this.mangle($field.name(), $schema.isError())};\n  }\n#end\n\n#if (${this.createOptionalGetters})\n  /**\n   * Gets the value of the '${this.mangle($field.name(), $schema.isError())}' field as an Optional&lt;${this.javaType($field.schema())}&gt;.\n#if ($field.doc())      * $field.doc()\n#end\n   * @return The value wrapped in an Optional&lt;${this.javaType($field.schema())}&gt;.\n   */\n  public Optional<${this.javaType($field.schema())}> ${this.generateGetOptionalMethod($schema, $field)}() {\n    return Optional.<${this.javaType($field.schema())}>ofNullable(${this.mangle($field.name(), $schema.isError())});\n  }\n#end\n\n#if ($this.createSetters)\n  /**\n   * Sets the value of the '${this.mangle($field.name(), $schema.isError())}' field.\n#if ($field.doc())   * $field.doc()\n#end\n   * @param value the value to set.\n   */\n  public void ${this.generateSetMethod($schema, $field)}(${this.javaUnbox($field.schema(), false)} value) {\n    ${this.generateSetterCode($field.schema(), ${this.mangle($field.name(), $schema.isError())}, \"value\")}\n  }\n#end\n\n#end\n  /**\n   * Creates a new ${this.mangle($schema.getName())} RecordBuilder.\n   * @return A new ${this.mangle($schema.getName())} RecordBuilder\n   */\n  public static #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder newBuilder() {\n    return new #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder();\n  }\n\n  /**\n   * Creates a new ${this.mangle($schema.getName())} RecordBuilder by copying an existing Builder.\n   * @param other The existing builder to copy.\n   * @return A new ${this.mangle($schema.getName())} RecordBuilder\n   */\n  public static #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder newBuilder(#if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder other) {\n    if (other == null) {\n      return new #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder();\n    } else {\n      return new #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder(other);\n    }\n  }\n\n  /**\n   * Creates a new ${this.mangle($schema.getName())} RecordBuilder by copying an existing $this.mangle($schema.getName()) instance.\n   * @param other The existing instance to copy.\n   * @return A new ${this.mangle($schema.getName())} RecordBuilder\n   */\n  public static #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder newBuilder(#if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())} other) {\n    if (other == null) {\n      return new #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder();\n    } else {\n      return new #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder(other);\n    }\n  }\n\n  /**\n   * RecordBuilder for ${this.mangle($schema.getName())} instances.\n   */\n  @org.apache.avro.specific.AvroGenerated\n  public static class Builder extends#if ($schema.isError()) org.apache.avro.specific.SpecificErrorBuilderBase<${this.mangle($schema.getName())}>#else org.apache.avro.specific.SpecificRecordBuilderBase<${this.mangle($schema.getName())}>#end\n\n    implements#if ($schema.isError()) org.apache.avro.data.ErrorBuilder<${this.mangle($schema.getName())}>#else org.apache.avro.data.RecordBuilder<${this.mangle($schema.getName())}>#end {\n\n#foreach ($field in $schema.getFields())\n#if ($field.doc())\n    /** $field.doc() */\n#end\n    private ${this.javaUnbox($field.schema(), false)} ${this.mangle($field.name(), $schema.isError())};\n#if (${this.hasBuilder($field.schema())})\n    private ${this.javaUnbox($field.schema(), false)}.Builder ${this.mangle($field.name(), $schema.isError())}Builder;\n#end\n#end\n\n    /** Creates a new Builder */\n    private Builder() {\n      super(SCHEMA$, MODEL$);\n    }\n\n    /**\n     * Creates a Builder by copying an existing Builder.\n     * @param other The existing Builder to copy.\n     */\n    private Builder(#if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder other) {\n      super(other);\n#foreach ($field in $schema.getFields())\n      if (isValidValue(fields()[$field.pos()], other.${this.mangle($field.name(), $schema.isError())})) {\n        this.${this.mangle($field.name(), $schema.isError())} = data().deepCopy(fields()[$field.pos()].schema(), other.${this.mangle($field.name(), $schema.isError())});\n        fieldSetFlags()[$field.pos()] = other.fieldSetFlags()[$field.pos()];\n      }\n#if (${this.hasBuilder($field.schema())})\n      if (other.${this.generateHasBuilderMethod($schema, $field)}()) {\n        this.${this.mangle($field.name(), $schema.isError())}Builder = ${this.javaType($field.schema())}.newBuilder(other.${this.generateGetBuilderMethod($schema, $field)}());\n      }\n#end\n#end\n    }\n\n    /**\n     * Creates a Builder by copying an existing $this.mangle($schema.getName()) instance\n     * @param other The existing instance to copy.\n     */\n    private Builder(#if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())} other) {\n#if ($schema.isError())      super(other)#else\n      super(SCHEMA$, MODEL$)#end;\n#foreach ($field in $schema.getFields())\n      if (isValidValue(fields()[$field.pos()], other.${this.mangle($field.name(), $schema.isError())})) {\n        this.${this.mangle($field.name(), $schema.isError())} = data().deepCopy(fields()[$field.pos()].schema(), other.${this.mangle($field.name(), $schema.isError())});\n        fieldSetFlags()[$field.pos()] = true;\n      }\n#if (${this.hasBuilder($field.schema())})\n      this.${this.mangle($field.name(), $schema.isError())}Builder = null;\n#end\n#end\n    }\n#if ($schema.isError())\n\n    @Override\n    public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder setValue(Object value) {\n      super.setValue(value);\n      return this;\n    }\n\n    @Override\n    public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder clearValue() {\n      super.clearValue();\n      return this;\n    }\n\n    @Override\n    public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder setCause(Throwable cause) {\n      super.setCause(cause);\n      return this;\n    }\n\n    @Override\n    public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder clearCause() {\n      super.clearCause();\n      return this;\n    }\n#end\n\n#foreach ($field in $schema.getFields())\n    /**\n      * Gets the value of the '${this.mangle($field.name(), $schema.isError())}' field.\n#if ($field.doc())      * $field.doc()\n#end\n      * @return The value.\n      */\n    public ${this.javaUnbox($field.schema(), false)} ${this.generateGetMethod($schema, $field)}() {\n      return ${this.mangle($field.name(), $schema.isError())};\n    }\n\n#if (${this.createOptionalGetters})\n    /**\n      * Gets the value of the '${this.mangle($field.name(), $schema.isError())}' field as an Optional&lt;${this.javaType($field.schema())}&gt;.\n#if ($field.doc())      * $field.doc()\n#end\n      * @return The value wrapped in an Optional&lt;${this.javaType($field.schema())}&gt;.\n      */\n    public Optional<${this.javaType($field.schema())}> ${this.generateGetOptionalMethod($schema, $field)}() {\n      return Optional.<${this.javaType($field.schema())}>ofNullable(${this.mangle($field.name(), $schema.isError())});\n    }\n#end\n\n    /**\n      * Sets the value of the '${this.mangle($field.name(), $schema.isError())}' field.\n#if ($field.doc())      * $field.doc()\n#end\n      * @param value The value of '${this.mangle($field.name(), $schema.isError())}'.\n      * @return This builder.\n      */\n    public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder ${this.generateSetMethod($schema, $field)}(${this.javaUnbox($field.schema(), false)} value) {\n      validate(fields()[$field.pos()], value);\n#if (${this.hasBuilder($field.schema())})\n      this.${this.mangle($field.name(), $schema.isError())}Builder = null;\n#end\n      ${this.generateSetterCode($field.schema(), ${this.mangle($field.name(), $schema.isError())}, \"value\")}\n      fieldSetFlags()[$field.pos()] = true;\n      return this;\n    }\n\n    /**\n      * Checks whether the '${this.mangle($field.name(), $schema.isError())}' field has been set.\n#if ($field.doc())      * $field.doc()\n#end\n      * @return True if the '${this.mangle($field.name(), $schema.isError())}' field has been set, false otherwise.\n      */\n    public boolean ${this.generateHasMethod($schema, $field)}() {\n      return fieldSetFlags()[$field.pos()];\n    }\n\n#if (${this.hasBuilder($field.schema())})\n    /**\n     * Gets the Builder instance for the '${this.mangle($field.name(), $schema.isError())}' field and creates one if it doesn't exist yet.\n#if ($field.doc())     * $field.doc()\n#end\n     * @return This builder.\n     */\n    public ${this.javaType($field.schema())}.Builder ${this.generateGetBuilderMethod($schema, $field)}() {\n      if (${this.mangle($field.name(), $schema.isError())}Builder == null) {\n        if (${this.generateHasMethod($schema, $field)}()) {\n          ${this.generateSetBuilderMethod($schema, $field)}(${this.javaType($field.schema())}.newBuilder(${this.mangle($field.name(), $schema.isError())}));\n        } else {\n          ${this.generateSetBuilderMethod($schema, $field)}(${this.javaType($field.schema())}.newBuilder());\n        }\n      }\n      return ${this.mangle($field.name(), $schema.isError())}Builder;\n    }\n\n    /**\n     * Sets the Builder instance for the '${this.mangle($field.name(), $schema.isError())}' field\n#if ($field.doc())     * $field.doc()\n#end\n     * @param value The builder instance that must be set.\n     * @return This builder.\n     */\n\n    public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder ${this.generateSetBuilderMethod($schema, $field)}(${this.javaUnbox($field.schema(), false)}.Builder value) {\n      ${this.generateClearMethod($schema, $field)}();\n      ${this.mangle($field.name(), $schema.isError())}Builder = value;\n      return this;\n    }\n\n    /**\n     * Checks whether the '${this.mangle($field.name(), $schema.isError())}' field has an active Builder instance\n#if ($field.doc())     * $field.doc()\n#end\n     * @return True if the '${this.mangle($field.name(), $schema.isError())}' field has an active Builder instance\n     */\n    public boolean ${this.generateHasBuilderMethod($schema, $field)}() {\n      return ${this.mangle($field.name(), $schema.isError())}Builder != null;\n    }\n#end\n\n    /**\n      * Clears the value of the '${this.mangle($field.name(), $schema.isError())}' field.\n#if ($field.doc())      * $field.doc()\n#end\n      * @return This builder.\n      */\n    public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangle($schema.getName())}.Builder ${this.generateClearMethod($schema, $field)}() {\n#if (${this.isUnboxedJavaTypeNullable($field.schema())})\n      ${this.mangle($field.name(), $schema.isError())} = null;\n#end\n#if (${this.hasBuilder($field.schema())})\n      ${this.mangle($field.name(), $schema.isError())}Builder = null;\n#end\n      fieldSetFlags()[$field.pos()] = false;\n      return this;\n    }\n\n#end\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public ${this.mangle($schema.getName())} build() {\n      try {\n        ${this.mangle($schema.getName())} record = new ${this.mangle($schema.getName())}(#if ($schema.isError())getValue(), getCause()#end);\n#foreach ($field in $schema.getFields())\n#if (${this.hasBuilder($field.schema())})\n        if (${this.mangle($field.name(), $schema.isError())}Builder != null) {\n          try {\n            record.${this.mangle($field.name(), $schema.isError())} = this.${this.mangle($field.name(), $schema.isError())}Builder.build();\n          } catch (org.apache.avro.AvroMissingFieldException e) {\n            e.addParentField(record.getSchema().getField(\"${this.mangle($field.name(), $schema.isError())}\"));\n            throw e;\n          }\n        } else {\n          record.${this.mangle($field.name(), $schema.isError())} = fieldSetFlags()[$field.pos()] ? this.${this.mangle($field.name(), $schema.isError())} : #if(${this.javaType($field.schema())} != \"java.lang.Object\")(${this.javaType($field.schema())})#{end} defaultValue(fields()[$field.pos()]);\n        }\n#else\n        record.${this.mangle($field.name(), $schema.isError())} = fieldSetFlags()[$field.pos()] ? this.${this.mangle($field.name(), $schema.isError())} : #if(${this.javaType($field.schema())} != \"java.lang.Object\")(${this.javaType($field.schema())})#{end} defaultValue(fields()[$field.pos()]);\n#end\n#end\n        return record;\n      } catch (org.apache.avro.AvroMissingFieldException e) {\n        throw e;\n      } catch (java.lang.Exception e) {\n        throw new org.apache.avro.AvroRuntimeException(e);\n      }\n    }\n  }\n\n  @SuppressWarnings(\"unchecked\")\n  private static final org.apache.avro.io.DatumWriter<${this.mangle($schema.getName())}>\n    WRITER$ = (org.apache.avro.io.DatumWriter<${this.mangle($schema.getName())}>)MODEL$.createDatumWriter(SCHEMA$);\n\n  @Override public void writeExternal(java.io.ObjectOutput out)\n    throws java.io.IOException {\n    WRITER$.write(this, SpecificData.getEncoder(out));\n  }\n\n  @SuppressWarnings(\"unchecked\")\n  private static final org.apache.avro.io.DatumReader<${this.mangle($schema.getName())}>\n    READER$ = (org.apache.avro.io.DatumReader<${this.mangle($schema.getName())}>)MODEL$.createDatumReader(SCHEMA$);\n\n  @Override public void readExternal(java.io.ObjectInput in)\n    throws java.io.IOException {\n    READER$.read(this, SpecificData.getDecoder(in));\n  }\n\n#if ($this.isCustomCodable($schema))\n  @Override protected boolean hasCustomCoders() { return true; }\n\n  @Override public void customEncode(org.apache.avro.io.Encoder out)\n    throws java.io.IOException\n  {\n#set ($nv = 0)## Counter to ensure unique var-names\n#set ($maxnv = 0)## Holds high-water mark during recursion\n#foreach ($field in $schema.getFields())\n#set ($n = $this.mangle($field.name(), $schema.isError()))\n#set ($s = $field.schema())\n#encodeVar(0 \"this.${n}\" $s)\n\n#set ($nv = $maxnv)\n#end\n  }\n\n  @Override public void customDecode(org.apache.avro.io.ResolvingDecoder in)\n    throws java.io.IOException\n  {\n    org.apache.avro.Schema.Field[] fieldOrder = in.readFieldOrderIfDiff();\n    if (fieldOrder == null) {\n## Common case: order of fields hasn't changed, so read them in a\n## fixed order according to reader's schema\n#set ($nv = 0)## Counter to ensure unique var-names\n#set ($maxnv = 0)## Holds high-water mark during recursion\n#foreach ($field in $schema.getFields())\n#set ($n = $this.mangle($field.name(), $schema.isError()))\n#set ($s = $field.schema())\n#set ($rs = \"SCHEMA$.getField(\"\"${n}\"\").schema()\")\n#decodeVar(2 \"this.${n}\" $s $rs)\n\n#set ($nv = $maxnv)\n#end\n    } else {\n      for (int i = 0; i < $schema.getFields().size(); i++) {\n        switch (fieldOrder[i].pos()) {\n#set ($fieldno = 0)\n#set ($nv = 0)## Counter to ensure unique var-names\n#set ($maxnv = 0)## Holds high-water mark during recursion\n#foreach ($field in $schema.getFields())\n        case $fieldno:\n#set ($n = $this.mangle($field.name(), $schema.isError()))\n#set ($s = $field.schema())\n#set ($rs = \"SCHEMA$.getField(\"\"${n}\"\").schema()\")\n#decodeVar(6 \"this.${n}\" $s $rs)\n          break;\n\n#set ($nv = $maxnv)\n#set ($fieldno = $fieldno + 1)\n#end\n        default:\n          throw new java.io.IOException(\"Corrupt ResolvingDecoder.\");\n        }\n      }\n    }\n  }\n#end\n}\n\n#macro( encodeVar $indent $var $s )\n#set ($I = $this.indent($indent))\n##### Compound types (array, map, and union) require calls\n##### that will recurse back into this encodeVar macro:\n#if ($s.Type.Name.equals(\"array\"))\n#encodeArray($indent $var $s)\n#elseif ($s.Type.Name.equals(\"map\"))\n#encodeMap($indent $var $s)\n#elseif ($s.Type.Name.equals(\"union\"))\n#encodeUnion($indent $var $s)\n##### Use the generated \"encode\" method as fast way to write\n##### (specific) record types:\n#elseif ($s.Type.Name.equals(\"record\"))\n$I    ${var}.customEncode(out);\n##### For rest of cases, generate calls out.writeXYZ:\n#elseif ($s.Type.Name.equals(\"null\"))\n$I    out.writeNull();\n#elseif ($s.Type.Name.equals(\"boolean\"))\n$I    out.writeBoolean(${var});\n#elseif ($s.Type.Name.equals(\"int\"))\n$I    out.writeInt(${var});\n#elseif ($s.Type.Name.equals(\"long\"))\n$I    out.writeLong(${var});\n#elseif ($s.Type.Name.equals(\"float\"))\n$I    out.writeFloat(${var});\n#elseif ($s.Type.Name.equals(\"double\"))\n$I    out.writeDouble(${var});\n#elseif ($s.Type.Name.equals(\"string\"))\n#if ($this.isStringable($s))\n$I    out.writeString(${var}.toString());\n#else\n$I    out.writeString(${var});\n#end\n#elseif ($s.Type.Name.equals(\"bytes\"))\n$I    out.writeBytes(${var});\n#elseif ($s.Type.Name.equals(\"fixed\"))\n$I    out.writeFixed(${var}.bytes(), 0, ${s.FixedSize});\n#elseif ($s.Type.Name.equals(\"enum\"))\n$I    out.writeEnum(${var}.ordinal());\n#else\n## TODO -- singal a code-gen-time error\n#end\n#end\n\n#macro( encodeArray $indent $var $s )\n#set ($I = $this.indent($indent))\n#set ($et = $this.javaType($s.ElementType))\n$I    long size${nv} = ${var}.size();\n$I    out.writeArrayStart();\n$I    out.setItemCount(size${nv});\n$I    long actualSize${nv} = 0;\n$I    for ($et e${nv}: ${var}) {\n$I      actualSize${nv}++;\n$I      out.startItem();\n#set ($var = \"e${nv}\")\n#set ($nv = $nv + 1)\n#set ($maxnv = $nv)\n#set ($indent = $indent + 2)\n#encodeVar($indent $var $s.ElementType)\n#set ($nv = $nv - 1)\n#set ($indent = $indent - 2)\n#set ($I = $this.indent($indent))\n$I    }\n$I    out.writeArrayEnd();\n$I    if (actualSize${nv} != size${nv})\n$I      throw new java.util.ConcurrentModificationException(\"Array-size written was \" + size${nv} + \", but element count was \" + actualSize${nv} + \".\");\n#end\n\n#macro( encodeMap $indent $var $s )\n#set ($I = $this.indent($indent))\n#set ($kt = $this.getStringType($s))\n#set ($vt = $this.javaType($s.ValueType))\n$I    long size${nv} = ${var}.size();\n$I    out.writeMapStart();\n$I    out.setItemCount(size${nv});\n$I    long actualSize${nv} = 0;\n$I    for (java.util.Map.Entry<$kt, $vt> e${nv}: ${var}.entrySet()) {\n$I      actualSize${nv}++;\n$I      out.startItem();\n#if ($this.isStringable($s))\n$I      out.writeString(e${nv}.getKey().toString());\n#else\n$I      out.writeString(e${nv}.getKey());\n#end\n$I      $vt v${nv} = e${nv}.getValue();\n#set ($var = \"v${nv}\")\n#set ($nv = $nv + 1)\n#set ($maxnv = $nv)\n#set ($indent = $indent + 2)\n#encodeVar($indent $var $s.ValueType)\n#set ($nv = $nv - 1)\n#set ($indent = $indent - 2)\n#set ($I = $this.indent($indent))\n$I    }\n$I    out.writeMapEnd();\n$I    if (actualSize${nv} != size${nv})\n      throw new java.util.ConcurrentModificationException(\"Map-size written was \" + size${nv} + \", but element count was \" + actualSize${nv} + \".\");\n#end\n\n#macro( encodeUnion $indent $var $s )\n#set ($I = $this.indent($indent))\n#set ($et = $this.javaType($s.Types.get($this.getNonNullIndex($s))))\n$I    if (${var} == null) {\n$I      out.writeIndex(#if($this.getNonNullIndex($s)==0)1#{else}0#end);\n$I      out.writeNull();\n$I    } else {\n$I      out.writeIndex(${this.getNonNullIndex($s)});\n#set ($indent = $indent + 2)\n#encodeVar($indent $var $s.Types.get($this.getNonNullIndex($s)))\n#set ($indent = $indent - 2)\n#set ($I = $this.indent($indent))\n$I    }\n#end\n\n\n#macro( decodeVar $indent $var $s $rs )\n#set ($I = $this.indent($indent))\n##### Compound types (array, map, and union) require calls\n##### that will recurse back into this decodeVar macro:\n#if ($s.Type.Name.equals(\"array\"))\n#decodeArray($indent $var $s $rs)\n#elseif ($s.Type.Name.equals(\"map\"))\n#decodeMap($indent $var $s $rs)\n#elseif ($s.Type.Name.equals(\"union\"))\n#decodeUnion($indent $var $s $rs)\n##### Use the generated \"decode\" method as fast way to write\n##### (specific) record types:\n#elseif ($s.Type.Name.equals(\"record\"))\n$I    if (${var} == null) {\n$I      ${var} = new ${this.javaType($s)}();\n$I    }\n$I    ${var}.customDecode(in);\n##### For rest of cases, generate calls in.readXYZ:\n#elseif ($s.Type.Name.equals(\"null\"))\n$I    in.readNull();\n#elseif ($s.Type.Name.equals(\"boolean\"))\n$I    $var = in.readBoolean();\n#elseif ($s.Type.Name.equals(\"int\"))\n$I    $var = in.readInt();\n#elseif ($s.Type.Name.equals(\"long\"))\n$I    $var = in.readLong();\n#elseif ($s.Type.Name.equals(\"float\"))\n$I    $var = in.readFloat();\n#elseif ($s.Type.Name.equals(\"double\"))\n$I    $var = in.readDouble();\n#elseif ($s.Type.Name.equals(\"string\"))\n#decodeString( \"$I\" $var $s )\n#elseif ($s.Type.Name.equals(\"bytes\"))\n$I    $var = in.readBytes(${var});\n#elseif ($s.Type.Name.equals(\"fixed\"))\n$I    if (${var} == null) {\n$I      ${var} = new ${this.javaType($s)}();\n$I    }\n$I    in.readFixed(${var}.bytes(), 0, ${s.FixedSize});\n#elseif ($s.Type.Name.equals(\"enum\"))\n$I    $var = ${this.javaType($s)}.values()[in.readEnum()];\n#else\n## TODO -- singal a code-gen-time error\n#end\n#end\n\n#macro( decodeString $II $var $s )\n#set ($st = ${this.getStringType($s)})\n#if ($this.isStringable($s))\n#if ($st.equals(\"java.net.URI\"))\n$II    try {\n$II      ${var} = new ${st}(in.readString());\n$II    } catch (java.net.URISyntaxException e) {\n$II      throw new java.io.IOException(e.getMessage());\n$II    }\n#elseif ($st.equals(\"java.net.URL\"))\n$II    try {\n$II      ${var} = new ${st}(in.readString());\n$II    } catch (java.net.MalformedURLException e) {\n$II      throw new java.io.IOException(e.getMessage());\n$II    }\n#else\n$II    ${var} = new ${st}(in.readString());\n#end\n#elseif ($st.equals(\"java.lang.String\"))\n$II    $var = in.readString();\n#elseif ($st.equals(\"org.apache.avro.util.Utf8\"))\n$II    $var = in.readString(${var});\n#else\n$II    $var = in.readString(${var} instanceof Utf8 ? (Utf8)${var} : null);\n#end\n#end\n\n#macro( decodeArray $indent $var $s $rs )\n#set ($I = $this.indent($indent))\n#set ($t = $this.javaType($s))\n#set ($et = $this.javaType($s.ElementType))\n#set ($gat = \"SpecificData.Array<${et}>\")\n$I    long size${nv} = in.readArrayStart();\n## Need fresh variable name due to limitation of macro system\n$I    $t a${nv} = ${var};\n$I    if (a${nv} == null) {\n$I      a${nv} = new ${gat}((int)size${nv}, ${rs});\n$I      $var = a${nv};\n$I    } else a${nv}.clear();\n$I    $gat ga${nv} = (a${nv} instanceof SpecificData.Array ? (${gat})a${nv} : null);\n$I    for ( ; 0 < size${nv}; size${nv} = in.arrayNext()) {\n$I      for ( ; size${nv} != 0; size${nv}--) {\n$I        $et e${nv} = (ga${nv} != null ? ga${nv}.peek() : null);\n#set ($var = \"e${nv}\")\n#set ($nv = $nv + 1)\n#set ($maxnv = $nv)\n#set ($indent = $indent + 4)\n#decodeVar($indent $var $s.ElementType \"${rs}.getElementType()\")\n#set ($nv = $nv - 1)\n#set ($indent = $indent - 4)\n#set ($I = $this.indent($indent))\n$I        a${nv}.add(e${nv});\n$I      }\n$I    }\n#end\n\n#macro( decodeMap $indent $var $s $rs )\n#set ($I = $this.indent($indent))\n#set ($t = $this.javaType($s))\n#set ($kt = $this.getStringType($s))\n#set ($vt = $this.javaType($s.ValueType))\n$I    long size${nv} = in.readMapStart();\n$I    $t m${nv} = ${var}; // Need fresh name due to limitation of macro system\n$I    if (m${nv} == null) {\n$I      m${nv} = new java.util.HashMap<${kt},${vt}>((int)size${nv});\n$I      $var = m${nv};\n$I    } else m${nv}.clear();\n$I    for ( ; 0 < size${nv}; size${nv} = in.mapNext()) {\n$I      for ( ; size${nv} != 0; size${nv}--) {\n$I        $kt k${nv} = null;\n#decodeString( \"$I    \" \"k${nv}\" $s )\n$I        $vt v${nv} = null;\n#set ($var = \"v${nv}\")\n#set ($nv = $nv + 1)\n#set ($maxnv = $nv)\n#set ($indent = $indent + 4)\n#decodeVar($indent $var $s.ValueType \"${rs}.getValueType()\")\n#set ($nv = $nv - 1)\n#set ($indent = $indent - 4)\n#set ($I = $this.indent($indent))\n$I        m${nv}.put(k${nv}, v${nv});\n$I      }\n$I    }\n#end\n\n#macro( decodeUnion $indent $var $s $rs )\n#set ($I = $this.indent($indent))\n#set ($et = $this.javaType($s.Types.get($this.getNonNullIndex($s))))\n#set ($si = $this.getNonNullIndex($s))\n$I    if (in.readIndex() != ${si}) {\n$I      in.readNull();\n$I      ${var} = null;\n$I    } else {\n#set ($indent = $indent + 2)\n#decodeVar($indent $var $s.Types.get($si) \"${rs}.getTypes().get(${si})\")\n#set ($indent = $indent - 2)\n#set ($I = $this.indent($indent))\n$I    }\n#end\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/shared.avdl",
    "content": "/**\n * Copyright 2019 Paychex, Inc.\n * Licensed pursuant to the terms of the Apache License, Version 2.0 (the \"License\");\n * your use of the Work is subject to the terms and conditions of the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor\n * provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including,\n * without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,\n * MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible\n * for determining the appropriateness of using or redistributing the Work and assume\n * any risks associated with your exercise of permissions under this License.\n */\n\n@namespace(\"com.example.shared\")\nprotocol SharedProtocol {\n  record SomethingShared {\n    string greeting;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/com/github/davidmc24/gradle/plugin/avro/user.avsc",
    "content": "{\"namespace\": \"example.avro\",\n \"type\": \"record\",\n \"name\": \"User\",\n \"fields\": [\n     {\"name\": \"name\", \"type\": \"string\"},\n     {\"name\": \"favorite_number\",  \"type\": [\"int\", \"null\"]},\n     {\"name\": \"favorite_color\", \"type\": [\"string\", \"null\"]},\n     {\n         \"name\": \"salary\",\n         \"type\": { \"type\": \"bytes\", \"logicalType\": \"decimal\", \"precision\": 4, \"scale\": 2 }\n     },\n     {\n         \"name\": \"birth_date\",\n         \"type\": { \"type\": \"int\", \"logicalType\": \"date\" }\n     }\n ]\n}\n"
  },
  {
    "path": "src/test/resources/examples/inline/Cat.avsc",
    "content": "{\n    \"name\": \"Cat\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\n            \"name\": \"breed\",\n            \"type\": {\n                \"name\": \"Breed\",\n                \"type\": \"enum\",\n                \"symbols\" : [\n                    \"ABYSSINIAN\", \"AMERICAN_SHORTHAIR\", \"BIRMAN\", \"MAINE_COON\", \"ORIENTAL\", \"PERSIAN\", \"RAGDOLL\", \"SIAMESE\", \"SPHYNX\"\n                ]\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/examples/separate/Breed.avsc",
    "content": "{\n    \"name\": \"Breed\",\n    \"namespace\": \"example\",\n    \"type\": \"enum\",\n    \"symbols\" : [\"ABYSSINIAN\", \"AMERICAN_SHORTHAIR\", \"BIRMAN\", \"MAINE_COON\", \"ORIENTAL\", \"PERSIAN\", \"RAGDOLL\", \"SIAMESE\", \"SPHYNX\"]\n}\n"
  },
  {
    "path": "src/test/resources/examples/separate/Cat.avsc",
    "content": "{\n    \"name\": \"Cat\",\n    \"namespace\": \"example\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"breed\", \"type\": \"Breed\"}\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/resolver/SimpleEnum.avsc",
    "content": "{\n    \"name\": \"SimpleEnum\",\n    \"namespace\": \"resolver\",\n    \"type\": \"enum\",\n    \"symbols\" : [\"val1\", \"val2\", \"val3\"]\n}\n"
  },
  {
    "path": "src/test/resources/resolver/SimpleFixed.avsc",
    "content": "{\n    \"name\": \"SimpleFixed\",\n    \"type\": \"fixed\",\n    \"namespace\": \"resolver\",\n    \"size\": 16\n}\n"
  },
  {
    "path": "src/test/resources/resolver/SimpleRecord.avsc",
    "content": "{\n    \"name\": \"SimpleRecord\",\n    \"type\": \"record\",\n    \"namespace\": \"resolver\",\n    \"fields\": [\n        { \"name\": \"field1\", \"type\": \"string\" }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/resolver/UseArray.avsc",
    "content": "{\n    \"name\": \"UseRecord\",\n    \"namespace\": \"resolver\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"field1\", \"type\": {\"type\": \"array\", \"items\": \"SimpleRecord\"} },\n        {\"name\": \"field2\", \"type\": {\"type\": \"array\", \"items\": \"SimpleEnum\"} },\n        {\"name\": \"field3\", \"type\": {\"type\": \"array\", \"items\": \"SimpleFixed\"} }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/resolver/UseArrayWithType.avsc",
    "content": "{\n    \"name\": \"UseRecord\",\n    \"namespace\": \"resolver\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"field1\", \"type\": {\"type\": \"array\", \"items\": {\"type\": \"SimpleRecord\"} } },\n        {\"name\": \"field2\", \"type\": {\"type\": \"array\", \"items\": {\"type\": \"SimpleEnum\"} } },\n        {\"name\": \"field3\", \"type\": {\"type\": \"array\", \"items\": {\"type\": \"SimpleFixed\"} } }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/resolver/UseEnum.avsc",
    "content": "{\n    \"name\": \"UseEnum\",\n    \"namespace\": \"resolver\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"field1\", \"type\": \"SimpleEnum\"}\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/resolver/UseEnumWithType.avsc",
    "content": "{\n    \"name\": \"UseEnum\",\n    \"namespace\": \"resolver\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"field1\", \"type\": {\"type\": \"SimpleEnum\"} }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/resolver/UseFixed.avsc",
    "content": "{\n    \"name\": \"UseEnum\",\n    \"namespace\": \"resolver\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"field1\", \"type\": \"SimpleFixed\"}\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/resolver/UseFixedWithType.avsc",
    "content": "{\n    \"name\": \"UseEnum\",\n    \"namespace\": \"resolver\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"field1\", \"type\": {\"type\": \"SimpleFixed\"} }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/resolver/UseMap.avsc",
    "content": "{\n    \"name\": \"UseRecord\",\n    \"namespace\": \"resolver\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"field1\", \"type\": {\"type\": \"map\", \"values\": \"SimpleRecord\"} },\n        {\"name\": \"field2\", \"type\": {\"type\": \"map\", \"values\": \"SimpleEnum\"} },\n        {\"name\": \"field3\", \"type\": {\"type\": \"map\", \"values\": \"SimpleFixed\"} }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/resolver/UseMapWithType.avsc",
    "content": "{\n    \"name\": \"UseRecord\",\n    \"namespace\": \"resolver\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"field1\", \"type\": {\"type\": \"map\", \"values\": {\"type\": \"SimpleRecord\"} } },\n        {\"name\": \"field2\", \"type\": {\"type\": \"map\", \"values\": {\"type\": \"SimpleEnum\"} } },\n        {\"name\": \"field3\", \"type\": {\"type\": \"map\", \"values\": {\"type\": \"SimpleFixed\"} } }\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/resolver/UseRecord.avsc",
    "content": "{\n    \"name\": \"UseRecord\",\n    \"namespace\": \"resolver\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"field1\", \"type\": \"SimpleRecord\"}\n    ]\n}\n"
  },
  {
    "path": "src/test/resources/resolver/UseRecordWithType.avsc",
    "content": "{\n    \"name\": \"UseRecord\",\n    \"namespace\": \"resolver\",\n    \"type\": \"record\",\n    \"fields\" : [\n        {\"name\": \"field1\", \"type\": {\"type\": \"SimpleRecord\"} }\n    ]\n}\n"
  },
  {
    "path": "test-project/build.gradle",
    "content": "plugins {\n    id \"idea\"\n    id \"com.github.davidmc24.gradle.plugin.avro\" version \"1.2.0\"\n//    id \"com.github.davidmc24.gradle.plugin.avro\" version \"1.2.1-SNAPSHOT\"\n}\n\nrepositories {\n    mavenCentral()\n}\n\next {\n    avroVersion = \"1.11.0\"\n}\n\ndependencies {\n    implementation \"org.apache.avro:avro:${avroVersion}\"\n    implementation \"org.apache.avro:avro-tools:${avroVersion}\"\n    testImplementation \"org.junit.jupiter:junit-jupiter:5.6.2\"\n}\n\ntest {\n    useJUnitPlatform()\n    testLogging {\n        events \"passed\", \"skipped\", \"failed\"\n    }\n}\n\navro {\n    stringType = \"CharSequence\"\n    fieldVisibility = \"private\"\n    customConversion org.apache.avro.Conversions.UUIDConversion\n}\n\njava {\n//    withJavadocJar()\n    withSourcesJar()\n}\n"
  },
  {
    "path": "test-project/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.1.1-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "test-project/gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\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\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -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\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MSYS* | MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -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    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif [ \"$cygwin\" = \"true\" -o \"$msys\" = \"true\" ] ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=`expr $i + 1`\n    done\n    case $i in\n        0) set -- ;;\n        1) set -- \"$args0\" ;;\n        2) set -- \"$args0\" \"$args1\" ;;\n        3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=`save \"$@\"`\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "test-project/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n\r\n@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "test-project/settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        mavenCentral()\n        mavenLocal()\n    }\n}\n\nrootProject.name = \"test-project\"\n"
  },
  {
    "path": "test-project/src/main/avro/BuggyRecord.avsc",
    "content": "{\n   \"namespace\":\"com.example\",\n   \"type\":\"record\",\n   \"name\":\"BuggyRecord\",\n   \"fields\":[\n      {\n         \"name\":\"my_mandatory_date\",\n         \"type\":{\n            \"type\":\"long\",\n            \"logicalType\":\"timestamp-millis\"\n         },\n         \"default\":1502250227187\n      },\n      {\n         \"name\":\"my_optional_date\",\n         \"type\":[\n            {\n               \"type\":\"long\",\n               \"logicalType\":\"timestamp-millis\"\n            },\n            \"null\"\n         ],\n         \"default\":1502250227187\n      }\n   ]\n}\n"
  },
  {
    "path": "test-project/src/main/avro/BuggyRecordWorkaround.avsc",
    "content": "{\n   \"namespace\":\"com.example\",\n   \"type\":\"record\",\n   \"name\":\"BuggyRecordWorkaround\",\n   \"fields\":[\n      {\n         \"name\":\"my_mandatory_date\",\n         \"type\":{\n            \"type\":\"long\",\n            \"logicalType\":\"timestamp-millis\"\n         },\n         \"default\":1502250227187\n      },\n      {\n         \"name\":\"my_optional_date\",\n         \"type\":[\n            \"null\",\n            {\n               \"type\":\"long\",\n               \"logicalType\":\"timestamp-millis\"\n            }\n         ],\n         \"default\":null\n      }\n   ]\n}\n"
  },
  {
    "path": "test-project/src/main/avro/Messages.avsc",
    "content": "{\n    \"type\": \"record\",\n\t  \"name\": \"Messages\",\n\t  \"namespace\": \"com.somedomain\",\n\t  \"fields\": [\n\t\t{\n\t\t  \"name\": \"start\",\n\t\t  \"type\": {\n\t\t\t\"type\": \"long\",\n\t\t\t\"logicalType\": \"timestamp-millis\"\n\t\t  }\n\t\t},\n\t\t{\n\t\t  \"name\": \"end\",\n\t\t  \"type\": [\n\t\t\t\"null\",\n\t\t\t{\n\t\t\t  \"type\": \"long\",\n\t\t\t  \"logicalType\": \"timestamp-millis\"\n\t\t\t}\n\t\t  ],\n\t\t  \"default\": null\n\t\t}\n\t  ]\n\t}\n}\n"
  },
  {
    "path": "test-project/src/main/avro/UUIDTestRecord.avsc",
    "content": "{\n  \"name\": \"UUIDTestRecord\",\n  \"type\": \"record\",\n  \"fields\": [\n    {\n      \"name\": \"id\",\n      \"type\": {\n        \"type\": \"string\",\n        \"logicalType\": \"uuid\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test-project/src/main/java/project/SystemUtil.java",
    "content": "package project;\n\nimport java.security.Permission;\n\nclass SystemUtil {\n    private static final String PERMISSION_PREFIX = \"exitVM.\";\n\n    static class ExitTrappedException extends SecurityException {\n        private final int status;\n\n        ExitTrappedException(int status) {\n            super(\"Trapped System.exit(\" + status + \")\");\n            this.status = status;\n        }\n\n        int getStatus() {\n            return status;\n        }\n    }\n\n    static void forbidSystemExitCall() {\n        final SecurityManager securityManager = new SecurityManager() {\n            public void checkPermission(Permission permission) {\n                String permissionName = permission.getName();\n                if (permissionName.startsWith(PERMISSION_PREFIX)) {\n                    String suffix = permissionName.substring(PERMISSION_PREFIX.length());\n                    int status = Integer.parseInt(suffix);\n                    throw new ExitTrappedException(status);\n                }\n            }\n        };\n        System.setSecurityManager(securityManager);\n    }\n\n    static void allowSystemExitCall() {\n        System.setSecurityManager(null);\n    }\n}\n"
  },
  {
    "path": "test-project/src/test/java/project/CLIComparisonTest.java",
    "content": "package project;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport static project.CLIUtil.runCLITool;\n\npublic class CLIComparisonTest {\n    @TempDir\n    Path cliGeneratedDir;\n    Path schemaDir = Paths.get(\"src/main/avro\");\n    Path pluginGeneratedDir = Paths.get(\"build/generated-main-avro-java\");\n\n    @SuppressWarnings(\"unused\")\n    private static Stream<Arguments> compareSpecificCompilerOutput() {\n        return Stream.of(\n            // From https://stackoverflow.com/questions/45581437/how-to-specify-converter-for-default-value-in-avro-union-logical-type-fields\n            Arguments.of(\"BuggyRecord.avsc\", \"com/example/BuggyRecord.java\", \"compile schema\".split(\" \")),\n            // From https://github.com/davidmc24/gradle-avro-plugin/issues/120\n            Arguments.of(\"Messages.avsc\", \"com/somedomain/Messages.java\", \"compile schema\".split(\" \"))\n        );\n    }\n\n    @ParameterizedTest\n    @MethodSource\n    void compareSpecificCompilerOutput(String schemaPath, String generatedPath, String... toolArgs) throws Exception {\n        Path schemaFile = schemaDir.resolve(schemaPath);\n        Path pluginGeneratedFile = pluginGeneratedDir.resolve(generatedPath);\n        Path cliGeneratedFile = cliGeneratedDir.resolve(generatedPath);\n\n        List<String> args = new ArrayList<>(Arrays.asList(toolArgs));\n        args.add(schemaFile.toString());\n        args.add(cliGeneratedDir.toString());\n        runCLITool(args.toArray(new String[0]));\n        \n        String pluginGeneratedContent = readFile(pluginGeneratedFile);\n        String cliGeneratedContent = readFile(cliGeneratedFile);\n        Assertions.assertEquals(cliGeneratedContent, pluginGeneratedContent);\n    }\n\n    private static String readFile(Path file) throws Exception {\n        return new String(Files.readAllBytes(file), StandardCharsets.UTF_8);\n    }\n}\n"
  },
  {
    "path": "test-project/src/test/java/project/CLIUtil.java",
    "content": "package project;\n\nimport org.apache.avro.tool.Main;\nimport org.junit.jupiter.api.Assertions;\n\nclass CLIUtil {\n    private static final int STATUS_SUCCESS = 0;\n\n    static void runCLITool(String... args) throws Exception {\n        SystemUtil.forbidSystemExitCall();\n        try {\n            Main.main(args);\n        } catch (SystemUtil.ExitTrappedException ex) {\n            Assertions.assertEquals(STATUS_SUCCESS, ex.getStatus(), \"CLI tool failed\");\n        } finally {\n            SystemUtil.allowSystemExitCall();\n        }\n    }\n}\n"
  },
  {
    "path": "test-project/src/test/java/project/RandomRecordTest.java",
    "content": "package project;\n\nimport org.apache.avro.specific.SpecificRecord;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport static project.CLIUtil.runCLITool;\n\npublic class RandomRecordTest {\n    @TempDir\n    Path cliGeneratedDir;\n    Path schemaDir = Paths.get(\"src/main/avro\");\n    \n    @SuppressWarnings(\"unused\")\n    private static Stream<Arguments> generateRandomRecords() {\n        return Stream.of(\n            // From https://stackoverflow.com/questions/45581437/how-to-specify-converter-for-default-value-in-avro-union-logical-type-fields\n            Arguments.of(\"BuggyRecord.avsc\"),\n            // From https://github.com/davidmc24/gradle-avro-plugin/issues/120\n            Arguments.of(\"Messages.avsc\")\n        );\n    }\n\n    @ParameterizedTest\n    @MethodSource\n    <T extends SpecificRecord> void generateRandomRecords(String schemaPath) throws Exception {\n        Path schemaFile = schemaDir.resolve(schemaPath);\n        Path outputFile = cliGeneratedDir.resolve(\"random.avro\");\n        List<String> args = new ArrayList<>();\n        args.add(\"random\");\n        args.add(\"--count\");\n        args.add(\"1\");\n        args.add(\"--schema-file\");\n        args.add(schemaFile.toString());\n        args.add(outputFile.toString());\n        runCLITool(args.toArray(new String[0]));\n    }\n}\n"
  },
  {
    "path": "test-project/src/test/java/project/RecordTest.java",
    "content": "package project;\n\nimport com.example.BuggyRecord;\nimport com.example.BuggyRecordWorkaround;\nimport com.somedomain.Messages;\nimport org.apache.avro.Schema;\nimport org.apache.avro.io.Encoder;\nimport org.apache.avro.io.EncoderFactory;\nimport org.apache.avro.specific.SpecificDatumWriter;\nimport org.apache.avro.specific.SpecificRecord;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.OutputStream;\nimport java.time.Instant;\nimport java.util.stream.Stream;\n\npublic class RecordTest {\n    private static final EncoderFactory encoderFactory = EncoderFactory.get();\n\n    @SuppressWarnings(\"unused\")\n    private static Stream<Arguments> buildAndWriteRecord() {\n        return Stream.of(\n            // From https://stackoverflow.com/questions/45581437/how-to-specify-converter-for-default-value-in-avro-union-logical-type-fields\n            // Broken due to an Avro bug\n            Arguments.of(BuggyRecord.newBuilder().setMyMandatoryDate(Instant.now()).build()),\n            // Broken due to an Avro bug\n            Arguments.of(BuggyRecordWorkaround.newBuilder().setMyMandatoryDate(Instant.now()).build()),\n            // From https://github.com/davidmc24/gradle-avro-plugin/issues/120\n            // Broken due to an Avro bug\n            Arguments.of(Messages.newBuilder().setStart(Instant.now()).build())\n        );\n    }\n\n    // Broken due to an Avro bug\n    @Disabled\n    @ParameterizedTest\n    @MethodSource\n    <T extends SpecificRecord> void buildAndWriteRecord(T record) throws Exception {\n        Schema schema = record.getSchema();\n        SpecificDatumWriter<T> writer = new SpecificDatumWriter<>();\n        OutputStream outputStream = new ByteArrayOutputStream();\n        Encoder jsonEncoder = encoderFactory.jsonEncoder(schema, outputStream, true);\n        Encoder validatingEncoder = encoderFactory.validatingEncoder(schema, jsonEncoder);\n        writer.write(record, validatingEncoder);\n    }\n}\n"
  },
  {
    "path": "test-project-kotlin/build.gradle.kts",
    "content": "plugins {\n    id(\"idea\")\n    id(\"com.github.davidmc24.gradle.plugin.avro\") version (\"1.2.0\")\n//    id \"com.github.davidmc24.gradle.plugin.avro\" version \"1.2.1-SNAPSHOT\"\n}\n\nrepositories {\n    mavenCentral()\n}\n\nproject.ext.set(\"avroVersion\", \"1.11.0\")\ndependencies {\n    implementation(\"org.apache.avro:avro:${project.ext.get(\"avroVersion\")}\")\n    implementation(\"org.apache.avro:avro-tools:${project.ext.get(\"avroVersion\")}\")\n    testImplementation(\"org.junit.jupiter:junit-jupiter:5.6.2\")\n}\n\ntasks.test {\n    useJUnitPlatform()\n    testLogging {\n        events(\"passed\", \"skipped\", \"failed\")\n    }\n}\n\navro {\n    stringType.set(\"CharSequence\")\n    fieldVisibility.set(\"private\")\n    customConversion(org.apache.avro.Conversions.UUIDConversion::class.java)\n}\n"
  },
  {
    "path": "test-project-kotlin/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.1.1-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "test-project-kotlin/gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\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\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -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\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MSYS* | MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -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    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif [ \"$cygwin\" = \"true\" -o \"$msys\" = \"true\" ] ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=`expr $i + 1`\n    done\n    case $i in\n        0) set -- ;;\n        1) set -- \"$args0\" ;;\n        2) set -- \"$args0\" \"$args1\" ;;\n        3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=`save \"$@\"`\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "test-project-kotlin/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n\r\n@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "test-project-kotlin/settings.gradle.kts",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        mavenCentral()\n        mavenLocal()\n    }\n}\n\nrootProject.name = \"test-project\"\n"
  },
  {
    "path": "test-project-kotlin/src/main/avro/BuggyRecord.avsc",
    "content": "{\n   \"namespace\":\"com.example\",\n   \"type\":\"record\",\n   \"name\":\"BuggyRecord\",\n   \"fields\":[\n      {\n         \"name\":\"my_mandatory_date\",\n         \"type\":{\n            \"type\":\"long\",\n            \"logicalType\":\"timestamp-millis\"\n         },\n         \"default\":1502250227187\n      },\n      {\n         \"name\":\"my_optional_date\",\n         \"type\":[\n            {\n               \"type\":\"long\",\n               \"logicalType\":\"timestamp-millis\"\n            },\n            \"null\"\n         ],\n         \"default\":1502250227187\n      }\n   ]\n}\n"
  },
  {
    "path": "test-project-kotlin/src/main/avro/BuggyRecordWorkaround.avsc",
    "content": "{\n   \"namespace\":\"com.example\",\n   \"type\":\"record\",\n   \"name\":\"BuggyRecordWorkaround\",\n   \"fields\":[\n      {\n         \"name\":\"my_mandatory_date\",\n         \"type\":{\n            \"type\":\"long\",\n            \"logicalType\":\"timestamp-millis\"\n         },\n         \"default\":1502250227187\n      },\n      {\n         \"name\":\"my_optional_date\",\n         \"type\":[\n            \"null\",\n            {\n               \"type\":\"long\",\n               \"logicalType\":\"timestamp-millis\"\n            }\n         ],\n         \"default\":null\n      }\n   ]\n}\n"
  },
  {
    "path": "test-project-kotlin/src/main/avro/Messages.avsc",
    "content": "{\n    \"type\": \"record\",\n\t  \"name\": \"Messages\",\n\t  \"namespace\": \"com.somedomain\",\n\t  \"fields\": [\n\t\t{\n\t\t  \"name\": \"start\",\n\t\t  \"type\": {\n\t\t\t\"type\": \"long\",\n\t\t\t\"logicalType\": \"timestamp-millis\"\n\t\t  }\n\t\t},\n\t\t{\n\t\t  \"name\": \"end\",\n\t\t  \"type\": [\n\t\t\t\"null\",\n\t\t\t{\n\t\t\t  \"type\": \"long\",\n\t\t\t  \"logicalType\": \"timestamp-millis\"\n\t\t\t}\n\t\t  ],\n\t\t  \"default\": null\n\t\t}\n\t  ]\n\t}\n}\n"
  },
  {
    "path": "test-project-kotlin/src/main/avro/UUIDTestRecord.avsc",
    "content": "{\n  \"name\": \"UUIDTestRecord\",\n  \"type\": \"record\",\n  \"fields\": [\n    {\n      \"name\": \"id\",\n      \"type\": {\n        \"type\": \"string\",\n        \"logicalType\": \"uuid\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test-project-kotlin/src/main/java/project/SystemUtil.java",
    "content": "package project;\n\nimport java.security.Permission;\n\nclass SystemUtil {\n    private static final String PERMISSION_PREFIX = \"exitVM.\";\n\n    static class ExitTrappedException extends SecurityException {\n        private final int status;\n\n        ExitTrappedException(int status) {\n            super(\"Trapped System.exit(\" + status + \")\");\n            this.status = status;\n        }\n\n        int getStatus() {\n            return status;\n        }\n    }\n\n    static void forbidSystemExitCall() {\n        final SecurityManager securityManager = new SecurityManager() {\n            public void checkPermission(Permission permission) {\n                String permissionName = permission.getName();\n                if (permissionName.startsWith(PERMISSION_PREFIX)) {\n                    String suffix = permissionName.substring(PERMISSION_PREFIX.length());\n                    int status = Integer.parseInt(suffix);\n                    throw new ExitTrappedException(status);\n                }\n            }\n        };\n        System.setSecurityManager(securityManager);\n    }\n\n    static void allowSystemExitCall() {\n        System.setSecurityManager(null);\n    }\n}\n"
  },
  {
    "path": "test-project-kotlin/src/test/java/project/CLIComparisonTest.java",
    "content": "package project;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport static project.CLIUtil.runCLITool;\n\npublic class CLIComparisonTest {\n    @TempDir\n    Path cliGeneratedDir;\n    Path schemaDir = Paths.get(\"src/main/avro\");\n    Path pluginGeneratedDir = Paths.get(\"build/generated-main-avro-java\");\n\n    @SuppressWarnings(\"unused\")\n    private static Stream<Arguments> compareSpecificCompilerOutput() {\n        return Stream.of(\n            // From https://stackoverflow.com/questions/45581437/how-to-specify-converter-for-default-value-in-avro-union-logical-type-fields\n            Arguments.of(\"BuggyRecord.avsc\", \"com/example/BuggyRecord.java\", \"compile schema\".split(\" \")),\n            // From https://github.com/davidmc24/gradle-avro-plugin/issues/120\n            Arguments.of(\"Messages.avsc\", \"com/somedomain/Messages.java\", \"compile schema\".split(\" \"))\n        );\n    }\n\n    @ParameterizedTest\n    @MethodSource\n    void compareSpecificCompilerOutput(String schemaPath, String generatedPath, String... toolArgs) throws Exception {\n        Path schemaFile = schemaDir.resolve(schemaPath);\n        Path pluginGeneratedFile = pluginGeneratedDir.resolve(generatedPath);\n        Path cliGeneratedFile = cliGeneratedDir.resolve(generatedPath);\n\n        List<String> args = new ArrayList<>(Arrays.asList(toolArgs));\n        args.add(schemaFile.toString());\n        args.add(cliGeneratedDir.toString());\n        runCLITool(args.toArray(new String[0]));\n        \n        String pluginGeneratedContent = readFile(pluginGeneratedFile);\n        String cliGeneratedContent = readFile(cliGeneratedFile);\n        Assertions.assertEquals(cliGeneratedContent, pluginGeneratedContent);\n    }\n\n    private static String readFile(Path file) throws Exception {\n        return new String(Files.readAllBytes(file), StandardCharsets.UTF_8);\n    }\n}\n"
  },
  {
    "path": "test-project-kotlin/src/test/java/project/CLIUtil.java",
    "content": "package project;\n\nimport org.apache.avro.tool.Main;\nimport org.junit.jupiter.api.Assertions;\n\nclass CLIUtil {\n    private static final int STATUS_SUCCESS = 0;\n\n    static void runCLITool(String... args) throws Exception {\n        SystemUtil.forbidSystemExitCall();\n        try {\n            Main.main(args);\n        } catch (SystemUtil.ExitTrappedException ex) {\n            Assertions.assertEquals(STATUS_SUCCESS, ex.getStatus(), \"CLI tool failed\");\n        } finally {\n            SystemUtil.allowSystemExitCall();\n        }\n    }\n}\n"
  },
  {
    "path": "test-project-kotlin/src/test/java/project/RandomRecordTest.java",
    "content": "package project;\n\nimport org.apache.avro.specific.SpecificRecord;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport static project.CLIUtil.runCLITool;\n\npublic class RandomRecordTest {\n    @TempDir\n    Path cliGeneratedDir;\n    Path schemaDir = Paths.get(\"src/main/avro\");\n    \n    @SuppressWarnings(\"unused\")\n    private static Stream<Arguments> generateRandomRecords() {\n        return Stream.of(\n            // From https://stackoverflow.com/questions/45581437/how-to-specify-converter-for-default-value-in-avro-union-logical-type-fields\n            Arguments.of(\"BuggyRecord.avsc\"),\n            // From https://github.com/davidmc24/gradle-avro-plugin/issues/120\n            Arguments.of(\"Messages.avsc\")\n        );\n    }\n\n    @ParameterizedTest\n    @MethodSource\n    <T extends SpecificRecord> void generateRandomRecords(String schemaPath) throws Exception {\n        Path schemaFile = schemaDir.resolve(schemaPath);\n        Path outputFile = cliGeneratedDir.resolve(\"random.avro\");\n        List<String> args = new ArrayList<>();\n        args.add(\"random\");\n        args.add(\"--count\");\n        args.add(\"1\");\n        args.add(\"--schema-file\");\n        args.add(schemaFile.toString());\n        args.add(outputFile.toString());\n        runCLITool(args.toArray(new String[0]));\n    }\n}\n"
  },
  {
    "path": "test-project-kotlin/src/test/java/project/RecordTest.java",
    "content": "package project;\n\nimport com.example.BuggyRecord;\nimport com.example.BuggyRecordWorkaround;\nimport com.somedomain.Messages;\nimport org.apache.avro.Schema;\nimport org.apache.avro.io.Encoder;\nimport org.apache.avro.io.EncoderFactory;\nimport org.apache.avro.specific.SpecificDatumWriter;\nimport org.apache.avro.specific.SpecificRecord;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.OutputStream;\nimport java.time.Instant;\nimport java.util.stream.Stream;\n\npublic class RecordTest {\n    private static final EncoderFactory encoderFactory = EncoderFactory.get();\n\n    @SuppressWarnings(\"unused\")\n    private static Stream<Arguments> buildAndWriteRecord() {\n        return Stream.of(\n            // From https://stackoverflow.com/questions/45581437/how-to-specify-converter-for-default-value-in-avro-union-logical-type-fields\n            // Broken due to an Avro bug\n            Arguments.of(BuggyRecord.newBuilder().setMyMandatoryDate(Instant.now()).build()),\n            // Broken due to an Avro bug\n            Arguments.of(BuggyRecordWorkaround.newBuilder().setMyMandatoryDate(Instant.now()).build()),\n            // From https://github.com/davidmc24/gradle-avro-plugin/issues/120\n            // Broken due to an Avro bug\n            Arguments.of(Messages.newBuilder().setStart(Instant.now()).build())\n        );\n    }\n\n    // Broken due to an Avro bug\n    @Disabled\n    @ParameterizedTest\n    @MethodSource\n    <T extends SpecificRecord> void buildAndWriteRecord(T record) throws Exception {\n        Schema schema = record.getSchema();\n        SpecificDatumWriter<T> writer = new SpecificDatumWriter<>();\n        OutputStream outputStream = new ByteArrayOutputStream();\n        Encoder jsonEncoder = encoderFactory.jsonEncoder(schema, outputStream, true);\n        Encoder validatingEncoder = encoderFactory.validatingEncoder(schema, jsonEncoder);\n        writer.write(record, validatingEncoder);\n    }\n}\n"
  }
]