diff --git a/doc/package-lock.json b/doc/package-lock.json
deleted file mode 100644
index 9153207d6cb..00000000000
--- a/doc/package-lock.json
+++ /dev/null
@@ -1,927 +0,0 @@
-{
- "name": "doc",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "devDependencies": {
- "autoprefixer": "^10.4.22",
- "postcss": "^8.5.6",
- "postcss-cli": "^11.0.1"
- }
- },
- "node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/anymatch": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
- "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/autoprefixer": {
- "version": "10.4.22",
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.22.tgz",
- "integrity": "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/autoprefixer"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "browserslist": "^4.27.0",
- "caniuse-lite": "^1.0.30001754",
- "fraction.js": "^5.3.4",
- "normalize-range": "^0.1.2",
- "picocolors": "^1.1.1",
- "postcss-value-parser": "^4.2.0"
- },
- "bin": {
- "autoprefixer": "bin/autoprefixer"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- },
- "peerDependencies": {
- "postcss": "^8.1.0"
- }
- },
- "node_modules/baseline-browser-mapping": {
- "version": "2.8.20",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.20.tgz",
- "integrity": "sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ==",
- "dev": true,
- "license": "Apache-2.0",
- "bin": {
- "baseline-browser-mapping": "dist/cli.js"
- }
- },
- "node_modules/binary-extensions": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
- "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/braces": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fill-range": "^7.1.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/browserslist": {
- "version": "4.27.0",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz",
- "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "baseline-browser-mapping": "^2.8.19",
- "caniuse-lite": "^1.0.30001751",
- "electron-to-chromium": "^1.5.238",
- "node-releases": "^2.0.26",
- "update-browserslist-db": "^1.1.4"
- },
- "bin": {
- "browserslist": "cli.js"
- },
- "engines": {
- "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
- }
- },
- "node_modules/caniuse-lite": {
- "version": "1.0.30001755",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001755.tgz",
- "integrity": "sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "CC-BY-4.0"
- },
- "node_modules/chokidar": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
- "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- },
- "engines": {
- "node": ">= 8.10.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/dependency-graph": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz",
- "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/electron-to-chromium": {
- "version": "1.5.240",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.240.tgz",
- "integrity": "sha512-OBwbZjWgrCOH+g6uJsA2/7Twpas2OlepS9uvByJjR2datRDuKGYeD+nP8lBBks2qnB7bGJNHDUx7c/YLaT3QMQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/escalade": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
- "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/fill-range": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/fraction.js": {
- "version": "5.3.4",
- "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
- "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "*"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/rawify"
- }
- },
- "node_modules/fs-extra": {
- "version": "11.3.2",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz",
- "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=14.14"
- }
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "6.* || 8.* || >= 10.*"
- }
- },
- "node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "binary-extensions": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/lilconfig": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
- "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/antonk52"
- }
- },
- "node_modules/nanoid": {
- "version": "3.3.11",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
- "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
- "node_modules/node-releases": {
- "version": "2.0.26",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.26.tgz",
- "integrity": "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/normalize-range": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
- "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/picocolors": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/postcss": {
- "version": "8.5.6",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
- "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "nanoid": "^3.3.11",
- "picocolors": "^1.1.1",
- "source-map-js": "^1.2.1"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
- "node_modules/postcss-cli": {
- "version": "11.0.1",
- "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-11.0.1.tgz",
- "integrity": "sha512-0UnkNPSayHKRe/tc2YGW6XnSqqOA9eqpiRMgRlV1S6HdGi16vwJBx7lviARzbV1HpQHqLLRH3o8vTcB0cLc+5g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "chokidar": "^3.3.0",
- "dependency-graph": "^1.0.0",
- "fs-extra": "^11.0.0",
- "picocolors": "^1.0.0",
- "postcss-load-config": "^5.0.0",
- "postcss-reporter": "^7.0.0",
- "pretty-hrtime": "^1.0.3",
- "read-cache": "^1.0.0",
- "slash": "^5.0.0",
- "tinyglobby": "^0.2.12",
- "yargs": "^17.0.0"
- },
- "bin": {
- "postcss": "index.js"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "postcss": "^8.0.0"
- }
- },
- "node_modules/postcss-load-config": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-5.1.0.tgz",
- "integrity": "sha512-G5AJ+IX0aD0dygOE0yFZQ/huFFMSNneyfp0e3/bT05a8OfPC5FUoZRPfGijUdGOJNMewJiwzcHJXFafFzeKFVA==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "lilconfig": "^3.1.1",
- "yaml": "^2.4.2"
- },
- "engines": {
- "node": ">= 18"
- },
- "peerDependencies": {
- "jiti": ">=1.21.0",
- "postcss": ">=8.0.9",
- "tsx": "^4.8.1"
- },
- "peerDependenciesMeta": {
- "jiti": {
- "optional": true
- },
- "postcss": {
- "optional": true
- },
- "tsx": {
- "optional": true
- }
- }
- },
- "node_modules/postcss-reporter": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.1.0.tgz",
- "integrity": "sha512-/eoEylGWyy6/DOiMP5lmFRdmDKThqgn7D6hP2dXKJI/0rJSO1ADFNngZfDzxL0YAxFvws+Rtpuji1YIHj4mySA==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "picocolors": "^1.0.0",
- "thenby": "^1.3.4"
- },
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "postcss": "^8.1.0"
- }
- },
- "node_modules/postcss-value-parser": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/pretty-hrtime": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
- "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/read-cache": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
- "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "pify": "^2.3.0"
- }
- },
- "node_modules/readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "picomatch": "^2.2.1"
- },
- "engines": {
- "node": ">=8.10.0"
- }
- },
- "node_modules/require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/slash": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
- "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14.16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/source-map-js": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
- "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/thenby": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz",
- "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==",
- "dev": true,
- "license": "Apache-2.0"
- },
- "node_modules/tinyglobby": {
- "version": "0.2.15",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
- "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fdir": "^6.5.0",
- "picomatch": "^4.0.3"
- },
- "engines": {
- "node": ">=12.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/SuperchupuDev"
- }
- },
- "node_modules/tinyglobby/node_modules/fdir": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "node_modules/tinyglobby/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/update-browserslist-db": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
- "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "escalade": "^3.2.0",
- "picocolors": "^1.1.1"
- },
- "bin": {
- "update-browserslist-db": "cli.js"
- },
- "peerDependencies": {
- "browserslist": ">= 4.21.0"
- }
- },
- "node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/yaml": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
- "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "yaml": "bin.mjs"
- },
- "engines": {
- "node": ">= 14.6"
- }
- },
- "node_modules/yargs": {
- "version": "17.7.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
- "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cliui": "^8.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
- "y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=12"
- }
- }
- }
-}
diff --git a/lang/java/.gitignore b/lang/java/.gitignore
index 5a536dfcc82..250e79d5ba7 100644
--- a/lang/java/.gitignore
+++ b/lang/java/.gitignore
@@ -20,3 +20,6 @@
dependency-reduced-pom.xml
mapred/userlogs/
tools/userlogs/
+gradle-plugin/build
+gradle-plugin/.kotlin
+gradle-plugin/.gradle
diff --git a/lang/java/gradle-plugin/README.md b/lang/java/gradle-plugin/README.md
new file mode 100644
index 00000000000..7dbc3ca9101
--- /dev/null
+++ b/lang/java/gradle-plugin/README.md
@@ -0,0 +1,59 @@
+# Avro Gradle plugin (in development)
+
+Gradle plugin that generates Java code from Avro schemas
+
+## Usage
+
+### Add avro extension
+In `build.gradle.kts`:
+
+Add plugin
+
+```kotlin
+plugins {
+ id("eu.eventloopsoftware.avro-gradle-plugin") version "0.0.2"
+}
+```
+Add Avro dependency
+
+```kotlin
+implementation("org.apache.avro:avro:1.12.1")
+```
+Configure Avro Gradle plugin
+```kotlin
+avro {
+ sourceDirectory = "src/main/avro"
+ // All properties are available in `GradlePluginExtension.kt`
+}
+```
+
+In `settings.gradle.kts`:
+
+Plugin is published on Maven Central:
+```kotlin
+pluginManagement {
+ repositories {
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+```
+
+
+
+### Add a task hook
+For Intellij to recognize the newly generated Java files add this to `build.gradle.kts`:
+
+```kotlin
+tasks.named("compileKotlin") { dependsOn(tasks.named("avroGenerateJavaClasses")) }
+```
+
+### Generate Java classes
+
+`./gradlew avroGenerateJavaClasses`
+
+
+## Example project that uses avro-gradle-plugin
+https://codeberg.org/frevib/use-gradle-plugin-test
+
+
diff --git a/lang/java/gradle-plugin/build.gradle.kts b/lang/java/gradle-plugin/build.gradle.kts
new file mode 100644
index 00000000000..65fea48c6f1
--- /dev/null
+++ b/lang/java/gradle-plugin/build.gradle.kts
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ kotlin("jvm") version "2.2.10"
+ `java-gradle-plugin`
+ `maven-publish`
+}
+
+group = "org.apache.avro"
+version = "1.13.0-SNAPSHOT"
+
+repositories {
+ mavenCentral()
+ mavenLocal()
+}
+
+dependencies {
+ //implementation("org.gradle:gradle-tooling-api:7.1.1")
+ implementation("org.apache.avro:avro-compiler:${version}")
+ testImplementation(kotlin("test"))
+}
+
+tasks.test {
+ useJUnitPlatform()
+}
+kotlin {
+ jvmToolchain(17)
+}
+
+java {
+ withSourcesJar()
+}
+
+
+gradlePlugin {
+ plugins {
+ create("gradlePlugin") {
+ id = "org.apache.avro.avro-gradle-plugin"
+ implementationClass = "org.apache.avro.gradle.plugin.GradlePlugin"
+ }
+ }
+}
diff --git a/lang/java/gradle-plugin/gradle/wrapper/gradle-wrapper.jar b/lang/java/gradle-plugin/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000000..1b33c55baab
Binary files /dev/null and b/lang/java/gradle-plugin/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/lang/java/gradle-plugin/gradle/wrapper/gradle-wrapper.properties b/lang/java/gradle-plugin/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000000..ca025c83a7c
--- /dev/null
+++ b/lang/java/gradle-plugin/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/lang/java/gradle-plugin/gradlew b/lang/java/gradle-plugin/gradlew
new file mode 100755
index 00000000000..23d15a93670
--- /dev/null
+++ b/lang/java/gradle-plugin/gradlew
@@ -0,0 +1,251 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH="\\\"\\\""
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/lang/java/gradle-plugin/gradlew.bat b/lang/java/gradle-plugin/gradlew.bat
new file mode 100644
index 00000000000..db3a6ac207e
--- /dev/null
+++ b/lang/java/gradle-plugin/gradlew.bat
@@ -0,0 +1,94 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/lang/java/gradle-plugin/pom.xml b/lang/java/gradle-plugin/pom.xml
new file mode 100644
index 00000000000..d6de00b7f48
--- /dev/null
+++ b/lang/java/gradle-plugin/pom.xml
@@ -0,0 +1,75 @@
+
+
+
+ 4.0.0
+
+
+ avro-parent
+ org.apache.avro
+ 1.13.0-SNAPSHOT
+ ../pom.xml
+
+
+ avro-gradle-plugin
+ pom
+
+ Apache Avro Gradle Plugin
+ Gradle plugin for Avro IDL and Specific API Compilers
+
+
+ ${project.parent.parent.basedir}
+ 3.3.0
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.1.0
+
+
+
+ run-gradle-task
+ package
+
+ exec
+
+
+ ./gradlew
+
+ assemble
+
+
+
+
+
+
+
+ com.diffplug.spotless
+ spotless-maven-plugin
+
+ true
+
+
+
+
+
+
diff --git a/lang/java/gradle-plugin/settings.gradle.kts b/lang/java/gradle-plugin/settings.gradle.kts
new file mode 100644
index 00000000000..d8028b29b2e
--- /dev/null
+++ b/lang/java/gradle-plugin/settings.gradle.kts
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
+}
+rootProject.name = "avro-gradle-plugin"
+
+
+
diff --git a/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/GradlePlugin.kt b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/GradlePlugin.kt
new file mode 100644
index 00000000000..05b46415d89
--- /dev/null
+++ b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/GradlePlugin.kt
@@ -0,0 +1,87 @@
+package org.apache.avro.gradle.plugin
+
+import org.apache.avro.gradle.plugin.extension.GradlePluginExtension
+import org.apache.avro.gradle.plugin.tasks.CompileSchemaTask
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.plugins.JavaPluginExtension
+import org.gradle.internal.cc.base.logger
+import kotlin.collections.toSet
+
+abstract class GradlePlugin : Plugin {
+ override fun apply(project: Project) {
+ logger.info("Running Avro Gradle plugin for project: ${project.name}")
+
+ val extension: GradlePluginExtension = project.extensions.create("avro", GradlePluginExtension::class.java)
+
+ // Needed for Android support
+ project.pluginManager.apply("java")
+
+ project.tasks.register("avroGenerateJavaClasses", CompileSchemaTask::class.java) { compileSchemaTask ->
+ val sourceDirectory = extension.sourceDirectory.get()
+ val outputDirectory = extension.outputDirectory.get()
+ runPlugin(compileSchemaTask, extension, project, sourceDirectory, outputDirectory)
+ }
+
+ project.tasks.register("avroGenerateTestJavaClasses", CompileSchemaTask::class.java) {compileSchemaTask ->
+ val sourceDirectory = extension.testSourceDirectory.get()
+ val outputDirectory = extension.testOutputDirectory.get()
+ runPlugin(compileSchemaTask, extension, project, sourceDirectory, outputDirectory)
+ }
+ }
+
+ private fun runPlugin(
+ compileTask: CompileSchemaTask,
+ extension: GradlePluginExtension,
+ project: Project,
+ sourceDirectory: String,
+ outputDirectory: String
+ ) {
+ val schemaType: SchemaType = SchemaType.valueOf(extension.schemaType.get())
+
+ when (schemaType) {
+ SchemaType.schema -> {
+ compileTask.source(project.fileTree(sourceDirectory))
+ compileTask.sourceDirectory.set(sourceDirectory)
+ compileTask.outputDirectory.set(outputDirectory)
+ compileTask.fieldVisibility.set(extension.fieldVisibility)
+ compileTask.setExcludes(extension.excludes.get().toSet())
+ compileTask.setIncludes(setOf("**/*.avsc"))
+ compileTask.testExcludes.set(extension.testExcludes)
+ compileTask.stringType.set(extension.stringType)
+ compileTask.velocityToolsClassesNames.set(extension.velocityToolsClassesNames.get())
+ compileTask.templateDirectory.set(extension.templateDirectory)
+ compileTask.recordSpecificClass.set(extension.recordSpecificClass)
+ compileTask.errorSpecificClass.set(extension.errorSpecificClass)
+ compileTask.createOptionalGetters.set(extension.createOptionalGetters)
+ compileTask.gettersReturnOptional.set(extension.gettersReturnOptional)
+ compileTask.createSetters.set(extension.createSetters)
+ compileTask.createNullSafeAnnotations.set(extension.createNullSafeAnnotations)
+ compileTask.nullSafeAnnotationNullable.set(extension.nullSafeAnnotationNullable)
+ compileTask.nullSafeAnnotationNotNull.set(extension.nullSafeAnnotationNotNull)
+ compileTask.optionalGettersForNullableFieldsOnly.set(extension.optionalGettersForNullableFieldsOnly)
+ compileTask.customConversions.set(extension.customConversions)
+ compileTask.customLogicalTypeFactories.set(extension.customLogicalTypeFactories)
+ compileTask.enableDecimalLogicalType.set(extension.enableDecimalLogicalType)
+
+ addGeneratedSourcesToProject(project, compileTask.outputDirectory.get())
+ }
+
+ SchemaType.idl -> TODO()
+ }
+ }
+
+ private fun addGeneratedSourcesToProject(project: Project, outputDirectory: String) {
+ val sourceSets = project.extensions.getByType(JavaPluginExtension::class.java).sourceSets
+ val generatedSourcesDir = project.layout.buildDirectory.dir(outputDirectory)
+ project.logger.debug("Generated sources directory: ${generatedSourcesDir.get()}")
+
+ // Add directory that contains the generated Java files to source set
+ sourceSets.getByName("main").java.srcDir(generatedSourcesDir)
+ }
+}
+
+enum class SchemaType {
+ schema,
+ idl
+}
diff --git a/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/extension/GradlePluginExtension.kt b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/extension/GradlePluginExtension.kt
new file mode 100644
index 00000000000..763b4e40005
--- /dev/null
+++ b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/extension/GradlePluginExtension.kt
@@ -0,0 +1,226 @@
+package org.apache.avro.gradle.plugin.extension
+
+import org.gradle.api.model.ObjectFactory
+import org.gradle.api.provider.ListProperty
+import org.gradle.api.provider.Property
+import javax.inject.Inject
+
+abstract class GradlePluginExtension @Inject constructor(objects: ObjectFactory) {
+
+ /**
+ * Schema type: "schema", "idl", "protocol" are valid. Default is "schema"
+ */
+ val schemaType = objects.property(String::class.java).convention("schema")
+
+ /**
+ * The source directory of avro files. This directory is added to the classpath
+ * at schema compiling time. All files can therefore be referenced as classpath
+ * resources following the directory structure under the source directory.
+ *
+ * @parameter property="sourceDirectory"
+ * default-value="${buildDirectory}/src/main/avro"
+ */
+ val sourceDirectory: Property = objects.property(String::class.java).convention("src/main/avro")
+
+ /**
+ * @parameter property="outputDirectory"
+ * default-value="${buildDirectory}/generated-sources/avro"
+ */
+ val outputDirectory: Property = objects.property(String::class.java).convention("generated-sources-avro")
+
+
+ /**
+ * @parameter property="sourceDirectory"
+ * default-value="${project.layout.buildDirectory}/src/test/avro"
+ */
+ val testSourceDirectory: Property = objects.property(String::class.java).convention("src/test/avro")
+
+ /**
+ * @parameter property="outputDirectory"
+ * default-value="${project.layout.buildDirectory}/generated-test-sources/avro"
+ */
+ val testOutputDirectory: Property =
+ objects.property(String::class.java).convention("generated-test-sources-avro")
+
+
+ /**
+ * The field visibility indicator for the fields of the generated class, as
+ * string values of SpecificCompiler.FieldVisibility. The text is case
+ * insensitive.
+ *
+ * @parameter default-value="PRIVATE"
+ */
+ val fieldVisibility: Property = objects.property(String::class.java).convention("PRIVATE")
+
+
+ /**
+ * A set of Ant-like exclusion patterns used to prevent certain files from being
+ * processed. By default, this set is empty such that no files are excluded.
+ *
+ * @parameter
+ */
+ val excludes: ListProperty = objects.listProperty(String::class.java).convention(emptyList())
+
+ /**
+ * A set of Ant-like exclusion patterns used to prevent certain files from being
+ * processed. By default, this set is empty such that no files are excluded.
+ *
+ * @parameter
+ */
+ val testExcludes: ListProperty = objects.listProperty(String::class.java).convention(emptyList())
+
+ /**
+ * The Java type to use for Avro strings. May be one of CharSequence, String or
+ * Utf8. String by default.
+ *
+ * @parameter property="stringType"
+ */
+ val stringType: Property = objects.property(String::class.java).convention("String")
+
+
+ /**
+ * The qualified names of classes which the plugin will look up, instantiate
+ * (through an empty constructor that must exist) and set up to be injected into
+ * Velocity templates by Avro compiler.
+ *
+ * @parameter property="velocityToolsClassesNames"
+ */
+ val velocityToolsClassesNames: ListProperty =
+ objects.listProperty(String::class.java).convention(emptyList())
+
+
+ /**
+ * The directory (within the java classpath) that contains the velocity
+ * templates to use for code generation. The default value points to the
+ * templates included with the avro-maven-plugin.
+ *
+ * @parameter property="templateDirectory"
+ */
+ val templateDirectory: Property =
+ objects.property(String::class.java).convention("/org/apache/avro/compiler/specific/templates/java/classic/")
+
+
+ /**
+ * Generated record schema classes will extend this class.
+ *
+ * @parameter property="recordSpecificClass"
+ */
+ val recordSpecificClass: Property =
+ objects.property(String::class.java).convention("org.apache.avro.specific.SpecificRecordBase")
+
+
+ /**
+ * Generated error schema classes will extend this class.
+ *
+ * @parameter property="errorSpecificClass"
+ */
+ val errorSpecificClass: Property =
+ objects.property(String::class.java).convention("org.apache.avro.specific.SpecificExceptionBase")
+
+
+ /**
+ * The createOptionalGetters parameter enables generating the getOptional...
+ * methods that return an Optional of the requested type. This works ONLY on
+ * Java 8+
+ *
+ * @parameter property="createOptionalGetters"
+ */
+ val createOptionalGetters: Property = objects.property(Boolean::class.java).convention(false)
+
+ /**
+ * The gettersReturnOptional parameter enables generating get... methods that
+ * return an Optional of the requested type. This works ONLY on Java 8+
+ *
+ * @parameter property="gettersReturnOptional"
+ */
+ val gettersReturnOptional: Property = objects.property(Boolean::class.java).convention(false)
+
+ /**
+ * The optionalGettersForNullableFieldsOnly parameter works in conjunction with
+ * gettersReturnOptional option. If it is set, Optional getters will be
+ * generated only for fields that are nullable. If the field is mandatory,
+ * regular getter will be generated. This works ONLY on Java 8+.
+ *
+ * @parameter property="optionalGettersForNullableFieldsOnly"
+ */
+ val optionalGettersForNullableFieldsOnly: Property =
+ objects.property(Boolean::class.java).convention(false)
+
+
+ /**
+ * Determines whether or not to create setters for the fields of the record. The
+ * default is to create setters.
+ *
+ * @parameter default-value="true"
+ */
+ val createSetters: Property = objects.property(Boolean::class.java).convention(true)
+
+ /**
+ * If set to true, @Nullable and @NotNull annotations are
+ * added to fields of the record. The default is false. If enabled, JetBrains
+ * annotations are used by default but other annotations can be specified via
+ * the nullSafeAnnotationNullable and nullSafeAnnotationNotNull parameters.
+ *
+ * @parameter property="createNullSafeAnnotations"
+ *
+ * @see [
+ * JetBrains nullability annotations](https://www.jetbrains.com/help/idea/annotating-source-code.html.nullability-annotations)
+ */
+ val createNullSafeAnnotations: Property = objects.property(Boolean::class.java).convention(false)
+
+ /**
+ * Controls which annotation should be added to nullable fields if
+ * createNullSafeAnnotations is enabled. The default is
+ * org.jetbrains.annotations.Nullable.
+ *
+ * @parameter property="nullSafeAnnotationNullable"
+ *
+ * @see [
+ * JetBrains nullability annotations](https://www.jetbrains.com/help/idea/annotating-source-code.html.nullability-annotations)
+ */
+ val nullSafeAnnotationNullable: Property =
+ objects.property(String::class.java).convention("org.jetbrains.annotations.Nullable")
+
+ /**
+ * Controls which annotation should be added to non-nullable fields if
+ * createNullSafeAnnotations is enabled. The default is
+ * org.jetbrains.annotations.NotNull.
+ *
+ * @parameter property="nullSafeAnnotationNotNull"
+ *
+ * @see [
+ * JetBrains nullability annotations](https://www.jetbrains.com/help/idea/annotating-source-code.html.nullability-annotations)
+ */
+ val nullSafeAnnotationNotNull: Property =
+ objects.property(String::class.java).convention("org.jetbrains.annotations.NotNull")
+
+ /**
+ * A set of fully qualified class names of custom
+ * {@link org.apache.avro.Conversion} implementations to add to the compiler.
+ * The classes must be on the classpath at compile time and whenever the Java
+ * objects are serialized.
+ *
+ * @parameter property="customConversions"
+ */
+ val customConversions: ListProperty = objects.listProperty(String::class.java).convention(emptyList())
+
+ /**
+ * A set of fully qualified class names of custom
+ * [org.apache.avro.LogicalTypes.LogicalTypeFactory] implementations to
+ * add to the compiler. The classes must be on the classpath at compile time and
+ * whenever the Java objects are serialized.
+ *
+ * @parameter property="customLogicalTypeFactories"
+ */
+ val customLogicalTypeFactories: ListProperty =
+ objects.listProperty(String::class.java).convention(emptyList())
+
+
+ /**
+ * Determines whether or not to use Java classes for decimal types
+ *
+ * @parameter default-value="false"
+ */
+ val enableDecimalLogicalType: Property = objects.property(Boolean::class.java).convention(false)
+
+}
diff --git a/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/tasks/AbstractCompileTask.kt b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/tasks/AbstractCompileTask.kt
new file mode 100644
index 00000000000..81be641e3ea
--- /dev/null
+++ b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/tasks/AbstractCompileTask.kt
@@ -0,0 +1,72 @@
+package org.apache.avro.gradle.plugin.tasks
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.provider.ListProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.SourceTask
+
+abstract class AbstractCompileTask : SourceTask() {
+
+ @get:Input
+ abstract val sourceDirectory: Property
+
+ @get:Input
+ abstract val outputDirectory: Property
+
+ @get:Input
+ abstract val fieldVisibility: Property
+
+ //@get:Input
+ //abstract val excludes: ListProperty
+
+ @get:Input
+ abstract val testExcludes: ListProperty
+
+ @get:Input
+ abstract val stringType: Property
+
+ @get:Input
+ abstract val velocityToolsClassesNames: ListProperty
+
+ @get:Input
+ abstract val templateDirectory: Property
+
+ @get:Input
+ abstract val recordSpecificClass: Property
+
+ @get:Input
+ abstract val errorSpecificClass: Property
+
+ @get:Input
+ abstract val createOptionalGetters: Property
+
+ @get:Input
+ abstract val gettersReturnOptional: Property
+
+ @get:Input
+ abstract val optionalGettersForNullableFieldsOnly: Property
+
+ @get:Input
+ abstract val createSetters: Property
+
+ @get:Input
+ abstract val createNullSafeAnnotations: Property
+
+ @get:Input
+ abstract val nullSafeAnnotationNullable: Property
+
+ @get:Input
+ abstract val nullSafeAnnotationNotNull: Property
+
+ @get:Input
+ abstract val customConversions: ListProperty
+
+ @get:Input
+ abstract val customLogicalTypeFactories: ListProperty
+
+ @get:Input
+ abstract val enableDecimalLogicalType: Property
+
+
+}
diff --git a/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/tasks/CompileSchemaTask.kt b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/tasks/CompileSchemaTask.kt
new file mode 100644
index 00000000000..69dff11cf21
--- /dev/null
+++ b/lang/java/gradle-plugin/src/main/kotlin/org/apache/avro/gradle/plugin/tasks/CompileSchemaTask.kt
@@ -0,0 +1,136 @@
+package org.apache.avro.gradle.plugin.tasks
+
+import org.apache.avro.SchemaParseException
+import org.apache.avro.SchemaParser
+import org.apache.avro.compiler.specific.SpecificCompiler
+import org.apache.avro.compiler.specific.SpecificCompiler.FieldVisibility
+import org.apache.avro.generic.GenericData
+import org.gradle.api.Project
+import org.gradle.api.file.FileTree
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.TaskAction
+import java.io.File
+import java.io.IOException
+import java.util.*
+
+abstract class CompileSchemaTask : AbstractCompileTask() {
+
+ /**
+ * A set of Glob patterns used to select files from the source
+ * directory for processing. By default, the pattern "**/*.avsc"
+ * is used to select avsc files.
+ *
+ * @parameter
+ */
+ //@get:Input
+ //val includes: Set = setOf("**/*.avsc")
+
+ @TaskAction
+ fun compileSchema() {
+ project.logger.info("Generating Java files from Avro schemas...")
+
+ if (!source.isEmpty) {
+ val sourceDirectoryFullPath = getSourceDirectoryFullPath(sourceDirectory)
+ val outputDirectoryFullPath = getBuildDirectoryFullPath(outputDirectory)
+ compileSchemas(source, sourceDirectoryFullPath, outputDirectoryFullPath)
+ } else {
+ logger.warn("No Avro files found in $sourceDirectory. Nothing to compile")
+ }
+
+ project.logger.info("Done generating Java files from Avro schemas...")
+ }
+
+
+ private fun getSourceDirectoryFullPath(directoryProperty: Property): File =
+ project.layout.projectDirectory.dir(directoryProperty.get()).asFile
+
+ private fun getBuildDirectoryFullPath(directoryProperty: Property): File =
+ project.layout.buildDirectory.dir(directoryProperty).get().asFile
+
+ private fun compileSchemas(fileTree: FileTree, sourceDirectory: File, outputDirectory: File) {
+ val sourceFileForModificationDetection: File? =
+ fileTree
+ .files
+ .filter { file: File -> file.lastModified() > 0 }
+ .maxBy { it.lastModified() }
+
+ try {
+ val parser = SchemaParser()
+ for (sourceFile in fileTree.files) {
+ parser.parse(sourceFile)
+ }
+ val schemas = parser.parsedNamedSchemas
+
+ doCompile(sourceFileForModificationDetection, SpecificCompiler(schemas), outputDirectory)
+ } catch (ex: IOException) {
+ // TODO: more concrete exceptions
+ throw RuntimeException("IO ex: Error compiling a file in " + sourceDirectory + " to " + outputDirectory, ex)
+ } catch (ex: SchemaParseException) {
+ throw RuntimeException(
+ "SchemaParse ex Error compiling a file in " + sourceDirectory + " to " + outputDirectory,
+ ex
+ )
+ }
+ }
+
+ private fun doCompile(
+ sourceFileForModificationDetection: File?,
+ compiler: SpecificCompiler,
+ outputDirectory: File
+ ) {
+ setCompilerProperties(compiler)
+ // TODO:
+ // * customLogicalTypeFactories
+
+ try {
+ for (customConversion in customConversions.get()) {
+ compiler.addCustomConversion(Thread.currentThread().getContextClassLoader().loadClass(customConversion))
+ }
+ } catch (e: ClassNotFoundException) {
+ throw IOException(e)
+ }
+ compiler.compileToDestination(sourceFileForModificationDetection, outputDirectory)
+ }
+
+
+ private fun setCompilerProperties(compiler: SpecificCompiler) {
+ compiler.setTemplateDir(project.layout.projectDirectory.dir(templateDirectory.get()).asFile.absolutePath + "/")
+ compiler.setStringType(GenericData.StringType.valueOf(stringType.get()))
+ compiler.setFieldVisibility(getFieldV())
+ compiler.setCreateOptionalGetters(createOptionalGetters.get())
+ compiler.setGettersReturnOptional(gettersReturnOptional.get())
+ compiler.setOptionalGettersForNullableFieldsOnly(optionalGettersForNullableFieldsOnly.get())
+ compiler.setCreateSetters(createSetters.get())
+ compiler.setCreateNullSafeAnnotations(createNullSafeAnnotations.get())
+ compiler.setNullSafeAnnotationNullable(nullSafeAnnotationNullable.get())
+ compiler.setNullSafeAnnotationNotNull(nullSafeAnnotationNotNull.get())
+ compiler.setEnableDecimalLogicalType(enableDecimalLogicalType.get())
+ // TODO: likely not needed
+// compiler.setOutputCharacterEncoding(project.getProperties().getProperty("project.build.sourceEncoding"))
+ compiler.setAdditionalVelocityTools(instantiateAdditionalVelocityTools(velocityToolsClassesNames.get()))
+ compiler.setRecordSpecificClass(recordSpecificClass.get())
+ compiler.setErrorSpecificClass(errorSpecificClass.get())
+ }
+
+ private fun getFieldV(): FieldVisibility {
+ try {
+ val upperCaseFieldVisibility = fieldVisibility.get().trim().uppercase(Locale.getDefault())
+ return FieldVisibility.valueOf(upperCaseFieldVisibility)
+ } catch (e: IllegalArgumentException) {
+ logger.warn("Could not parse field visibility, using PRIVATE")
+ return FieldVisibility.PRIVATE
+ }
+ }
+
+ protected fun instantiateAdditionalVelocityTools(velocityToolsClassesNames: List): List {
+ return velocityToolsClassesNames.map { velocityToolClassName ->
+ try {
+ Class.forName(velocityToolClassName)
+ .getDeclaredConstructor()
+ .newInstance()
+ } catch (e: Exception) {
+ throw RuntimeException(e)
+ }
+ }
+ }
+}
diff --git a/lang/java/gradle-plugin/src/main/resources/META-INF/org.apache.avro.gradle.plugin.properties b/lang/java/gradle-plugin/src/main/resources/META-INF/org.apache.avro.gradle.plugin.properties
new file mode 100644
index 00000000000..13fbf0d2be3
--- /dev/null
+++ b/lang/java/gradle-plugin/src/main/resources/META-INF/org.apache.avro.gradle.plugin.properties
@@ -0,0 +1 @@
+implementation-class=org.apache.avro.gradle.plugin.GradlePlugin
diff --git a/lang/java/gradle-plugin/src/test/avro/AvdlClasspathImport.avdl b/lang/java/gradle-plugin/src/test/avro/AvdlClasspathImport.avdl
new file mode 100644
index 00000000000..81bdb609445
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/avro/AvdlClasspathImport.avdl
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace test;
+
+import idl "avro/User.avdl";
+
+/** Ignored Doc Comment */
+/** IDL User */
+record IdlUserWrapper {
+ union { null, test.IdlUser } wrapped;
+}
diff --git a/lang/java/gradle-plugin/src/test/avro/User.avdl b/lang/java/gradle-plugin/src/test/avro/User.avdl
new file mode 100644
index 00000000000..98de878d9d0
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/avro/User.avdl
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@namespace("test")
+protocol IdlTest {
+
+ enum IdlPrivacy {
+ Public, Private
+ }
+
+ record IdlUser {
+ union { null, string } id;
+ union { null, long } createdOn;
+ timestamp_ms modifiedOn;
+ union { null, IdlPrivacy } privacy;
+ }
+
+}
diff --git a/lang/java/gradle-plugin/src/test/avro/User.avpr b/lang/java/gradle-plugin/src/test/avro/User.avpr
new file mode 100644
index 00000000000..6dd8b9b8900
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/avro/User.avpr
@@ -0,0 +1,41 @@
+{
+ "protocol" : "ProtocolTest",
+ "namespace" : "test",
+ "types" : [
+ {
+ "type" : "enum",
+ "name" : "ProtocolPrivacy",
+ "symbols" : [ "Public", "Private"]
+ },
+ {
+ "type": "record",
+ "namespace": "test",
+ "name": "ProtocolUser",
+ "doc": "User Test Bean",
+ "fields": [
+ {
+ "name": "id",
+ "type": ["null", "string"],
+ "default": null
+ },
+ {
+ "name": "createdOn",
+ "type": ["null", "long"],
+ "default": null
+ },
+ {
+ "name": "privacy",
+ "type": ["null", "ProtocolPrivacy"],
+ "default": null
+ },
+ {
+ "name": "modifiedOn",
+ "type": {
+ "type": "long",
+ "logicalType": "timestamp-millis"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/lang/java/gradle-plugin/src/test/avro/User.avsc b/lang/java/gradle-plugin/src/test/avro/User.avsc
new file mode 100644
index 00000000000..a93e0d13f21
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/avro/User.avsc
@@ -0,0 +1,45 @@
+{
+ "type": "record",
+ "namespace": "test",
+ "name": "SchemaUser",
+ "doc": "User Test Bean",
+ "fields": [
+ {
+ "name": "id",
+ "type": ["null", "string"],
+ "default": null
+ },
+ {
+ "name": "createdOn",
+ "type": ["null", "long"],
+ "default": null
+ },
+ {
+ "name": "privacy",
+ "type": ["null", {
+ "type": "enum",
+ "name": "SchemaPrivacy",
+ "namespace": "test",
+ "symbols" : ["Public","Private"]
+ }],
+ "default": null
+ },
+ {
+ "name": "privacyImported",
+ "type": ["null", "test.PrivacyImport"],
+ "default": null
+ },
+ {
+ "name": "privacyDirectImport",
+ "type": ["null", "test.PrivacyDirectImport"],
+ "default": null
+ },
+ {
+ "name": "time",
+ "type": {
+ "type": "long",
+ "logicalType": "timestamp-millis"
+ }
+ }
+ ]
+}
diff --git a/lang/java/gradle-plugin/src/test/avro/directImport/PrivacyDirectImport.avsc b/lang/java/gradle-plugin/src/test/avro/directImport/PrivacyDirectImport.avsc
new file mode 100644
index 00000000000..a5b62959206
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/avro/directImport/PrivacyDirectImport.avsc
@@ -0,0 +1,7 @@
+{
+ "type": "enum",
+ "namespace": "test",
+ "name": "PrivacyDirectImport",
+ "doc": "Privacy Test Enum",
+ "symbols" : ["Public","Private"]
+}
diff --git a/lang/java/gradle-plugin/src/test/avro/extends/Custom.avsc b/lang/java/gradle-plugin/src/test/avro/extends/Custom.avsc
new file mode 100644
index 00000000000..63056e5d17f
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/avro/extends/Custom.avsc
@@ -0,0 +1,18 @@
+{
+ "type": "record",
+ "namespace": "test",
+ "name": "SchemaCustom",
+ "doc": "Custom Test Bean",
+ "fields": [
+ {
+ "name": "id",
+ "type": ["null", "string"],
+ "default": null
+ },
+ {
+ "name": "createdOn",
+ "type": ["null", "long"],
+ "default": null
+ }
+ ]
+}
diff --git a/lang/java/gradle-plugin/src/test/avro/imports/PrivacyImport.avsc b/lang/java/gradle-plugin/src/test/avro/imports/PrivacyImport.avsc
new file mode 100644
index 00000000000..f454f1d3996
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/avro/imports/PrivacyImport.avsc
@@ -0,0 +1,7 @@
+{
+ "type": "enum",
+ "namespace": "test",
+ "name": "PrivacyImport",
+ "doc": "Privacy Test Enum",
+ "symbols" : ["Public","Private"]
+}
diff --git a/lang/java/gradle-plugin/src/test/avro/multipleSchemas/ApplicationEvent.avsc b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/ApplicationEvent.avsc
new file mode 100644
index 00000000000..efc7fbf6139
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/ApplicationEvent.avsc
@@ -0,0 +1,44 @@
+{
+ "namespace": "model",
+ "type": "record",
+ "doc": "",
+ "name": "ApplicationEvent",
+ "fields": [
+ {
+ "name": "applicationId",
+ "type": "string",
+ "doc": "Application ID"
+ },
+ {
+ "name": "status",
+ "type": "string",
+ "doc": "Application Status"
+ },
+ {
+ "name": "documents",
+ "type": ["null", {
+ "type": "array",
+ "items": "model.DocumentInfo"
+ }],
+ "doc": "",
+ "default": null
+ },
+ {
+ "name": "response",
+ "type": {
+ "namespace": "model",
+ "type": "record",
+ "doc": "",
+ "name": "MyResponse",
+ "fields": [
+ {
+ "name": "isSuccessful",
+ "type": "boolean",
+ "doc": "Indicator for successful or unsuccessful call"
+ }
+ ]
+ }
+ }
+ ]
+
+}
diff --git a/lang/java/gradle-plugin/src/test/avro/multipleSchemas/DocumentInfo.avsc b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/DocumentInfo.avsc
new file mode 100644
index 00000000000..95dd4243ea6
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/DocumentInfo.avsc
@@ -0,0 +1,19 @@
+{
+ "namespace": "model",
+ "type": "record",
+ "doc": "",
+ "name": "DocumentInfo",
+ "fields": [
+ {
+ "name": "documentId",
+ "type": "string",
+ "doc": "Document ID"
+ },
+ {
+ "name": "filePath",
+ "type": "string",
+ "doc": "Document Path"
+ }
+ ]
+
+}
diff --git a/lang/java/gradle-plugin/src/test/avro/multipleSchemas/MyResponse.avsc b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/MyResponse.avsc
new file mode 100644
index 00000000000..ac6d08291d9
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/MyResponse.avsc
@@ -0,0 +1,14 @@
+{
+ "namespace": "model",
+ "type": "record",
+ "doc": "",
+ "name": "MyResponse",
+ "fields": [
+ {
+ "name": "isSuccessful",
+ "type": "boolean",
+ "doc": "Indicator for successful or unsuccessful call"
+ }
+ ]
+
+}
diff --git a/lang/java/gradle-plugin/src/test/avro/multipleSchemas/README.md b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/README.md
new file mode 100644
index 00000000000..fe3541b660e
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/avro/multipleSchemas/README.md
@@ -0,0 +1,8 @@
+## test for parsing multiple files.
+This folder aims to test `public List Schema.parse(Iterable sources) throws IOException` method.
+
+The objective is to check that a record schema define in a file can be use in another record schema as a field type.
+Here, ApplicationEvent.avsc file contains a field of type DocumentInfo, defined in file DocumentInfo.avsc.
+
+The is written at TestSchema.testParseMultipleFile.
+
diff --git a/lang/java/gradle-plugin/src/test/kotlin/org/apache/avro/gradle/plugin/SchemaCompileTaskTest.kt b/lang/java/gradle-plugin/src/test/kotlin/org/apache/avro/gradle/plugin/SchemaCompileTaskTest.kt
new file mode 100644
index 00000000000..7da2008d75e
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/kotlin/org/apache/avro/gradle/plugin/SchemaCompileTaskTest.kt
@@ -0,0 +1,271 @@
+package org.apache.avro.gradle.plugin
+
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.jupiter.api.io.TempDir
+import java.nio.file.Path
+import kotlin.io.path.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+@ExperimentalPathApi
+class SchemaCompileTaskTest {
+
+ @TempDir
+ lateinit var tempDir: Path
+
+ @Test
+ fun `plugin executes avroGenerateJavaClasses task successfully`() {
+ // given
+ val tempSettingsFile = tempDir.resolve("settings.gradle.kts")
+ val tempBuildFile = tempDir.resolve("build.gradle.kts")
+ val tempAvroSrcDir = tempDir.resolve("src/test/avro").createDirectories()
+
+ val testAvroFiles = Path.of("src/test/avro")
+ val testAvroOutPutDir = Path.of("generated-sources/avro")
+
+ val testOutPutDirectory = tempDir.resolve("build/$testAvroOutPutDir/test")
+
+ testAvroFiles.copyToRecursively(
+ tempAvroSrcDir,
+ overwrite = true,
+ followLinks = false
+ )
+
+ tempSettingsFile.writeText("")
+ tempBuildFile.writeText(
+ """
+ plugins {
+ id("org.apache.avro.avro-gradle-plugin")
+ }
+
+ avro {
+ schemaType = "schema"
+ sourceDirectory = "$testAvroFiles"
+ outputDirectory = "$testAvroOutPutDir"
+ }
+ """.trimIndent()
+ )
+
+ // when
+ val result = GradleRunner.create()
+ .withProjectDir(tempDir.toFile())
+ .withArguments("avroGenerateJavaClasses")
+ .withPluginClasspath()
+ .forwardOutput() // to see printLn in code
+ .build()
+
+ val expectedFiles = setOf(
+ "SchemaPrivacy.java",
+ "SchemaUser.java",
+ "PrivacyImport.java",
+ "SchemaCustom.java",
+ "PrivacyDirectImport.java"
+ )
+
+ // then
+ assertEquals(TaskOutcome.SUCCESS, result.task(":avroGenerateJavaClasses")?.outcome)
+ assertFilesExist(testOutPutDirectory, expectedFiles)
+
+ val schemaUserContent = testOutPutDirectory.resolve("SchemaUser.java").readText()
+ assertTrue(schemaUserContent.contains("java.time.Instant"))
+ }
+
+
+ @Test
+ fun `plugin executes avroGenerateTestJavaClasses task successfully - for files in test directory`() {
+ // given
+ val tempSettingsFile = tempDir.resolve("settings.gradle.kts")
+ val tempBuildFile = tempDir.resolve("build.gradle.kts")
+ val tempAvroSrcDir = tempDir.resolve("src/test/avro").createDirectories()
+
+ val testAvroFiles = Path.of("src/test/avro")
+ val testAvroOutPutDir = Path.of("generated-test-sources-avro")
+
+ val testOutPutDirectory = tempDir.resolve("build/$testAvroOutPutDir/test")
+
+ testAvroFiles.copyToRecursively(
+ tempAvroSrcDir,
+ overwrite = true,
+ followLinks = false
+ )
+
+ tempSettingsFile.writeText("")
+ tempBuildFile.writeText(
+ """
+ plugins {
+ id("org.apache.avro.avro-gradle-plugin")
+ }
+
+ avro {
+ schemaType = "schema"
+ testSourceDirectory = "$testAvroFiles"
+ testOutputDirectory = "$testAvroOutPutDir"
+ }
+ """.trimIndent()
+ )
+
+ // when
+ val result = GradleRunner.create()
+ .withProjectDir(tempDir.toFile())
+ .withArguments("avroGenerateTestJavaClasses")
+ .withPluginClasspath()
+ .forwardOutput() // to see printLn in code
+ .build()
+
+ val expectedFiles = setOf(
+ "SchemaPrivacy.java",
+ "SchemaUser.java",
+ "PrivacyImport.java",
+ "SchemaCustom.java",
+ "PrivacyDirectImport.java"
+ )
+
+ // then
+ assertEquals(TaskOutcome.SUCCESS, result.task(":avroGenerateTestJavaClasses")?.outcome)
+ assertFilesExist(testOutPutDirectory, expectedFiles)
+
+ val schemaUserContent = testOutPutDirectory.resolve("SchemaUser.java").readText()
+ assertTrue(schemaUserContent.contains("java.time.Instant"))
+ }
+
+ @Test
+ fun `plugin executes avroGenerateJavaClasses task successfully - with Velocity class names`() {
+ // given
+ val tempSettingsFile = tempDir.resolve("settings.gradle.kts")
+ val tempBuildFile = tempDir.resolve("build.gradle.kts")
+ val tempAvroSrcDir = tempDir.resolve("src/test/avro").createDirectories()
+ //val bla = tempDir.resolve("src/aap").createDirectories()
+ val tempVelocityToolClassesDir = tempDir.resolve("src/test/resources/templates").createDirectories()
+
+ val testAvroFilesDir = Path.of("src/test/avro")
+ val testAvroOutPutDir = Path.of("generated-sources-avro")
+ val testVelocityToolClassesDir = Path.of("src/test/resources/templates")
+
+ val testOutPutDirectory = tempDir.resolve("build/$testAvroOutPutDir/test")
+
+ testAvroFilesDir.copyToRecursively(
+ tempAvroSrcDir,
+ overwrite = true,
+ followLinks = false
+ )
+
+ testVelocityToolClassesDir.copyToRecursively(
+ tempVelocityToolClassesDir,
+ overwrite = true,
+ followLinks = false
+ )
+
+ tempSettingsFile.writeText("")
+ tempBuildFile.writeText(
+ """
+ plugins {
+ id("org.apache.avro.avro-gradle-plugin")
+ }
+
+ avro {
+ schemaType = "schema"
+ sourceDirectory = "$testAvroFilesDir"
+ outputDirectory = "$testAvroOutPutDir"
+ templateDirectory = "$testVelocityToolClassesDir"
+ velocityToolsClassesNames = listOf("java.lang.String")
+ }
+ """.trimIndent()
+ )
+
+ // when
+ val result = GradleRunner.create()
+ .withProjectDir(tempDir.toFile())
+ .withArguments("avroGenerateJavaClasses")
+ .withPluginClasspath()
+ .forwardOutput() // to see printLn in code
+ .build()
+
+ val expectedFiles = setOf(
+ "SchemaPrivacy.java",
+ "SchemaUser.java",
+ "PrivacyImport.java",
+ "SchemaCustom.java",
+ "PrivacyDirectImport.java"
+ )
+
+ // then
+ assertEquals(TaskOutcome.SUCCESS, result.task(":avroGenerateJavaClasses")?.outcome)
+ assertFilesExist(testOutPutDirectory, expectedFiles)
+
+ val schemaUserContent = testOutPutDirectory.resolve("SchemaUser.java").readText()
+ assertTrue(schemaUserContent.contains("It works!"))
+ }
+
+ @Test
+ fun `plugin executes avroGenerateJavaClasses task successfully - custom recordSpecificClass`() {
+ // given
+ val tempSettingsFile = tempDir.resolve("settings.gradle.kts")
+ val tempBuildFile = tempDir.resolve("build.gradle.kts")
+ val tempAvroSrcDir = tempDir.resolve("src/test/avro").createDirectories()
+
+ val testAvroFiles = Path.of("src/test/avro")
+ val testAvroOutPutDir = Path.of("generated-sources/avro")
+
+ val testOutPutDirectory = tempDir.resolve("build/$testAvroOutPutDir/test")
+
+ testAvroFiles.copyToRecursively(
+ tempAvroSrcDir,
+ overwrite = true,
+ followLinks = false
+ )
+
+ tempSettingsFile.writeText("")
+ tempBuildFile.writeText(
+ """
+ plugins {
+ id("org.apache.avro.avro-gradle-plugin")
+ }
+
+ avro {
+ schemaType = "schema"
+ sourceDirectory = "$testAvroFiles"
+ outputDirectory = "$testAvroOutPutDir"
+ recordSpecificClass = "org.apache.avro.custom.CustomRecordBase"
+ }
+ """.trimIndent()
+ )
+
+ // when
+ val result = GradleRunner.create()
+ .withProjectDir(tempDir.toFile())
+ .withArguments("avroGenerateJavaClasses")
+ .withPluginClasspath()
+ .forwardOutput() // to see printLn in code
+ .build()
+
+ // then
+ assertEquals(TaskOutcome.SUCCESS, result.task(":avroGenerateJavaClasses")?.outcome)
+
+ val outPutFile = testOutPutDirectory.resolve("SchemaCustom.java")
+ assertTrue(outPutFile.toFile().exists())
+
+ val extendsLines = outPutFile.readLines()
+ .filter { line -> line.contains("class SchemaCustom extends ") }
+ assertEquals(1, extendsLines.size)
+
+ val extendLine = extendsLines[0]
+ assertTrue(extendLine.contains(" org.apache.avro.custom.CustomRecordBase "))
+ assertFalse(extendLine.contains("org.apache.avro.specific.SpecificRecordBase"))
+ }
+
+
+ private fun assertFilesExist(directory: Path, expectedFiles: Set) {
+ assertTrue(directory.exists(), "Directory $directory does not exist")
+ assertTrue(expectedFiles.isNotEmpty())
+
+ val filesInDirectory: Set = directory
+ .listDirectoryEntries()
+ .map { it.fileName.toString() }.toSet()
+
+ assertEquals(expectedFiles, filesInDirectory)
+ }
+
+}
diff --git a/lang/java/gradle-plugin/src/test/resources/templates/enum.vm b/lang/java/gradle-plugin/src/test/resources/templates/enum.vm
new file mode 100644
index 00000000000..fbb32a9583a
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/resources/templates/enum.vm
@@ -0,0 +1,19 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## https://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+##
+
+## No need to place anything here by now. File must exist, though.
diff --git a/lang/java/gradle-plugin/src/test/resources/templates/protocol.vm b/lang/java/gradle-plugin/src/test/resources/templates/protocol.vm
new file mode 100644
index 00000000000..fbb32a9583a
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/resources/templates/protocol.vm
@@ -0,0 +1,19 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## https://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+##
+
+## No need to place anything here by now. File must exist, though.
diff --git a/lang/java/gradle-plugin/src/test/resources/templates/record.vm b/lang/java/gradle-plugin/src/test/resources/templates/record.vm
new file mode 100644
index 00000000000..859bc062968
--- /dev/null
+++ b/lang/java/gradle-plugin/src/test/resources/templates/record.vm
@@ -0,0 +1,21 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## https://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+##
+
+/**
+ * $string.concat("It works!")
+ */
diff --git a/lang/java/pom.xml b/lang/java/pom.xml
index 3e503842bbc..7cbf4870bf7 100644
--- a/lang/java/pom.xml
+++ b/lang/java/pom.xml
@@ -334,6 +334,7 @@
com.diffplug.spotless
spotless-maven-plugin
+ true
CHANGES.txt
DIST_README.txt