[
  {
    "path": ".github/workflows/robot.yml",
    "content": "on:\n  push:\n    tags:\n      - 'v*'\n    branches:\n      - master\n      - next\n  pull_request:\n    branches:\n      - master\n\npermissions:\n  contents: write\n\nname: Build robot\n\njobs:\n  build:\n    runs-on: ubuntu-24.04\n    strategy:\n      matrix:\n        java: [ 8, 11, 17, 21 ]\n    name: Java ${{ matrix.java }}\n    steps:\n      - name: Checkout with submodules\n        uses: actions/checkout@v6.0.2\n        with:\n          submodules: true\n          fetch-depth: 0 # To make git describe give the intended output\n          fetch-tags: true\n      - name: Setup java\n        uses: actions/setup-java@v5.2.0\n        with:\n          java-version: ${{ matrix.java }}\n          distribution: temurin\n          cache: maven\n      - name: Run Maven for capfile\n        if: matrix.java != 8\n        run: ./mvnw -B --no-transfer-progress -T1C -U -Pcheck verify\n      - name: Run ANT tests\n        run: ant test dist\n      - name: Get SHA-256 of ant-javacard.jar\n        run: echo \"JARSUM=$(shasum -a 256 --tag ant-javacard.jar)\" >> \"$GITHUB_ENV\"\n      - name: Deploy package or snapshot\n        if: github.event_name != 'pull_request' && matrix.java == '11'\n        uses: martinpaljak/maven-ssh-deploy@v1\n        with:\n          key: ${{ secrets.SSH_KEY }}\n          user: mvn@mvn.javacard.pro\n          host_fp: SHA256:auiF3nHWvDmvq2stDl+QEECCqMcDp+FY1/bDRnvxpRw\n      - name: Upload\n        if: github.event_name != 'pull_request' && matrix.java == '11'\n        uses: actions/upload-artifact@v7.0.1\n        with:\n          name: ant-javacard.jar\n          path: ant-javacard.jar\n      - name: Release\n        if: startsWith(github.ref, 'refs/tags/v') && matrix.java == '11'\n        id: create_release\n        uses: softprops/action-gh-release@v3.0.0\n        with:\n          files: |\n            ant-javacard.jar\n          fail_on_unmatched_files: true\n          body: \"Release ${{ github.ref_name }}, `${{ env.JARSUM }}` (built with JDK-11)\"\n          prerelease: true # manually promoted\n"
  },
  {
    "path": ".gitignore",
    "content": "**/target\n*.cap\n*.class\n*.iml\n*~\n/*.iml\n/*.jar\n/*.jca\n/.idea\n/.mvn/wrapper/maven-wrapper.jar\n/target\n/build\n/testlib\nhs_err_pid*\npom.xml.versionsBackup\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"sdks\"]\n\tpath = sdks\n\turl = ../oracle_javacard_sdks.git\n"
  },
  {
    "path": ".mvn/wrapper/maven-wrapper.properties",
    "content": "wrapperVersion=3.3.4\ndistributionType=script\ndistributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.15/apache-maven-3.9.15-bin.zip\nwrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015-2024 Martin Paljak\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "LICENSES/Apache-2.0.txt",
    "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 distribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \"control\" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \"submitted\" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\n     (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and\n\n     (b) You must cause any modified files to carry prominent notices stating that You changed the files; and\n\n     (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n\n     (d) If the Work includes a \"NOTICE\" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.\n\n     You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\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 notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!)  The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "LICENSES/MIT.txt",
    "content": "MIT License\n\nCopyright (c) <year> <copyright holders>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and\nassociated documentation files (the \"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the\nfollowing conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial\nportions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT\nLIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO\nEVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\nUSE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "TZ = UTC # same as Github\nexport TZ\nSHELL := /bin/bash\nJDK := zulu\nJAVA8 := /Library/Java/JavaVirtualMachines/$(JDK)-8.jdk/Contents/Home\nJAVA11 := /Library/Java/JavaVirtualMachines/$(JDK)-11.jdk/Contents/Home\nJAVA17 := /Library/Java/JavaVirtualMachines/$(JDK)-17.jdk/Contents/Home\nJAVA21 := /Library/Java/JavaVirtualMachines/$(JDK)-21.jdk/Contents/Home\n\ndefault: today reportjava\n\t./mvnw package\n\tant test\n\ndist: reportjava\n\tJAVA_HOME=$(JAVA11) ant clean dist\n\tshasum -a 256 --tag ant-javacard.jar\n\nreportjava:\n\t@echo using java $(shell java -version 2>&1 | grep version) from \\\"$(JAVA_HOME)\\\"\n\njar:\n\tJAVA_HOME=$(JAVA8) ant clean dist\n\ncap:\n\t# run maven with JDK21\n\tJAVA_HOME=$(JAVA21) ./mvnw package\n\nfull:\n\t./mvnw package -Pfixup,check\n\n8:\n\tJAVA_HOME=$(JAVA8) ant test\n\n11:\n\tJAVA_HOME=$(JAVA11) ant test\n\n17:\n\tJAVA_HOME=$(JAVA17) ant test\n\n21:\n\tJAVA_HOME=$(JAVA21) ant test\n\nall: cap 8 11 17 21\n\nclean:\n\trm -f *~ *.cap\n\nreuse:\n\treuse --no-multiprocessing lint\n\ntoday:\n\t# for a dirty tree, set the date to today\n\ttest -z \"$(shell git status --porcelain)\" || ./mvnw versions:set -DnewVersion=$(shell date +%y.%m.%d)-SNAPSHOT -DgenerateBackupPoms=false\n"
  },
  {
    "path": "README.md",
    "content": "# Building JavaCard applet CAP files with Ant\n\n> Easy to use [Apache Ant](https://ant.apache.org/) task for building JavaCard CAP files in a declarative way.\n\nHave a consistent and concise build declaration for JavaCard applets, no matter which JavaCard SDK version you use or target.\n\n[![Latest release](https://img.shields.io/github/release/martinpaljak/ant-javacard.svg)](https://github.com/martinpaljak/ant-javacard/releases/latest)\n&nbsp;[![Latest version](https://img.shields.io/maven-metadata/v?label=latest&metadataUrl=https%3A%2F%2Fmvn.javacard.pro%2Fmaven%2FSNAPSHOTS%2Fcom%2Fgithub%2Fmartinpaljak%2Fant-javacard%2Fmaven-metadata.xml)](https://gist.github.com/martinpaljak/c77d11d671260e24eef6c39123345cae)\n&nbsp;[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/martinpaljak/ant-javacard/blob/master/LICENSE)\n&nbsp;[![Build status](https://github.com/martinpaljak/ant-javacard/actions/workflows/robot.yml/badge.svg?branch=master)](https://github.com/martinpaljak/ant-javacard/actions)\n&nbsp;[![Made in Estonia](https://img.shields.io/badge/Made_in-Estonia-0072CE)](https://estonia.ee)\n\n## Features\n * **[Do What I Mean](http://en.wikipedia.org/wiki/DWIM)**. You will [love it](#happy-users)!\n * **No dependencies**, no extra or unrelated downloads. Just **a ~50KB reproducible jar file**.\n * Supports **all available JavaCard SDK versions**: 2.1.2, 2.2.1, 2.2.2, 3.0.3, 3.0.4, 3.0.5, 3.1.0 and 3.2.0\n   * Get one from [oracle.com](https://www.oracle.com/java/technologies/javacard-sdk-downloads.html) or use the [handy Github repository](https://github.com/martinpaljak/oracle_javacard_sdks)\n * **Works on all platforms** with LTS Java 1.8+: Windows, OSX, Linux.\n   * **[Usable SDK-s depend on JDK version](https://github.com/martinpaljak/ant-javacard/wiki/JavaCard-SDK-and-JDK-version-compatibility)**\n * Almost **everything integrates** or works with Ant.\n   * Trigger it [from Maven](https://github.com/martinpaljak/ant-javacard/wiki/How-to-use-from-Maven) or via [Gradle wrapper](https://github.com/bertrandmartel/javacard-gradle-plugin)\n * Can be easily integrated into **continuous integration** workflows.\n * Generates CAP files from **sources** or **pre-compiled** class files.\n * Import **external libraries**: natural use of `.jar` libraries and/or `.exp` files.\n * **No restrictions** on project folder layout (but `src/main/javacard` works).\n\n> [!TIP]\n> Loading JavaCard applets is equally pleasing with **[GlobalPlatformPro](https://github.com/martinpaljak/GlobalPlatformPro)**\n\n## Download & Use\n * Download [`ant-javacard.jar`](https://github.com/martinpaljak/ant-javacard/releases/latest/download/ant-javacard.jar)\n * Or use the download task:\n```xml\n<get src=\"https://github.com/martinpaljak/ant-javacard/releases/latest/download/ant-javacard.jar\" dest=\".\" skipexisting=\"true\"/>\n```\n * Then load the task with the following in your `build.xml` file:\n```xml\n<taskdef name=\"javacard\" classname=\"pro.javacard.ant.JavaCard\" classpath=\"ant-javacard.jar\"/>\n```\n * Now you can create applets within your Ant targets like this:\n```xml\n<javacard>\n  <cap jckit=\"/path/to/jckit_dir\" aid=\"0102030405\">\n    <applet class=\"myapplet.MyApplet\" aid=\"0102030405060708\"/>\n  </cap>\n</javacard>\n```\n(which results in output similar to this)\n```\ntarget:\n      [cap] INFO: using JavaCard 3.0.5 SDK in sdks/jc305u4_kit\n      [cap] INFO: targeting JavaCard 2.2.2 SDK in sdks/jc222_kit\n      [cap] Setting package name to testapplets.empty\n      [cap] INFO: generated applet AID: A000000617008E5CDAAE01 for testapplets.empty.Empty\n      [cap] Building CAP with 1 applet from package testapplets.empty (AID: A000000617008E5CDAAE)\n      [cap] testapplets.empty.Empty A000000617008E5CDAAE01\n  [compile] Compiling files from /Users/martin/projects/ant-javacard/src/testapplets/empty\n  [compile] Compiling 1 source file to /var/folders/gf/_m9mq9td3lz32qv1hd4r12yw0000gn/T/jccpro841338375581620546\n      [cap] CAP saved to /Users/martin/projects/ant-javacard/Empty_A000000617008E5CDAAE_50da91a4_2.2.2.cap\n```\n## Recommended setup\nBased on the [JavaCard SDK and JDK version compatibility matrix](https://github.com/martinpaljak/ant-javacard/wiki/JavaCard-SDK-and-JDK-version-compatibility).\n- Targeting JC 3.0.4 or later (modern JavaCard-s)\n  - Use JDK 17 (don't forget to set `$JAVA_HOME`)\n  - Use JavaCard SDK 3.2.0 (`jckit=\"sdks/jc320v24.0_kit\"`) with right target (`targetsdk=\"3.x.y\"`)\n  - NOTE: depending on your external components, absence of v2.3 export files will force you to stick with JavaCard SDK 3.1\n- Targeting JC 2.2.x or 3.0.1 (older JavaCard-s)\n  - Use JDK 11 (don't forget to set `$JAVA_HOME`)\n  - Use JavaCard SDK 3.0.5u4 (`jckit=\"sdks/jc305u4_kit\"`) with right target (`targetsdk=\"sdks/jc222_kit\"`)\n\n> [!NOTE]\n> ant-javacard will continue to support using legacy JavaCard 2.X SDK-s (and thus JDK-8) for as long as this is achievable with sane effort\n\n## Syntax\nSample:\n\n```xml\n<javacard jckit=\"/path/to/jckit_dir1\">\n  <cap targetsdk=\"/path/to/jckit_dir2\" aid=\"0102030405\" package=\"package.name\" version=\"0.1\" output=\"MyApplet.cap\" sources=\"src/myapplet\" classes=\"path/to/classes\" export=\"mylib\">\n    <applet class=\"myapplet.MyApplet\" aid=\"0102030405060708\"/>\n    <import exps=\"path/to/exps\" jar=\"/path/to/lib.jar\"/>\n  </cap>\n</javacard>\n```\nDetails:\n * `javacard` tag - generic task\n   * `jckit` attribute - path to the JavaCard SDK that is used if individual `cap` does not specify one. Optional if `cap` defines one, required otherwise.\n * `cap` tag - construct a CAP file\n   * `jckit` attribute - path to the JavaCard SDK to be used. Optional if `javacard` defines one, required otherwise.\n   * `targetsdk` attribute - path to the target JavaCard SDK (or `\"3.0.X\"` target version when using JavaCard SDK v3.1), to be used for this CAP. Optional, value of `jckit` used by default. Allows to use a more recent converter to target older JavaCard platforms.\n   * `sources` attribute - path(s) to Java source code, to be compiled against the JavaCard SDK. Either `sources` or `classes` is required, unless `src/main/javacard` or `src/main/java` exists.\n   * `sources2` attribute - additional sources to build per-platform applets. Optional, deprecated (use multiple paths for `sources`)\n   * `classes` attribute - path to pre-compiled class files to be assembled into a CAP file. If both `classes` and `sources` are specified, compiled class files will be put to `classes` folder, which is created if missing.\n   * `includes` attribute - comma or space separated list of patterns of files that must be included (like `**/SomeFile.java`).\n   * `excludes` attribute - comma or space separated list of patterns of files that must be excluded.\n   * `package` attribute - name of the package of the CAP file. Optional for applets - set to the parent package of the applet class if left unspecified, required for libraries\n   * `version` attribute - version of the package. Optional - defaults to 0.0 if left unspecified.\n   * `aid` attribute - AID (hex) of the package. Recommended - or set to the 5 first bytes of the applet AID if left unspecified.\n   * `output` attribute - path where to save the generated CAP file. Optional, see below for variables.\n   * `export` attribute - path (folder) where to place the JAR and generated EXP file. Optional.\n   * `exportmap` attribute - if set to true, use pre-defined export file. Optional.\n   * `jar` attribute - path where to save the generated archive JAR file. Optional.\n   * `jca` attribute - path where to save the generated JavaCard Assembly (JCA) file. Optional.\n   * `verify` attribute - if set to false, disables verification of the resulting CAP file with offcardeverifier. Optional.\n   * `debug` attribute - if set to true, generates debug CAP components. Optional.\n   * `strip` attribute - if set to true, removes class files from target CAP. Optional.\n   * `ints` attribute - if set to true, enables support for 32 bit `int` type. Optional.\n * `applet` tag - for creating an applet inside the CAP\n   * `class` attribute - class of the Applet where install() method is defined. Required.\n   * `aid` attribute - AID (hex) of the applet. Recommended - or set to package `aid`+`i` where `i` is index of the applet definition in the build.xml instruction\n * `import` tag - for linking against external components/libraries, like `GPSystem` or `OPSystem`\n   * `exps` attribute - path to the folder keeping `.exp` files. Optional. Required if file in `jar` does not include .exp files.\n   * `jar` attribute - path to the JAR file for compilation. Required if using `sources` mode and not necessary with `classes` mode if java code is already compiled\n\nNotes:\n * `jc.home` property has the highest precedence, followed by `jckit` path of `cap`, followed by path in `javacard`, followed by `JC_HOME` environment variable. SDK must be valid to be considered for use.\n * All source files are expected to be UTF-8. It is a sane choice, please use it.\n\n### Output file name variables\n\nThe default file name template is `%n_%a_%h_%j_%J.cap` which results in a file name similar to `SomeApplet_010203040506_9a037e30_2.2.2_jdk11.cap`.\n\nFollowing substitutions are available:\n * `%h` - 8 character prefix (hex) of the SHA-256 Load File Data Block hash of the CAP file\n * `%H` - SHA-256 Load File Data Block hash (hex) of the CAP file\n * `%n` - _common name_ of the entity, either applet class (if only one applet) or package name\n * `%p` - package name\n * `%a` - package AID (hex)\n * `%j` - targeted JavaCard version (ex: 3.0.5)\n * `%J` - used JDK version (ex: jdk11)\n * `%v` - applet package version (same as `version` attribute for `cap`, ex: v1.0)\n\n### Command line utility\n`ant-javacard.jar` can be used to dump built .cap file metadata and to re-run off-card verifier.\n\n- dump .cap file metadata\n  - `java -jar ant-javacard.jar <capfile>`\n- run off-card verifier\n  - `java -jar ant-javacard.jar <sdk> [<targetsdk>] <capfile> <expfiles>`\n\n### Environment variables\n- `JAVA_HOME` - path to the JDK to be used.\n- `JC_HOME` - path to the JavaCard SDK to be used if not specified in the build file.\n- `ANT_JAVACARD_TMP` - path to the temporary folder to be used for building CAP files. This is not cleaned after use.\n- `ANT_JAVACARD_DEBUG` - if set, shows debug output.\n\n## Maven dependency\nReleases are published to [`https://mvn.javacard.pro/maven/`](https://mvn.javacard.pro/maven/). To use it, add this to your `pom.xml`:\n\n```xml\n<repositories>\n    <repository>\n        <id>javacard-pro</id>\n        <url>https://mvn.javacard.pro/maven/</url>\n    </repository>\n</repositories>\n```\n\nPushes to Maven Central happen manually and only for selected final versions.\n\n## License\n * [MIT](./LICENSE)\n\n## Happy users\nA random list of users, with a public link:\n* Applets:\n  * [IsoApplet](https://github.com/philipWendland/IsoApplet) by [@philipWendland](https://github.com/philipWendland)\n  * [NdefApplet](https://github.com/promovicz/javacard-ndef) by [@promovicz](https://github.com/promovicz)\n  * [GidsApplet](https://github.com/vletoux/GidsApplet) by [@vletoux](https://github.com/vletoux)\n  * [LedgerWalletApplet](https://github.com/LedgerHQ/ledger-javacard) by [@LedgerHQ](https://github.com/LedgerHQ)\n  * [KeePassNFC](https://github.com/nfd/smartcard_crypto_applet) by [@nfd](https://github.com/nfd)\n  * [PivApplet](https://github.com/arekinath/PivApplet) (PIV) by [@arekinath](https://github.com/arekinath)\n  * [OpenFIPS201](https://github.com/makinako/OpenFIPS201) (PIV) by [@makinako](https://github.com/makinako)\n  * [Cryptonit](https://github.com/mbrossard/cryptonit-applet) (PIV) by [@mbrossard](https://github.com/mbrossard)\n  * [HTOP NDEF](https://github.com/petrs/hotp_via_ndef) by [@petrs](https://github.com/petrs)\n  * [Yubikey OTP](https://github.com/arekinath/YkOtpApplet) by [@arekinath](https://github.com/arekinath)\n  * [SmartPGP](https://github.com/ANSSI-FR/SmartPGP) by [@ANSSI-FR](https://github.com/ANSSI-FR)\n  * [SatochipApplet](https://github.com/Toporin/SatochipApplet) (Bitcoin Hardware Wallet) by [@Toporin](https://github.com/Toporin)\n  * [SIMple-ID](https://github.com/alan-turing-institute/SIMple-ID) by [@alan-turing-institute](https://github.com/alan-turing-institute)\n  * [HelloSTK2](https://github.com/mrlnc/HelloSTK2) (SIM toolkit sample app) by [@mrlnc](https://github.com/mrlnc)\n  * [NeoPGP](https://github.com/mwalle/NeoPGP) by [@mwalle](https://github.com/mwalle)\n  * [EUCLEAK](https://ninjalab.io/eucleak/) by [NinjaLab](https://ninjalab.io)\n  * Plus loads of academic projects, classes and papers.\n* Integration projects:\n  * [JavaCard Gradle plugin](https://github.com/bertrandmartel/javacard-gradle-plugin) by [@bertrandmartel](https://github.com/bertrandmartel)\n  * [JavaCard Template project with Gradle](https://github.com/ph4r05/javacard-gradle-template) by [@ph4r05](https://github.com/ph4r05)\n  * [ant-javacard in Docker](https://github.com/xoryouyou/docker-javacard) by [@xoryouyou](https://github.com/xoryouyou)\n* Other:\n  * **You!** Don't torture yourself with complexity, **KISS!**\n\n## Contact\n * See [javacard.pro](https://javacard.pro)\n * [GlobalPlatform/JavaCard discussions](https://github.com/martinpaljak/GlobalPlatformPro/discussions)\n\n## Similar projects\n * standard JavaCard SDK Ant tasks\n   * :( as cumbersome to use as the command line utilities\n   * :( not declarative/DWIM enough\n   * :) very explicit interface with all details exposed\n * JavaCard Gradle plugin (MIT) - https://github.com/bertrandmartel/javacard-gradle-plugin\n   * :) Wraps ant-javacard for use with Gradle\n * gradle-javacard (Apache 2.0) - https://github.com/fidesmo/gradle-javacard\n   * :) nice declarative interface\n   * :( requires gradle (40M download)\n   * :( JavaCard 2.2.2 only\n * EclipseJCDE (Eclipse 1.0) - http://eclipse-jcde.sourceforge.net/\n   * :( JavaCard 2.2.2 only\n   * :( not possible to integrate in CI - depends on eclipse\n   * :( essentially an Eclipse GUI wrapper for JC SDK\n * JCOP Tools\n   * :( not open source\n * NetBeans IDE JC support\n   * :( not possible to integrate into CI\n   * :( JavaCard 3.0 only\n   * :( Netbeans, not cross platform\n * Maven2 task from FedICT (LGPL3) - https://code.google.com/p/eid-quick-key-toolset\n   * :( Maven downloads half the internet before even simple tasks\n   * :( JavaCard 2.2.2 only\n * Ant script files with templates\n   * :( XML is a *very* bad and verbose programming environment\n"
  },
  {
    "path": "REUSE.toml",
    "content": "version = 1\n\n# Project-owned configuration, build, and documentation files.\n# These are not Java source and do not carry per-file SPDX comments;\n# bulk-declared here per REUSE.software v3.3.\n[[annotations]]\npath = [\n    \"LICENSE\",\n    \"Makefile\",\n    \"README.md\",\n    \".gitignore\",\n    \".gitmodules\",\n    \".github/**\",\n    \"pom.xml\",\n    \"**/pom.xml\",\n    \"build.xml\",\n    \"**/spotbugs.xml\",\n    \"tests.xml\",\n    \"tests-fail.xml\",\n    \"tests-1.8.xml\",\n    \"tests-11.xml\",\n    \"tests-17.xml\",\n    \"tests-21.xml\",\n    \"kits.xml\",\n    \"fails.xml\",\n    \"capfile/src/main/resources/**\",\n]\nprecedence = \"aggregate\"\nSPDX-FileCopyrightText = \"2015 Martin Paljak <martin@martinpaljak.net>\"\nSPDX-License-Identifier = \"MIT\"\n\n# Maven Wrapper - vendored from the Apache Maven project (Apache-2.0).\n[[annotations]]\npath = [\n    \"mvnw\",\n    \"mvnw.cmd\",\n    \".mvn/wrapper/**\",\n]\nprecedence = \"aggregate\"\nSPDX-FileCopyrightText = \"The Apache Software Foundation\"\nSPDX-License-Identifier = \"Apache-2.0\"\n"
  },
  {
    "path": "build.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project basedir=\".\" default=\"dist\" name=\"ant-javacard build\">\n    <!-- Build and load the JavaCard task -->\n    <!-- shenanigans to get the Ant classpath from maven-antrun-plugin, but also work with plain ant -->\n    <target name=\"ant-cp\" unless=\"maven.plugin.classpath\">\n        <path id=\"ant.compile.classpath\"/>\n    </target>\n    <target name=\"maven-cp\" if=\"maven.plugin.classpath\" depends=\"ant-cp\">\n        <path id=\"ant.compile.classpath\" path=\"${maven.plugin.classpath}\"/>\n    </target>\n    <target name=\"jcpro\" depends=\"maven-cp\">\n        <mkdir dir=\"build\"/>\n        <javac debug=\"true\" destdir=\"build\" includeantruntime=\"true\" target=\"8\" source=\"8\">\n            <src path=\"capfile/src/main/java\"/>\n            <src path=\"task/src/main/java\"/>\n            <classpath>\n                <path refid=\"ant.compile.classpath\"/>\n            </classpath>\n            <compilerarg value=\"-Xlint:-options\"/>\n            <compilerarg value=\"-Xlint:all\"/>\n            <exclude name=\"**/module-info.java\"/>\n        </javac>\n        <!-- Load the fresh task -->\n        <path id=\"task\">\n            <pathelement path=\"build\"/>\n        </path>\n        <taskdef name=\"javacard\" classname=\"pro.javacard.ant.JavaCard\" classpathref=\"task\"/>\n    </target>\n    <!-- Get commit and timestamp from git -->\n    <target name=\"git\" description=\"Get git values\">\n        <exec executable=\"git\" outputproperty=\"git.revision\" failifexecutionfails=\"true\" errorproperty=\"\">\n            <arg value=\"describe\"/>\n            <arg value=\"--tags\"/>\n            <arg value=\"--always\"/>\n            <arg value=\"--dirty\"/>\n        </exec>\n        <exec executable=\"git\" outputproperty=\"git.timestamp\" failifexecutionfails=\"true\" errorproperty=\"\">\n            <arg value=\"-c\"/>\n            <arg value=\"log.showSignature=false\"/>\n            <arg value=\"log\"/>\n            <arg value=\"-1\"/>\n            <arg value=\"--format=%aI\"/>\n        </exec>\n        <condition property=\"repository.version\" value=\"${git.revision}\" else=\"unknown\">\n            <and>\n                <isset property=\"git.revision\"/>\n                <length string=\"${git.revision}\" trim=\"yes\" length=\"0\" when=\"greater\"/>\n            </and>\n        </condition>\n        <condition property=\"repository.timestamp\" value=\"${git.timestamp}\" else=\"0\">\n            <and>\n                <isset property=\"git.timestamp\"/>\n                <length string=\"${git.timestamp}\" trim=\"yes\" length=\"0\" when=\"greater\"/>\n            </and>\n        </condition>\n        <echo level=\"info\">${repository.version} @ ${repository.timestamp}</echo>\n    </target>\n    <!-- Package it into a reproducible JAR -->\n    <target name=\"dist\" depends=\"jcpro,git\">\n        <!-- Have the modification timestamp from last git commit -->\n        <jar destfile=\"ant-javacard.jar\" level=\"9\" basedir=\"build\" modificationtime=\"${repository.timestamp}\">\n            <manifest>\n                <!-- It is possible to execute ant-javacard.jar... -->\n                <attribute name=\"Main-Class\" value=\"pro.javacard.ant.DummyMain\"/>\n                <!-- Blank out volatile values, so the same jar could be built with slight variations in JDK/ant version -->\n                <attribute name=\"Created-By\" value=\"ant-javacard build ${repository.version}\"/>\n                <attribute name=\"Implementation-Version\" value=\"${repository.version}\"/>\n                <attribute name=\"Ant-Version\" value=\"irrelevant\"/>\n            </manifest>\n        </jar>\n        <!-- Now this JAR can be used in your build.xml by placing the jar to -->\n        <!-- lib folder and having the following in your target: -->\n        <!-- <taskdef name=\"javacard\" classname=\"pro.javacard.ant.JavaCard\" classpath=\"lib/ant-javacard.jar\"/> -->\n    </target>\n    <!-- Build smoke test applets -->\n    <import file=\"tests-${ant.java.version}.xml\"/>\n    <import file=\"tests-fail.xml\"/>\n    <!-- Cleanup! -->\n    <target name=\"clean\">\n        <delete dir=\"build\"/>\n        <delete>\n            <fileset dir=\".\" includes=\"*.cap\"/>\n        </delete>\n        <delete file=\"ant-javacard.jar\"/>\n    </target>\n</project>\n"
  },
  {
    "path": "capfile/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>com.github.martinpaljak</groupId>\n        <artifactId>ant-javacard-package</artifactId>\n        <version>26.03.08-SNAPSHOT</version>\n    </parent>\n    <artifactId>capfile</artifactId>\n    <name>JavaCard SDK and CAP file library</name>\n    <properties>\n        <project.build.outputTimestamp>2026-03-08T04:57:03Z</project.build.outputTimestamp>\n    </properties>\n    <dependencies>\n        <dependency>\n            <groupId>org.testng</groupId>\n            <artifactId>testng</artifactId>\n            <version>7.12.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n            <version>2.0.17</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-simple</artifactId>\n            <version>2.0.17</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "capfile/spotbugs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<FindBugsFilter xmlns=\"https://github.com/spotbugs/filter/3.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd\">\n  <!-- two false positives in Java 11, see https://github.com/spotbugs/spotbugs/issues/756 -->\n  <Match>\n    <Bug pattern=\"RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE\"/>\n  </Match>\n  <Match>\n    <Bug pattern=\"RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE\"/>\n  </Match>\n</FindBugsFilter>\n"
  },
  {
    "path": "capfile/src/main/java/module-info.java",
    "content": "// SPDX-FileCopyrightText: 2024 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\n@SuppressWarnings({\"requires-automatic\"})\nmodule pro.javacard.capfile {\n    requires java.xml;\n    requires java.logging;\n\n    exports pro.javacard.capfile;\n    exports pro.javacard.sdk;\n}\n"
  },
  {
    "path": "capfile/src/main/java/pro/javacard/capfile/AID.java",
    "content": "// SPDX-FileCopyrightText: 2018 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.capfile;\n\nimport java.util.Arrays;\n\npublic final class AID {\n    private final byte[] bytes;\n\n    public AID(byte[] bytes) throws IllegalArgumentException {\n        this(bytes, 0, bytes.length);\n    }\n\n    public AID(String str) throws IllegalArgumentException {\n        this(HexUtils.hex2bin(str));\n    }\n\n    public AID(byte[] bytes, int offset, int length) throws IllegalArgumentException {\n        if ((length < 5) || (length > 16)) {\n            throw new IllegalArgumentException(\"AID must be between 5 and 16 bytes: \" + length);\n        }\n        this.bytes = Arrays.copyOfRange(bytes, offset, offset + length);\n    }\n\n    public static AID fromString(Object s) {\n        if (s instanceof String) {\n            return new AID(HexUtils.stringToBin((String) s));\n        }\n        throw new IllegalArgumentException(\"AID should be string\");\n    }\n\n    public byte[] getBytes() {\n        return bytes.clone();\n    }\n\n    public int getLength() {\n        return bytes.length;\n    }\n\n    @Override\n    public String toString() {\n        return HexUtils.bin2hex(bytes);\n    }\n\n    @Override\n    public int hashCode() {\n        return Arrays.hashCode(bytes);\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (o instanceof AID) {\n            return Arrays.equals(((AID) o).bytes, bytes);\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "capfile/src/main/java/pro/javacard/capfile/CAPFile.java",
    "content": "// SPDX-FileCopyrightText: 2018 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\n// Loosely based on code from GlobalPlatformPro, originally from GPJ\npackage pro.javacard.capfile;\n\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.NodeList;\nimport org.xml.sax.SAXException;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\nimport java.io.*;\nimport java.net.URI;\nimport java.nio.file.FileSystem;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.*;\nimport java.util.jar.Attributes;\nimport java.util.jar.Manifest;\nimport java.util.stream.Collectors;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\nimport java.util.zip.ZipOutputStream;\n\n/**\n * Parses a CAP file as specified in JavaCard 2.2 VM Specification, chapter 6.\n * CAP files are tiny, so we keep it in memory.\n */\npublic final class CAPFile {\n\n    private static final String[] componentNames = {\"Header\", \"Directory\", \"Import\", \"Applet\", \"Class\", \"Method\", \"StaticField\", \"Export\",\n            \"ConstantPool\", \"RefLocation\", \"Descriptor\", \"Debug\"};\n    protected final Map<String, byte[]> entries; // All raw ZIP entries\n    // Parsed content\n    private final Map<AID, String> applets = new LinkedHashMap<>();\n    private final List<CAPPackage> imports = new ArrayList<>();\n    private final CAPPackage pkg;\n    private final byte flags;\n    private final String cap_version;\n    // Metadata\n    private Manifest manifest = null; // From 2.2.2\n    private Document appletxml = null; // From 3.0.1\n    private Path file;\n\n\n    public static CAPFile fromStream(InputStream in) throws IOException {\n        return new CAPFile(in);\n    }\n\n    public static CAPFile fromBytes(byte[] bytes) throws IOException {\n        return fromStream(new ByteArrayInputStream(bytes));\n    }\n\n    public static CAPFile fromFile(Path path) throws IOException {\n        try (InputStream in = Files.newInputStream(path)) {\n            CAPFile cap = fromStream(in);\n            cap.file = path;\n            return cap;\n        }\n    }\n\n    public Optional<Path> getFile() {\n        return Optional.ofNullable(file);\n    }\n\n    public byte[] getComponent(String name) {\n        byte[] c = entries.get(pkg2jcdir(getPackageName()) + name + \".cap\");\n        return c == null ? null : c.clone();\n    }\n\n    public byte[] getMetaInfEntry(String name) {\n        return entries.get(\"META-INF/\" + name);\n    }\n\n    public Optional<byte[]> getZipComponent(String name) {\n        return Optional.ofNullable(entries.get(name));\n    }\n\n    public void store(OutputStream to) throws IOException {\n        try (ZipOutputStream out = new ZipOutputStream(to)) {\n            for (Map.Entry<String, byte[]> e : entries.entrySet()) {\n                out.putNextEntry(new ZipEntry(e.getKey()));\n                out.write(e.getValue());\n                out.closeEntry();\n            }\n        }\n    }\n\n    // XXX: 21 rightfully complains about this without final (getComponent leaking this)\n    protected CAPFile(InputStream in) throws IOException {\n        try (ZipInputStream zip = new ZipInputStream(in)) {\n            // All ZIP entries\n            entries = readEntries(zip);\n        }\n\n        // Parse manifest\n        byte[] mf = entries.get(\"META-INF/MANIFEST.MF\");\n        if (mf != null) {\n            ByteArrayInputStream mfi = new ByteArrayInputStream(mf);\n            manifest = new Manifest(mfi);\n        }\n\n        // Only if there are applets\n        byte[] ai = entries.get(\"APPLET-INF/applet.xml\");\n        if (ai != null) {\n            try {\n                DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();\n                // Not really a threat (intended for self-generated local files) but still nice to have\n                dbFactory.setFeature(\"http://apache.org/xml/features/disallow-doctype-decl\", true);\n                DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();\n                appletxml = dBuilder.parse(new ByteArrayInputStream(ai));\n                appletxml.getDocumentElement().normalize();\n            } catch (SAXException | ParserConfigurationException e) {\n                throw new IOException(e);\n            }\n        }\n\n\n        // Figure out package name. Failsafe without metadata as well, for 2.1.X support.\n        String pkgname = null;\n        for (String p : entries.keySet()) {\n            if (p.endsWith(\"Header.cap\")) {\n                pkgname = jcdir2pkg(p);\n                break;\n            }\n        }\n\n        if (pkgname == null) {\n            throw new IOException(\"Could not figure out the package name of the applet!\");\n        }\n\n        // Parse package.\n        // See JCVM 2.2 spec section 6.3 for offsets.\n        byte[] header = entries.get(pkg2jcdir(pkgname) + \"Header.cap\");\n        cap_version = String.format(\"%d.%d\", header[8], header[7]);\n        flags = header[9];\n\n        pkg = new CAPPackage(new AID(header, 13, header[12]), header[11], header[10], pkgname);\n\n        // Parse applets\n        // See JCVM 2.2 spec section 6.5 for offsets.\n        byte[] applet = getComponent(\"Applet\");\n        if (applet != null) {\n            int offset = 4;\n            for (int j = 0; j < (applet[3] & 0xFF); j++) {\n                int len = applet[offset++];\n                AID appaid = new AID(applet, offset, len);\n                // We might already have it, with the name from metadata\n                // FIXME: use metadata only as additional source\n                if (!applets.containsKey(appaid)) {\n                    applets.put(appaid, null);\n                }\n                // Skip install_method_offset\n                offset += len + 2;\n            }\n        }\n        // Parse imports\n        byte[] imps = getComponent(\"Import\");\n        if (imps != null) {\n            int offset = 4;\n            for (int j = 0; j < (imps[3] & 0xFF); j++) {\n                AID aid = new AID(imps, offset + 3, imps[offset + 2]);\n                CAPPackage p = new CAPPackage(aid, imps[offset + 1], imps[offset]);\n                imports.add(p);\n                offset += imps[offset + 2] + 3;\n            }\n        }\n\n        // Parse metadata to get applet names. Somewhat redundant\n        if (appletxml != null) {\n            NodeList apps = appletxml.getElementsByTagName(\"applet\");\n            for (int i = 0; i < apps.getLength(); i++) {\n                Element app = (Element) apps.item(i);\n                String name = app.getElementsByTagName(\"applet-class\").item(0).getTextContent();\n                String aidstring = app.getElementsByTagName(\"applet-AID\").item(0).getTextContent();\n                AID aid = AID.fromString(aidstring.replace(\"//aid/\", \"\").replace(\"/\", \"\"));\n                if (!applets.containsKey(aid)) {\n                    throw new IOException(\"applet.xml contains missing applet \" + aid);\n                }\n                applets.put(aid, name);\n            }\n        }\n    }\n\n    private static Map<String, byte[]> readEntries(ZipInputStream in) throws IOException {\n        Map<String, byte[]> result = new LinkedHashMap<>();\n        ZipEntry entry = in.getNextEntry();\n        while (entry != null) {\n            ByteArrayOutputStream bos = new ByteArrayOutputStream();\n            byte[] buf = new byte[1024];\n            int c;\n            while ((c = in.read(buf)) != -1) {\n                bos.write(buf, 0, c);\n            }\n            result.put(entry.getName(), bos.toByteArray());\n            entry = in.getNextEntry();\n        }\n        return Collections.unmodifiableMap(result);\n    }\n\n    public AID getPackageAID() {\n        return pkg.aid;\n    }\n\n    public List<AID> getAppletAIDs() {\n        List<AID> result = new ArrayList<>();\n        result.addAll(applets.keySet());\n        return result;\n    }\n\n    public String getPackageVersion() {\n        return pkg.getVersionString();\n    }\n\n    public String getPackageName() {\n        return pkg.getName().orElseThrow(() -> new IllegalStateException(\"No package name\"));\n    }\n\n    public byte[] getCode() {\n        return _getCode(false);\n    }\n\n    @Deprecated\n    public byte[] getCode(boolean includeDebug) {\n        return _getCode(includeDebug);\n    }\n\n    byte[] _getCode(boolean includeDebug) {\n        ByteArrayOutputStream result = new ByteArrayOutputStream();\n        for (String name : componentNames) {\n            byte[] c = getComponent(name);\n            if (c == null) {\n                continue;\n            }\n            if (!includeDebug && (name.equals(\"Debug\") || name.equals(\"Descriptor\"))) {\n                continue;\n            }\n            try {\n                result.write(c);\n            } catch (IOException e) {\n                throw new UncheckedIOException(e);\n            }\n        }\n        return result.toByteArray();\n    }\n\n    public byte[] getLoadFileDataHash(String hash) {\n        try {\n            return MessageDigest.getInstance(hash).digest(getCode());\n        } catch (NoSuchAlgorithmException e) {\n            throw new RuntimeException(\"Not possible\", e);\n        }\n    }\n\n    @Deprecated\n    public byte[] getLoadFileDataHash(String hash, boolean includeDebug) {\n        try {\n            return MessageDigest.getInstance(hash).digest(_getCode(includeDebug));\n        } catch (NoSuchAlgorithmException e) {\n            throw new RuntimeException(\"Not possible\", e);\n        }\n    }\n\n    public void dump(PrintStream out) {\n        Optional<String> gpv = guessGlobalPlatformVersion();\n        Optional<String> jcv = guessJavaCardVersion();\n        String gpversion = gpv.isPresent() ? \"/GlobalPlatform \" + gpv.get() : \"\";\n\n        out.println(String.format(\"CAP file (v%s), contains: %s for JavaCard %s%s\", cap_version, String.join(\", \", getFlags()), jcv.orElse(\"2.1.1?\"), gpversion));\n        out.printf(\"Package: %s %s v%s%n\", pkg.getName().get(), pkg.getAid().toString(), pkg.getVersionString());\n        for (Map.Entry<AID, String> applet : getApplets().entrySet()) {\n            out.println(\"Applet:  \" + (applet.getValue() == null ? \"\" : applet.getValue() + \" \") + applet.getKey());\n        }\n        for (CAPPackage imp : getImports()) {\n            out.println(\"Import:  \" + imp);\n        }\n        // Check manifest for metadata\n        if (manifest != null) {\n            Attributes mains = manifest.getMainAttributes();\n\n            // iterate all packages\n            Map<String, Attributes> ent = manifest.getEntries();\n            if (ent.keySet().size() > 1) {\n                throw new IllegalArgumentException(\"Too many elements in CAP manifest\");\n            }\n            if (ent.keySet().size() == 1) {\n                Attributes caps = ent.get(ent.keySet().toArray()[0]);\n                // Generic\n                String jdk_name = mains.getValue(\"Created-By\");\n                // JC specific\n                String cap_creation_time = caps.getValue(\"Java-Card-CAP-Creation-Time\");\n                String converter_version = caps.getValue(\"Java-Card-Converter-Version\");\n                String converter_provider = caps.getValue(\"Java-Card-Converter-Provider\");\n\n                out.println(String.format(\"Generated by %s converter %s\", converter_provider, converter_version));\n                out.println(String.format(\"On %s with JDK %s\", cap_creation_time, jdk_name));\n            }\n        }\n        out.println(String.format(\"Code size %d bytes (%d with debug)\", getCode().length, getCode(true).length));\n        out.println(\"SHA-256 \" + HexUtils.bin2hex(getLoadFileDataHash(\"SHA-256\")).toLowerCase());\n        out.println(\"SHA-1   \" + HexUtils.bin2hex(getLoadFileDataHash(\"SHA-1\")).toLowerCase());\n    }\n\n    public List<String> getFlags() {\n        return flags2strings(flags);\n    }\n\n    public static List<String> flags2strings(byte flags) {\n        ArrayList<String> result = new ArrayList<>();\n        // Table 6-3: CAP File Package Flags\n        if ((flags & 0x01) == 0x01) {\n            result.add(\"integers\");\n        }\n        if ((flags & 0x02) == 0x02) {\n            result.add(\"exports\");\n        }\n        if ((flags & 0x04) == 0x04) {\n            result.add(\"applets\");\n        }\n        return result;\n    }\n\n    public List<CAPPackage> getImports() {\n        return Collections.unmodifiableList(imports);\n    }\n\n    public Map<AID, String> getApplets() {\n        return Collections.unmodifiableMap(applets);\n    }\n\n    // Guess the targeted JavaCard version based on imported package versions.\n    //\n    // Mapping derived from parsing export files in actual SDK kits (jc211 through jc320v25.1):\n    //   framework      1.0=2.1.x, 1.2=2.2.1, 1.3=2.2.2, 1.4=3.0.1, 1.5=3.0.4, 1.6=3.0.5, 1.8=3.1.0, 1.9=3.2.0\n    //   security/crypto 1.1=2.1.x, 1.2=2.2.1, 1.3=2.2.2, 1.4=3.0.1, 1.5=3.0.4, 1.6=3.0.5, 1.7=3.1.0, 1.8=3.2.0\n    // Note: framework minor versions diverge from security/crypto starting from 3.1.0 (framework skips minor=7).\n    // Note: SDK 2.1.1 and 2.1.2 ship identical module versions - indistinguishable.\n    public Optional<String> guessJavaCardVersion() {\n        // Primary: javacard.framework has a unique version progression (skips minor=7)\n        AID jf = new AID(\"A0000000620101\"); // javacard.framework\n        for (CAPPackage p : imports) {\n            if (p.aid.equals(jf)) {\n                switch (p.minor) {\n                    case 0:\n                        return Optional.of(\"2.1.1\");\n                    case 1:\n                        // No actual SDK ships framework 1.1; kept for historical reasons\n                        return Optional.of(\"2.1.2\");\n                    case 2:\n                        return Optional.of(\"2.2.1\");\n                    case 3:\n                        return Optional.of(\"2.2.2\");\n                    case 4:\n                        return Optional.of(\"3.0.1\");\n                    case 5:\n                        return Optional.of(\"3.0.4\");\n                    case 6:\n                        return Optional.of(\"3.0.5\");\n                    // minor=7 not used by any SDK\n                    case 8:\n                        return Optional.of(\"3.1.0\");\n                    case 9:\n                        return Optional.of(\"3.2.0\");\n                    default:\n                        return Optional.of(String.format(\"unknown: %d.%d\", p.major, p.minor));\n                }\n            }\n        }\n\n        // Fallback: javacard.security and javacardx.crypto share identical version progression\n        AID js = new AID(\"A0000000620102\"); // javacard.security\n        AID jc = new AID(\"A0000000620201\"); // javacardx.crypto\n        for (CAPPackage p : imports) {\n            if (p.aid.equals(js) || p.aid.equals(jc)) {\n                switch (p.minor) {\n                    case 1:\n                        return Optional.of(\"2.1.1\");\n                    case 2:\n                        return Optional.of(\"2.2.1\");\n                    case 3:\n                        return Optional.of(\"2.2.2\");\n                    case 4:\n                        return Optional.of(\"3.0.1\");\n                    case 5:\n                        return Optional.of(\"3.0.4\");\n                    case 6:\n                        return Optional.of(\"3.0.5\");\n                    case 7:\n                        return Optional.of(\"3.1.0\");\n                    case 8:\n                        return Optional.of(\"3.2.0\");\n                    default:\n                        return Optional.of(String.format(\"unknown: %d.%d\", p.major, p.minor));\n                }\n            }\n        }\n        return Optional.empty();\n    }\n\n    // Guess GP version from org.globalplatform import version (A00000015100)\n    public Optional<String> guessGlobalPlatformVersion() {\n        AID jf = new AID(\"A00000015100\");\n        for (CAPPackage p : imports) {\n            if (p.aid.equals(jf) && p.major == 1) {\n                if (p.minor == 0) {\n                    return Optional.of(\"2.1.1\");\n                } else if (p.minor >= 1 && p.minor <= 4) {\n                    return Optional.of(\"2.2\");\n                } else if (p.minor == 5 || p.minor == 6) {\n                    return Optional.of(\"2.2.1\");\n                } else if (p.minor == 7) {\n                    // This is not really right, but a good indication nevertheless\n                    return Optional.of(\"2.3.1+A\");\n                } else {\n                    return Optional.of(String.format(\"unknown: %d.%d\", p.major, p.minor));\n                }\n            }\n        }\n        return Optional.empty();\n    }\n\n    private static String pkg2jcdir(String pkgname) {\n        return pkgname.replace(\".\", \"/\") + \"/javacard/\";\n    }\n\n    private static String jcdir2pkg(String jcdir) {\n        return jcdir.substring(0, jcdir.lastIndexOf(\"/javacard/\")).replace('/', '.');\n    }\n\n    public static void uncheckedDelete(Path p) throws UncheckedIOException {\n        try {\n            Files.delete(p);\n        } catch (IOException e) {\n            throw new UncheckedIOException(e);\n        }\n    }\n\n    // Remove compiled code from capfile\n    public static void strip(Path cap) throws IOException {\n        Map<String, String> props = new HashMap<>();\n        props.put(\"create\", \"false\");\n\n        URI zip_disk = URI.create(\"jar:\" + cap.toUri());\n        try (FileSystem zipfs = FileSystems.newFileSystem(zip_disk, props)) {\n            List<Path> toDelete = Files.walk(zipfs.getPath(\"/\")).filter(p -> p.toString().endsWith(\".class\")).collect(Collectors.toList());\n            Collections.sort(toDelete, Collections.reverseOrder(Comparator.comparingInt(o -> o.toString().length())));\n            toDelete.stream().forEach(CAPFile::uncheckedDelete);\n        }\n    }\n}\n"
  },
  {
    "path": "capfile/src/main/java/pro/javacard/capfile/CAPPackage.java",
    "content": "// SPDX-FileCopyrightText: 2018 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.capfile;\n\nimport java.util.Objects;\nimport java.util.Optional;\n\npublic final class CAPPackage {\n    final AID aid;\n    final int major;\n    final int minor;\n    final String name;\n\n    public CAPPackage(AID aid, int major, int minor) {\n        this(aid, major, minor, null);\n    }\n\n    public CAPPackage(AID aid, int major, int minor, String name) {\n        this.aid = aid;\n        this.major = major;\n        this.minor = minor;\n        this.name = name;\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other instanceof CAPPackage) {\n            CAPPackage o = (CAPPackage) other;\n            return aid.equals(o.aid) && major == o.major && minor == o.minor;\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(aid, major, minor);\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\"%-32s v%d.%d %s\", aid, major, minor, getName().orElse(WellKnownAID.getName(aid).orElse(\"(unknown)\")));\n    }\n\n    public String getVersionString() {\n        return String.format(\"%d.%d\", major, minor);\n    }\n\n    public AID getAid() {\n        return aid;\n    }\n\n    public int getMinor() {\n        return minor;\n    }\n\n    public int getMajor() {\n        return major;\n    }\n\n    public Optional<String> getName() {\n        return Optional.ofNullable(name);\n    }\n}\n"
  },
  {
    "path": "capfile/src/main/java/pro/javacard/capfile/HexUtils.java",
    "content": "// SPDX-FileCopyrightText: 2016 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.capfile;\n\npublic class HexUtils {\n    private HexUtils() {}\n\n    // This code has been taken from Apache commons-codec 1.7 (License: Apache 2.0)\n    private static final char[] UPPER_HEX = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};\n\n    public static String encodeHexString_imp(final byte[] data) {\n\n        final int l = data.length;\n        final char[] out = new char[l << 1];\n        // two characters form the hex value.\n        for (int i = 0, j = 0; i < l; i++) {\n            out[j++] = UPPER_HEX[(0xF0 & data[i]) >>> 4];\n            out[j++] = UPPER_HEX[0x0F & data[i]];\n        }\n        return new String(out);\n    }\n\n    public static byte[] decodeHexString_imp(String str) {\n        char data[] = str.toCharArray();\n        final int len = data.length;\n        if ((len & 0x01) != 0) {\n            throw new IllegalArgumentException(\"Odd number of characters: \" + str);\n        }\n        final byte[] out = new byte[len >> 1];\n        // two characters form the hex value.\n        for (int i = 0, j = 0; j < len; i++) {\n            int f = Character.digit(data[j], 16) << 4;\n            if (f < 0) {\n                throw new IllegalArgumentException(\"Illegal hex: \" + data[j]);\n            }\n            j++;\n            f = f | Character.digit(data[j], 16);\n            if (f < 0) {\n                throw new IllegalArgumentException(\"Illegal hex: \" + data[j]);\n            }\n            j++;\n            out[i] = (byte) (f & 0xFF);\n        }\n        return out;\n    }\n\n    // End of copied code from commons-codec\n    public static byte[] hex2bin(final String hex) {\n        return decodeHexString_imp(hex);\n    }\n\n    public static String bin2hex(final byte[] bin) {\n        return encodeHexString_imp(bin);\n    }\n\n    public static byte[] stringToBin(String s) {\n        s = s.toUpperCase().replaceAll(\" \", \"\").replaceAll(\":\", \"\");\n        s = s.replaceAll(\"0X\", \"\").replaceAll(\"\\n\", \"\").replaceAll(\"\\t\", \"\");\n        s = s.replaceAll(\";\", \"\");\n        return decodeHexString_imp(s);\n    }\n}\n"
  },
  {
    "path": "capfile/src/main/java/pro/javacard/capfile/WellKnownAID.java",
    "content": "// SPDX-FileCopyrightText: 2018 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.capfile;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Properties;\n\n// Static class for translating AID-s into human-readable form\npublic final class WellKnownAID {\n\n    private static final Map<AID, String> javaCardRegistry = new LinkedHashMap<>();\n    private static final Map<AID, String> wellKnownRegistry = new LinkedHashMap<>();\n\n    private WellKnownAID() {}\n\n    static {\n        // Copied from https://stackoverflow.com/questions/25031338/how-to-get-javacard-version-on-card/25063015#25063015\n        // Extended and verified against JC SDK exp files\n        javaCardRegistry.put(AID.fromString(\"A0000000620001\"), \"java.lang\");\n        javaCardRegistry.put(AID.fromString(\"A0000000620002\"), \"java.io\");\n        javaCardRegistry.put(AID.fromString(\"A0000000620003\"), \"java.rmi\");\n\n        javaCardRegistry.put(AID.fromString(\"A0000000620101\"), \"javacard.framework\");\n        javaCardRegistry.put(AID.fromString(\"A000000062010101\"), \"javacard.framework.service\");\n        javaCardRegistry.put(AID.fromString(\"A0000000620102\"), \"javacard.security\");\n\n        javaCardRegistry.put(AID.fromString(\"A0000000620201\"), \"javacardx.crypto\");\n        javaCardRegistry.put(AID.fromString(\"A0000000620202\"), \"javacardx.biometry\");\n        javaCardRegistry.put(AID.fromString(\"A0000000620203\"), \"javacardx.external\");\n        javaCardRegistry.put(AID.fromString(\"A0000000620204\"), \"javacardx.biometry1toN\");\n        javaCardRegistry.put(AID.fromString(\"A0000000620205\"), \"javacardx.security\");\n        javaCardRegistry.put(AID.fromString(\"A000000062020501\"), \"javacardx.security.cert\");\n        javaCardRegistry.put(AID.fromString(\"A000000062020502\"), \"javacardx.security.derivation\");\n        javaCardRegistry.put(AID.fromString(\"A000000062020503\"), \"javacardx.security.util\");\n\n        javaCardRegistry.put(AID.fromString(\"A000000062020801\"), \"javacardx.framework.util\");\n        javaCardRegistry.put(AID.fromString(\"A00000006202080101\"), \"javacardx.framework.util.intx\");\n        javaCardRegistry.put(AID.fromString(\"A000000062020802\"), \"javacardx.framework.math\");\n        javaCardRegistry.put(AID.fromString(\"A000000062020803\"), \"javacardx.framework.tlv\");\n        javaCardRegistry.put(AID.fromString(\"A000000062020804\"), \"javacardx.framework.string\");\n        javaCardRegistry.put(AID.fromString(\"A000000062020805\"), \"javacardx.framework.event\");\n        javaCardRegistry.put(AID.fromString(\"A000000062020806\"), \"javacardx.framework.nio\");\n        javaCardRegistry.put(AID.fromString(\"A000000062020807\"), \"javacardx.framework.time\");\n\n\n        javaCardRegistry.put(AID.fromString(\"A0000000620209\"), \"javacardx.apdu\");\n        javaCardRegistry.put(AID.fromString(\"A000000062020901\"), \"javacardx.apdu.util\");\n\n        // Other well-known AID-s\n        wellKnownRegistry.put(AID.fromString(\"A00000015100\"), \"org.globalplatform\");\n        wellKnownRegistry.put(AID.fromString(\"A00000015102\"), \"org.globalplatform.contactless\");\n        wellKnownRegistry.put(AID.fromString(\"A0000000030000\"), \"visa.openplatform\");\n        // Global Platform SSD\n        wellKnownRegistry.put(AID.fromString(\"A0000001515350\"), \"SSD creation package\");\n        wellKnownRegistry.put(AID.fromString(\"A000000151535041\"), \"SSD creation applet\");\n        // Global Platform CRS (Contactless Registry Service) - Amendment C\n        wellKnownRegistry.put(AID.fromString(\"A000000151435253\"), \"CRS package\");\n        wellKnownRegistry.put(AID.fromString(\"A00000015143525300\"), \"CRS application\");\n\n        wellKnownRegistry.put(AID.fromString(\"A0000000090003FFFFFFFF8910710001\"), \"sim.access\");\n        wellKnownRegistry.put(AID.fromString(\"A0000000090003FFFFFFFF8910710002\"), \"sim.toolkit\");\n        wellKnownRegistry.put(AID.fromString(\"A0000000090005FFFFFFFF8916010000\"), \"uicc.hci.framework\");\n        wellKnownRegistry.put(AID.fromString(\"A0000000090005FFFFFFFF8916020100\"), \"uicc.hci.services.cardemulation\");\n        wellKnownRegistry.put(AID.fromString(\"A0000000090005FFFFFFFF8916020200\"), \"uicc.hci.services.connectivity\");\n\n        // Load internal\n        try (InputStream in = WellKnownAID.class.getResourceAsStream(\"aid_list.properties\")) {\n            // If run differently, might not have the list\n            if (in != null) {\n                load(in);\n            }\n        } catch (IOException e) {\n            throw new RuntimeException(\"Can not load builtin list of AID-s: \" + e.getMessage(), e);\n        }\n\n        // Try to load more\n        Path p = Paths.get(System.getenv().getOrDefault(\"AID_LIST\", Paths.get(System.getProperty(\"user.home\"), \".apdu4j\", \"aid_list.properties\").toString()));\n        load(p);\n    }\n\n    public static void load(InputStream in) {\n        // FIXME: add logging instead of system.err\n        try {\n            Properties props = new Properties();\n            props.load(in);\n            for (String k: props.stringPropertyNames()) {\n                wellKnownRegistry.put(AID.fromString(k), props.getProperty(k));\n            }\n        } catch (ClassCastException e) {\n            System.err.println(\"Invalid format: \" + e.getMessage());\n        } catch (IOException e) {\n            System.out.println(\"Could not parse AID list: \" + e.getMessage());\n        }\n    }\n\n    public static void load(Path p) {\n        if (!Files.exists(p)) {\n            return;\n        }\n        try (InputStream in = Files.newInputStream(p)) {\n            load(in);\n        } catch (IOException e) {\n            System.err.println(\"Could not parse AID list: \" + e.getMessage());\n        }\n    }\n\n    public static String getJavaCardName(AID aid) {\n        return javaCardRegistry.get(aid);\n    }\n\n    public static Optional<String> getName(AID aid) {\n        return Optional.ofNullable(wellKnownRegistry.getOrDefault(aid, javaCardRegistry.get(aid)));\n    }\n}\n"
  },
  {
    "path": "capfile/src/main/java/pro/javacard/sdk/ExportFileHelper.java",
    "content": "// SPDX-FileCopyrightText: 2024 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.sdk;\n\nimport pro.javacard.capfile.HexUtils;\n\nimport java.io.DataInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.HashMap;\nimport java.util.Map;\n\n// Export file format: JCVM Spec v3.2, Chapter 5 \"The Export File Format\"\npublic final class ExportFileHelper {\n\n    // JCVM 5.5: \"The magic item contains the magic number identifying the ExportFile format; it has the value 0x00FACADE.\"\n    static final int MAGIC = 0x00FACADE;\n\n    // JCVM 5.6, Table 5-1: Export File Constant Pool Tags\n    static final int TAG_UTF8 = 1;\n    static final int TAG_INTEGER = 3;\n    static final int TAG_CLASSREF = 7;\n    static final int TAG_PACKAGE = 13;\n\n    public enum ExportFileVersion {\n        V21,\n        V22,\n        V23\n    }\n\n    public static final class PackageInfo {\n        private final ExportFileVersion version;\n        private final String name;\n        private final byte[] aid;\n        private final int major;\n        private final int minor;\n        private final boolean library;\n\n        PackageInfo(ExportFileVersion version, String name, byte[] aid,\n                    int major, int minor, boolean library) {\n            this.version = version;\n            this.name = name;\n            this.aid = aid.clone();\n            this.major = major;\n            this.minor = minor;\n            this.library = library;\n        }\n\n        public ExportFileVersion getVersion() {\n            return version;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public byte[] getAid() {\n            return aid.clone();\n        }\n\n        public int getMajor() {\n            return major;\n        }\n\n        public int getMinor() {\n            return minor;\n        }\n\n        public String getPackageVersion() {\n            return String.format(\"%d.%d\", major, minor);\n        }\n\n        public boolean isLibrary() {\n            return library;\n        }\n\n        @Override\n        public String toString() {\n            return String.format(\"%s %s%s v%s (%s)\", name, HexUtils.bin2hex(aid), library ? \" library\" : \"\", getPackageVersion(), version);\n        }\n    }\n\n    private ExportFileHelper() {\n    }\n\n    public static PackageInfo parsePackage(Path path) throws IOException {\n        try (InputStream in = Files.newInputStream(path)) {\n            return parsePackage(in);\n        }\n    }\n\n    public static PackageInfo parsePackage(InputStream in) throws IOException {\n        DataInputStream dis = new DataInputStream(in);\n\n        // JCVM 5.5: magic (u4)\n        int magic = dis.readInt();\n        if (magic != MAGIC) {\n            throw new IllegalArgumentException(String.format(\"Bad magic: 0x%08X\", magic));\n        }\n\n        // JCVM 5.5: minor_version (u1), major_version (u1)\n        byte fileMinor = dis.readByte();\n        byte fileMajor = dis.readByte();\n        if (fileMajor != 2) {\n            throw new IllegalArgumentException(\"Invalid export file major version: \" + fileMajor);\n        }\n        ExportFileVersion version = parseFileVersion(fileMinor);\n\n        // JCVM 5.5: constant_pool_count (u2)\n        int cpCount = dis.readUnsignedShort();\n        // JCVM 5.6: constant_pool[]\n        Object[] pool = new Object[cpCount];\n\n        // Store all CONSTANT_Package entries by index, since this_package\n        // tells us which one is the actual exported package\n        Map<Integer, int[]> pkgEntries = new HashMap<>(); // index -> [flags, nameIndex, minor, major]\n        Map<Integer, byte[]> pkgAids = new HashMap<>();   // index -> aid\n\n        for (int i = 0; i < cpCount; i++) {\n            int tag = dis.readUnsignedByte();\n            switch (tag) {\n                case TAG_UTF8: {\n                    // JCVM 5.6.4: length (u2), bytes[length]\n                    int len = dis.readUnsignedShort();\n                    byte[] bytes = new byte[len];\n                    dis.readFully(bytes);\n                    pool[i] = new String(bytes, \"UTF-8\");\n                    break;\n                }\n                case TAG_INTEGER: {\n                    // JCVM 5.6.3: bytes (u4)\n                    dis.readInt();\n                    break;\n                }\n                case TAG_CLASSREF: {\n                    // JCVM 5.6.2: name_index (u2)\n                    dis.readUnsignedShort();\n                    break;\n                }\n                case TAG_PACKAGE: {\n                    // JCVM 5.6.1: flags (u1), name_index (u2),\n                    //   minor_version (u1), major_version (u1), aid_length (u1), aid[aid_length]\n                    int flags = dis.readUnsignedByte();\n                    int nameIndex = dis.readUnsignedShort();\n                    int minor = dis.readUnsignedByte();\n                    int major = dis.readUnsignedByte();\n                    int aidLen = dis.readUnsignedByte();\n                    byte[] aid = new byte[aidLen];\n                    dis.readFully(aid);\n                    pkgEntries.put(i, new int[]{flags, nameIndex, minor, major});\n                    pkgAids.put(i, aid);\n                    break;\n                }\n                default:\n                    throw new IllegalArgumentException(String.format(\"Unknown constant pool tag: %d at index %d\", tag, i));\n            }\n        }\n\n        // JCVM 5.5: this_package (u2) - index into constant pool identifying the exported package\n        int thisPackage = dis.readUnsignedShort();\n\n        if (!pkgEntries.containsKey(thisPackage)) {\n            throw new IllegalArgumentException(String.format(\"this_package index %d does not point to a CONSTANT_Package\", thisPackage));\n        }\n\n        int[] pkg = pkgEntries.get(thisPackage);\n        int pkgFlags = pkg[0];\n        int pkgNameIndex = pkg[1];\n        int pkgMinor = pkg[2];\n        int pkgMajor = pkg[3];\n        byte[] pkgAid = pkgAids.get(thisPackage);\n\n        if (pkgNameIndex >= cpCount || !(pool[pkgNameIndex] instanceof String)) {\n            throw new IllegalArgumentException(\"Invalid package name index: \" + pkgNameIndex);\n        }\n\n        // JCVM 5.6.1: name_index -> CONSTANT_Utf8 with fully qualified package name using '/'\n        String name = ((String) pool[pkgNameIndex]).replace('/', '.');\n\n        // JCVM 5.6.1, Table 5-2: \"If bit 0 of the flags item is set, this package is a library\"\n        boolean library = (pkgFlags & 0x01) != 0;\n\n        return new PackageInfo(version, name, pkgAid, pkgMajor, pkgMinor, library);\n    }\n\n    // JCVM 5.5: \"major version has the value 2\", minor 1=v2.1, 2=v2.2, 3=v2.3\n    private static ExportFileVersion parseFileVersion(int minor) {\n        switch (minor) {\n            case 1:\n                return ExportFileVersion.V21;\n            case 2:\n                return ExportFileVersion.V22;\n            case 3:\n                return ExportFileVersion.V23;\n            default:\n                throw new IllegalArgumentException(\"Invalid export file minor version: \" + minor);\n        }\n    }\n}\n"
  },
  {
    "path": "capfile/src/main/java/pro/javacard/sdk/JavaCardSDK.java",
    "content": "// SPDX-FileCopyrightText: 2015 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.sdk;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.security.PrivilegedAction;\nimport java.util.*;\nimport java.util.stream.Collectors;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\npublic final class JavaCardSDK {\n\n    public static Optional<JavaCardSDK> detectSDK(Path path) {\n        if (path == null) {\n            throw new NullPointerException(\"path is null\");\n        }\n\n        // Detect\n        SDKVersion version = detectSDKVersion(path);\n\n        if (version == null) {\n            return Optional.empty();\n        }\n\n        Path exportDir = getExportDir(version);\n        List<Path> apiJars = getApiJars(version);\n        List<Path> compilerJars = getCompilerJars(version);\n        List<Path> toolJars = getToolJars(version);\n\n        JavaCardSDK sdk = new JavaCardSDK(path, version, exportDir, apiJars, toolJars, compilerJars);\n        return Optional.of(sdk);\n    }\n\n    private static SDKVersion detectSDKVersion(Path root) {\n        SDKVersion version = null;\n        Path libDir = root.resolve(\"lib\");\n        Path tools = libDir.resolve(\"tools.jar\");\n        if (Files.exists(tools)) {\n            try (ZipFile apiZip = new ZipFile(tools.toFile())) {\n                ZipEntry toolsver = apiZip.getEntry(\"com/sun/javacard/toolsversion.properties\");\n                if (toolsver != null) {\n                    Properties verprop = new Properties();\n                    verprop.load(apiZip.getInputStream(toolsver));\n                    String ver = verprop.getProperty(\"converter.version\");\n                    switch (ver) {\n                        case \"3.0.3\":\n                            return SDKVersion.V301; // XXX\n                        case \"3.0.4\":\n                            return SDKVersion.V304;\n                        case \"3.0.5\":\n                            return SDKVersion.V305;\n                        case \"3.1.0\":\n                            return SDKVersion.V310;\n                        case \"3.2.0\":\n                            return SDKVersion.V320; // 24.0\n                        case \"24.1\":\n                            return SDKVersion.V320_24_1;\n                        case \"25.0\":\n                            return SDKVersion.V320_25_0;\n                        case \"25.1\":\n                            return SDKVersion.V320_25_1;\n                        default:\n                            throw new IllegalStateException(\"Unknown SDK release: \" + ver);\n                    }\n                }\n            } catch (IOException e) {\n                throw new RuntimeException(e);\n            }\n        } else if (Files.exists(libDir.resolve(\"api21.jar\"))) {\n            version = SDKVersion.V212;\n        } else if (Files.exists(root.resolve(\"bin\").resolve(\"api.jar\"))) {\n            version = SDKVersion.V211;\n        } else if (Files.exists(libDir.resolve(\"converter.jar\"))) {\n            // assume 2.2.1 first\n            version = SDKVersion.V221;\n            // test for 2.2.2 by testing api.jar\n            Path api = libDir.resolve(\"api.jar\");\n            try (ZipFile apiZip = new ZipFile(api.toFile())) {\n                ZipEntry testEntry = apiZip.getEntry(\"javacardx/apdu/ExtendedLength.class\");\n                if (testEntry != null) {\n                    version = SDKVersion.V222;\n                }\n            } catch (IOException e) {\n                throw new RuntimeException(e);\n            }\n        }\n        return version;\n    }\n\n    private final Path path;\n    private final SDKVersion version;\n\n    private final Path exportDir;\n    private final List<Path> apiJars;\n    private final List<Path> toolJars;\n    private final List<Path> compilerJars;\n\n    private JavaCardSDK(Path root, SDKVersion version, Path exportDir, List<Path> apiJars, List<Path> toolJars, List<Path> compilerJars) {\n        this.path = root;\n        this.version = version;\n\n        this.exportDir = path.resolve(exportDir);\n        this.apiJars = apiJars.stream().map(path::resolve).collect(Collectors.toList());\n        this.compilerJars = compilerJars.stream().map(path::resolve).collect(Collectors.toList());\n        this.toolJars = toolJars.stream().map(path::resolve).collect(Collectors.toList());\n    }\n\n    public Path getRoot() {\n        return path;\n    }\n\n    public SDKVersion getVersion() {\n        return version;\n    }\n\n    public List<Path> getApiJars() {\n        return Collections.unmodifiableList(apiJars);\n    }\n\n    public List<Path> getCompilerJars() {\n        return Collections.unmodifiableList(compilerJars);\n    }\n\n    public List<Path> getToolJars() {\n        return Collections.unmodifiableList(toolJars);\n    }\n\n    public Path getExportDir() {\n        return exportDir;\n    }\n\n    // This is for build and verification tools\n    public JavaCardSDK target(SDKVersion targetVersion) {\n        if (version.targets.contains(targetVersion)) {\n            List<Path> apiJars = new ArrayList<>();\n            apiJars.add(Paths.get(\"lib\", String.format(\"api_classic-%s.jar\", targetVersion.v)));\n            apiJars.add(Paths.get(\"lib\", String.format(\"api_classic_annotations-%s.jar\", targetVersion.v)));\n            Path exportPath = Paths.get(String.format(\"api_export_files_%s\", targetVersion.v));\n            return new JavaCardSDK(path, targetVersion, exportPath, apiJars, toolJars, compilerJars);\n        } else {\n            throw new IllegalStateException(String.format(\"Can not target %s with %s\", targetVersion, version));\n        }\n    }\n\n    // Returns the classloader of verifier\n    @SuppressWarnings(\"removal\") // AccessController\n    public ClassLoader getClassLoader() {\n        return java.security.AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>() {\n            public URLClassLoader run() {\n                try {\n                    if (version.equalOrNewer(SDKVersion.V301)) {\n                        return new URLClassLoader(new URL[]{path.resolve(\"lib\").resolve(\"tools.jar\").toUri().toURL()}, this.getClass().getClassLoader());\n                    } else {\n                        return new URLClassLoader(new URL[]{path.resolve(\"lib\").resolve(\"offcardverifier.jar\").toUri().toURL()}, this.getClass().getClassLoader());\n                    }\n                } catch (MalformedURLException e) {\n                    throw new RuntimeException(\"Could not load classes: \" + e.getMessage());\n                }\n            }\n        });\n    }\n\n    public String getRelease() {\n        if (version == SDKVersion.V305) {\n            try {\n                // Get verifier class\n                Class<?> verifier = Class.forName(\"com.sun.javacard.offcardverifier.Verifier\", false, getClassLoader());\n\n                // Check if 3.0.5u3 (or, hopefully, later)\n                try {\n                    verifier.getDeclaredMethod(\"verifyTargetPlatform\", String.class);\n                    return \"3.0.5u3\";\n                } catch (NoSuchMethodException e) {\n                    // Do nothing\n                }\n\n                // Check if 3.0.5u1\n                try {\n                    verifier.getDeclaredMethod(\"verifyCap\", FileInputStream.class, String.class, Vector.class);\n                    return \"3.0.5u1\";\n                } catch (NoSuchMethodException e) {\n                    // Do nothing\n                }\n                // Assume 3.0.5u2 otherwise\n                return \"3.0.5u2\";\n            } catch (ReflectiveOperationException e) {\n                throw new RuntimeException(\"Could not figure out SDK release: \" + e.getMessage());\n            }\n        } else {\n            // No updates with older SDK-s\n            return version.toString();\n        }\n    }\n\n    // All export dir paths an SDK provides: its own + one per target version\n    public static List<Path> getAllExportDirs(SDKVersion version) {\n        ArrayList<Path> dirs = new ArrayList<Path>();\n        dirs.add(getExportDir(version));\n        for (SDKVersion target : version.targets) {\n            dirs.add(Paths.get(\"api_export_files_\" + target.v));\n        }\n        return dirs;\n    }\n\n    public static Path getExportDir(SDKVersion version) {\n        switch (version) {\n            case V212:\n                return Paths.get(\"api21_export_files\");\n            case V310:\n            case V320:\n            case V320_24_1:\n            case V320_25_0:\n            case V320_25_1:\n                return Paths.get(\"api_export_files_\" + version.v);\n            default:\n                return Paths.get(\"api_export_files\");\n        }\n    }\n\n    public static List<Path> getApiJars(SDKVersion version) {\n        List<Path> jars = new ArrayList<>();\n        switch (version) {\n            case V211:\n                jars.add(Paths.get(\"bin\", \"api.jar\"));\n                break;\n            case V212:\n                jars.add(Paths.get(\"lib\", \"api21.jar\"));\n                break;\n            case V221:\n            case V222:\n                jars.add(Paths.get(\"lib\", \"api.jar\"));\n                break;\n            case V301:\n            case V304:\n            case V305:\n                jars.add(Paths.get(\"lib\", \"api_classic.jar\"));\n                break;\n            case V310:\n            case V320:\n            case V320_24_1:\n            case V320_25_0:\n            case V320_25_1:\n                jars.add(Paths.get(\"lib\", String.format(\"api_classic-%s.jar\", version.v)));\n                jars.add(Paths.get(\"lib\", String.format(\"api_classic_annotations-%s.jar\", version.v)));\n                break;\n            default:\n                throw new IllegalStateException(\"Unknown SDK: \" + version);\n        }\n        // Add annotations for 3.0.4 and 3.0.5\n        if (version.isOneOf(SDKVersion.V304, SDKVersion.V305)) {\n            jars.add(Paths.get(\"lib\", \"api_classic_annotations.jar\"));\n        }\n        return jars;\n    }\n\n    public static List<Path> getToolJars(SDKVersion version) {\n        List<Path> jars = new ArrayList<>();\n        if (version.isOneOf(SDKVersion.V211)) {\n            // We don't support verification with 2.1.X, so only converter\n            jars.add(Paths.get(\"bin\", \"converter.jar\"));\n        } else if (version.equalOrNewer(SDKVersion.V301)) {\n            jars.add(Paths.get(\"lib\", \"tools.jar\"));\n        } else {\n            jars.add(Paths.get(\"lib\", \"converter.jar\"));\n            jars.add(Paths.get(\"lib\", \"offcardverifier.jar\"));\n        }\n        return jars;\n    }\n\n    public static List<Path> getCompilerJars(SDKVersion version) {\n        List<Path> jars = new ArrayList<>();\n        if (version.isOneOf(SDKVersion.V304, SDKVersion.V305)) {\n            jars.add(Paths.get(\"lib\", \"tools.jar\"));\n            jars.add(Paths.get(\"lib\", \"api_classic_annotations.jar\"));\n        } else if (version.equalOrNewer(SDKVersion.V310)) {\n            jars.add(Paths.get(\"lib\", \"tools.jar\"));\n            jars.add(Paths.get(\"lib\", String.format(\"api_classic_annotations-%s.jar\", version.v)));\n        }\n        return jars;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (o instanceof JavaCardSDK) {\n            JavaCardSDK other = (JavaCardSDK) o;\n            return path.toAbsolutePath().equals(other.path.toAbsolutePath()) && version.equals(other.version) && exportDir.equals(other.exportDir);\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(path, exportDir);\n    }\n}\n"
  },
  {
    "path": "capfile/src/main/java/pro/javacard/sdk/OffCardVerifier.java",
    "content": "// SPDX-FileCopyrightText: 2018 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.sdk;\n\nimport pro.javacard.capfile.CAPFile;\n\nimport java.io.*;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.nio.file.Files;\nimport java.nio.file.NoSuchFileException;\nimport java.nio.file.Path;\nimport java.util.*;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\nimport static pro.javacard.sdk.SDKVersion.*;\n\npublic final class OffCardVerifier {\n    private final JavaCardSDK sdk;\n\n    public static OffCardVerifier withSDK(JavaCardSDK sdk) {\n        // Only main method in 2.1 SDK\n        if (sdk.getVersion().isOneOf(V211, V212)) {\n            throw new RuntimeException(\"Verification is supported with JavaCard SDK 2.2.1 or later\");\n        }\n        return new OffCardVerifier(sdk);\n    }\n\n    private OffCardVerifier(JavaCardSDK sdk) {\n        this.sdk = sdk;\n    }\n\n    // Verify a CAP file against a specific JavaCard target SDK and a set of EXP files\n    public void verifyAgainst(File f, JavaCardSDK target, Vector<File> exps) throws VerifierError, IOException {\n        List<Path> exports = new ArrayList<>(exps.stream().map(File::toPath).collect(Collectors.toList()));\n        exports.add(target.getExportDir());\n        verify(f.toPath(), exports);\n    }\n\n    public void verifyAgainst(Path f, JavaCardSDK target, List<Path> exps) throws VerifierError, IOException {\n        // Warn about recommended usage\n        if (target.getVersion().isOneOf(V304, V305, V310) && sdk.getVersion() != V320_25_0) {\n            System.err.println(\"NB! Please use JavaCard SDK 3.2.0 / 25.0 for verifying!\");\n        } else {\n            if (!sdk.getRelease().equals(\"3.0.5u4\")) {\n                System.err.println(\"NB! Please use JavaCard SDK 3.0.5u4 or later for verifying!\");\n            }\n        }\n        List<Path> exports = new ArrayList<>(exps.stream().collect(Collectors.toList()));\n        exports.add(target.getExportDir());\n        verify(f, exports);\n    }\n\n    // Verify a given CAP file against a set of EXP files\n    public void verify(Path f, List<Path> exps) throws VerifierError, IOException {\n        Path tmp = Files.createTempDirectory(\"capfile\");\n        try (InputStream in = Files.newInputStream(f)) {\n            CAPFile cap = CAPFile.fromStream(in);\n\n            // Get verifier class\n            Class<?> verifier = Class.forName(\"com.sun.javacard.offcardverifier.Verifier\", true, sdk.getClassLoader());\n\n            // Verifier takes a vector of files, so collect\n            final Vector<File> expfiles = new Vector<>();\n            for (Path e : exps) {\n                // collect all export files to a list\n                if (Files.isDirectory(e)) {\n                    expfiles.addAll(Files.walk(e.toRealPath()).filter(p -> p.toString().endsWith(\".exp\")).map(Path::toFile).collect(Collectors.toList()));\n                } else if (Files.isReadable(e)) {\n                    if (e.toString().endsWith(\".exp\")) {\n                        expfiles.add(e.toFile());\n                    } else if (e.toString().endsWith(\".jar\")) {\n                        expfiles.addAll(extractExps(e, tmp).stream().map(Path::toFile).collect(Collectors.toList()));\n                    }\n                }\n            }\n\n            String packagename = cap.getPackageName();\n            // XXX: calling this on SDK 25.0 would set the level from INFO to ALL, so manually revert it in finally\n            Level logger_before = Logger.getLogger(\"\").getLevel();\n            try (FileInputStream input = new FileInputStream(f.toFile())) {\n                // 3.0.5u1 still uses old signature\n                if (sdk.getRelease().equals(\"3.0.5u3\") || sdk.getRelease().equals(\"3.0.5u2\") || sdk.getVersion().equalOrNewer(V310)) {\n                    Method m = verifier.getMethod(\"verifyCap\", File.class, String.class, Vector.class);\n                    m.invoke(null, f.toFile(), packagename, expfiles);\n                } else {\n                    Method m = verifier.getMethod(\"verifyCap\", FileInputStream.class, String.class, Vector.class);\n                    m.invoke(null, input, packagename, expfiles);\n                }\n            } catch (InvocationTargetException e) {\n                throw new VerifierError(e.getTargetException().getMessage(), e.getTargetException());\n            } catch (Exception e) {\n                throw new VerifierError(\"Verification failed: \" + e.getMessage(), e);\n            } finally {\n                Level logger_now = Logger.getLogger(\"\").getLevel();\n                if (!logger_before.equals(logger_now)) {\n                    System.err.println(String.format(\"Resetting root logger from %s back to %s\", logger_now, logger_before));\n                    Logger.getLogger(\"\").setLevel(logger_before);\n                }\n            }\n        } catch (ReflectiveOperationException | IOException e) {\n            throw new RuntimeException(\"Could not run verifier: \" + e.getMessage(), e);\n        } finally {\n            // Clean extracted exps\n            rmminusrf(tmp);\n        }\n    }\n\n    private static void rmminusrf(Path path) {\n        try {\n            Files.walk(path).sorted(Comparator.reverseOrder()).forEach(CAPFile::uncheckedDelete);\n        } catch (FileNotFoundException | NoSuchFileException e) {\n            // Already gone - do nothing.\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private static Path under(Path out, String name) {\n        Path p = out.resolve(name).normalize().toAbsolutePath();\n        if (!p.startsWith(out)) {\n            throw new IllegalArgumentException(String.format(\"Invalid path in JAR: %s vs %s\", p, out));\n        }\n        return p;\n    }\n\n    // Extracts .exp files from a jarfile to given path (temp folder) and returns the list of .exp files there\n    public static List<Path> extractExps(Path jarfilePath, Path out) throws IOException {\n        List<Path> exps = new ArrayList<>();\n        try (JarFile jarfile = new JarFile(jarfilePath.toFile())) {\n            Enumeration<JarEntry> entries = jarfile.entries();\n            while (entries.hasMoreElements()) {\n                JarEntry entry = entries.nextElement();\n                if (entry.getName().toLowerCase().endsWith(\".exp\")) {\n                    Path f = under(out, entry.getName());\n                    Path dir = f.getParent();\n                    if (dir == null) {\n                        throw new IOException(\"Null parent\"); // spotbugs\n                    }\n                    if (!Files.isDirectory(dir)) {\n                        Files.createDirectories(dir);\n                        //      throw new IOException(\"Failed to create folder: \" + f.getParentFile());\n                        // f = under(out, entry.getName());\n                    }\n                    try (InputStream is = jarfile.getInputStream(entry);\n                         OutputStream fo = Files.newOutputStream(f)) {\n                        byte[] buf = new byte[1024];\n                        while (true) {\n                            int r = is.read(buf);\n                            if (r == -1) {\n                                break;\n                            }\n                            fo.write(buf, 0, r);\n                        }\n                    }\n                    exps.add(f);\n                }\n            }\n        }\n        return exps;\n    }\n}\n"
  },
  {
    "path": "capfile/src/main/java/pro/javacard/sdk/SDKVersion.java",
    "content": "// SPDX-FileCopyrightText: 2022 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.sdk;\n\nimport java.util.*;\n\npublic enum SDKVersion {\n    V211(\"2.1.1\", \"1.1\", null, null),\n    V212(\"2.1.2\", \"1.1\", null, null),\n    V221(\"2.2.1\", \"1.2\", null, null),\n    V222(\"2.2.2\", \"1.5\", null, null),\n    V301(\"3.0.1\", \"1.6\", null, Arrays.asList(8, 11)),\n    V304(\"3.0.4\", \"1.6\", null, Arrays.asList(8, 11)),\n    V305(\"3.0.5\", \"1.6\", null, Arrays.asList(8, 11)),\n    // NOTE: can't use EnumSet \"recursively\", thus turn the List into normal HashSet in constructor\n    V310(\"3.1.0\", \"1.7\", Arrays.asList(V304, V305), Arrays.asList(8, 11, 17)),\n    V320(\"3.2.0\", \"1.7\", Arrays.asList(V304, V305, V310), Arrays.asList(8, 11, 17)),\n    V320_24_1(\"3.2.0\", \"1.7\", Arrays.asList(V304, V305, V310, V320), Arrays.asList(11, 17)),\n    V320_25_0(\"3.2.0\", \"1.8\", Arrays.asList(V304, V305, V310, V320), Arrays.asList(8, 11, 17, 21)),\n    V320_25_1(\"3.2.0\", \"1.8\", Arrays.asList(V304, V305, V310, V320), Arrays.asList(8, 11, 17, 21));\n\n\n\n    final String v;\n    final String class_file_target;  // This indicates the highest class file version edible by SDK-s converter\n    final Set<Integer> jdks;\n    final Set<SDKVersion> targets;\n\n    SDKVersion(String v, String classfile, Collection<SDKVersion> targets, List<Integer> jdks) {\n        this.v = v;\n        this.class_file_target = classfile;\n        this.targets = targets == null ? new HashSet<>() : new HashSet<>(targets);\n        this.jdks = new HashSet<>(jdks == null ? Arrays.asList(8) : jdks);\n    }\n\n    @Override\n    public String toString() {\n        return this.v;\n    }\n\n    public Set<SDKVersion> targets() {\n        return Collections.unmodifiableSet(this.targets);\n    }\n\n    public boolean isOneOf(SDKVersion... versions) {\n        for (SDKVersion v : versions) {\n            if (this.equals(v)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public String javaVersion() {\n        return class_file_target;\n    }\n\n    public Set<Integer> jdkVersions() {\n        return Collections.unmodifiableSet(jdks);\n    }\n\n    public static Optional<SDKVersion> fromVersion(String versionString) {\n        return Arrays.stream(values()).filter(ver -> ver.v.equals(versionString)).findFirst();\n    }\n\n    public boolean equalOrNewer(SDKVersion other) {\n        return this.ordinal() >= other.ordinal();\n    }\n}\n"
  },
  {
    "path": "capfile/src/main/java/pro/javacard/sdk/VerifierError.java",
    "content": "// SPDX-FileCopyrightText: 2018 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.sdk;\n\npublic class VerifierError extends Exception {\n    private static final long serialVersionUID = 9099882918121440945L;\n\n    public VerifierError(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public VerifierError(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "capfile/src/main/resources/pro/javacard/capfile/aid_list.properties",
    "content": "# from https://www.commoncriteriaportal.org/files/epfiles/anssi-cc-2018_32fr.pdf\nA0000000308000000008DB00FF=com.gemalto.javacard.eid\nA0000000180F000001833032=com.gemalto.mchipadv\nA00000001830030100000000000000FF=com.gemalto.mpcos\nA0000000308000000008F500FF=com.gemalto.javacard.esign\n4D4F43415F536572766572=com.gemalto.moc.server\nA00000001830070100000000000001FF=com.gemalto.dualPSE\nA0000000308000000006DF00FF=com.gemalto.javacard.mspnp\nA000000018320A0100000000000000FF=com.gemalto.pure\nA00000000310=com.visa.vsdc\nA000000018300B0200000000000000FF=eTravel (Virtual Pkg)\nA00000001880000000066240FF=com.gemalto.javacard.iasclassic\n# from https://www.commoncriteriaportal.org/files/epfiles/ANSSI-CC-2016-23.pdf\n4D4F43415F436C69656E74=com.gemalto.moc.applet\n# based on https://github.com/tsenger/CCU2F/blob/master/CCU2F/cap/ccu2f.cap\nD276000085494A434F5058=com.nxp.id.jcopx\nA0000003964D344D30=org.mifare4mobile.hostinterface\n"
  },
  {
    "path": "capfile/src/test/java/pro/javacard/capfile/TestWellKnownAID.java",
    "content": "// SPDX-FileCopyrightText: 2024 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.capfile;\n\nimport org.testng.Assert;\nimport org.testng.annotations.Test;\n\nimport java.io.InputStream;\nimport java.util.Optional;\n\npublic class TestWellKnownAID {\n\n    @Test\n    public void testInternalList() throws Exception {\n        try (InputStream in = WellKnownAID.class.getResourceAsStream(\"aid_list.properties\")) {\n            WellKnownAID.load(in);\n            Assert.assertEquals(WellKnownAID.getName(AID.fromString(\"D276000085494A434F5058\")), Optional.of(\"com.nxp.id.jcopx\"));\n        }\n    }\n}\n"
  },
  {
    "path": "capfile/src/test/java/pro/javacard/sdk/TestExportFiles.java",
    "content": "// SPDX-FileCopyrightText: 2024 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.sdk;\n\nimport org.testng.Assert;\nimport org.testng.annotations.Test;\nimport pro.javacard.capfile.HexUtils;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\npublic class TestExportFiles {\n\n    static Path sdksRoot() {\n        return Paths.get(System.getProperty(\"user.dir\")).getParent().resolve(\"sdks\");\n    }\n\n    @Test\n    public void testParseAllExportFiles() throws Exception {\n        int total = 0;\n        int failures = 0;\n\n        try (Stream<Path> dirs = Files.list(sdksRoot())) {\n            List<Path> sdkDirs = dirs.filter(Files::isDirectory).sorted().collect(Collectors.toList());\n            for (Path dir : sdkDirs) {\n                Optional<JavaCardSDK> sdk = JavaCardSDK.detectSDK(dir);\n                Assert.assertTrue(sdk.isPresent(), \"Failed to detect SDK in \" + dir);\n\n                List<Path> exportDirs = JavaCardSDK.getAllExportDirs(sdk.get().getVersion());\n                ArrayList<String> jarPrefixes = new ArrayList<String>();\n\n                for (Path exportDir : exportDirs) {\n                    Path fsDir = dir.resolve(exportDir);\n                    if (Files.isDirectory(fsDir)) {\n                        try (Stream<Path> walk = Files.walk(fsDir)) {\n                            List<Path> expFiles = walk.filter(p -> p.toString().endsWith(\".exp\"))\n                                    .filter(Files::isRegularFile)\n                                    .sorted()\n                                    .collect(Collectors.toList());\n                            for (Path exp : expFiles) {\n                                total++;\n                                try {\n                                    ExportFileHelper.PackageInfo pkg = ExportFileHelper.parsePackage(exp);\n                                    System.out.println(exp);\n                                    System.out.println(\"  \" + pkg);\n                                } catch (Exception e) {\n                                    System.err.println(String.format(\"%s: FAILED - %s\", exp, e.getMessage()));\n                                    failures++;\n                                }\n                            }\n                        }\n                    } else {\n                        jarPrefixes.add(exportDir.toString() + \"/\");\n                    }\n                }\n\n                if (!jarPrefixes.isEmpty()) {\n                    Path toolsJar = dir.resolve(\"lib\").resolve(\"tools.jar\");\n                    if (Files.exists(toolsJar)) {\n                        try (ZipFile zf = new ZipFile(toolsJar.toFile())) {\n                            Enumeration<? extends ZipEntry> entries = zf.entries();\n                            while (entries.hasMoreElements()) {\n                                ZipEntry entry = entries.nextElement();\n                                if (!entry.getName().endsWith(\".exp\")) {\n                                    continue;\n                                }\n                                if (jarPrefixes.stream().noneMatch(entry.getName()::startsWith)) {\n                                    continue;\n                                }\n                                total++;\n                                try {\n                                    ExportFileHelper.PackageInfo pkg = ExportFileHelper.parsePackage(zf.getInputStream(entry));\n                                    System.out.println(String.format(\"%s!%s\", toolsJar, entry.getName()));\n                                    System.out.println(\"  \" + pkg);\n                                } catch (Exception e) {\n                                    System.err.println(String.format(\"%s!%s: FAILED - %s\", toolsJar, entry.getName(), e.getMessage()));\n                                    failures++;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        Assert.assertTrue(total > 100, String.format(\"Expected at least 100 .exp files, found %d\", total));\n        Assert.assertEquals(failures, 0, String.format(\"%d export files failed to parse\", failures));\n\n        // Pinned assertions for specific files\n        ExportFileHelper.PackageInfo v21 = ExportFileHelper.parsePackage(\n                sdksRoot().resolve(\"jc211_kit/api_export_files/javacard/framework/javacard/framework.exp\"));\n        Assert.assertEquals(v21.getVersion(), ExportFileHelper.ExportFileVersion.V21);\n        Assert.assertEquals(v21.getName(), \"javacard.framework\");\n        Assert.assertEquals(v21.getAid(), HexUtils.hex2bin(\"A0000000620101\"));\n        Assert.assertEquals(v21.getMajor(), 1);\n        Assert.assertEquals(v21.getMinor(), 0);\n        Assert.assertEquals(v21.getPackageVersion(), \"1.0\");\n        Assert.assertTrue(v21.isLibrary());\n\n        ExportFileHelper.PackageInfo v22 = ExportFileHelper.parsePackage(\n                sdksRoot().resolve(\"jc221_kit/api_export_files/javacard/framework/service/javacard/service.exp\"));\n        Assert.assertEquals(v22.getVersion(), ExportFileHelper.ExportFileVersion.V22);\n        Assert.assertEquals(v22.getName(), \"javacard.framework.service\");\n        Assert.assertEquals(v22.getAid(), HexUtils.hex2bin(\"A000000062010101\"));\n\n        ExportFileHelper.PackageInfo v23 = ExportFileHelper.parsePackage(\n                sdksRoot().resolve(\"jc310b43_kit/api_export_files_3.0.4/java/lang/javacard/lang.exp\"));\n        Assert.assertEquals(v23.getVersion(), ExportFileHelper.ExportFileVersion.V23);\n        Assert.assertEquals(v23.getName(), \"java.lang\");\n        Assert.assertEquals(v23.getAid(), HexUtils.hex2bin(\"A0000000620001\"));\n\n        // Same package across export file versions - same identity, different format\n        Assert.assertEquals(v21.getName(), \"javacard.framework\");\n        Assert.assertEquals(v23.getName(), \"java.lang\");\n\n        // V23 export files with multiple CONSTANT_Package entries (imports + this_package)\n        ExportFileHelper.PackageInfo v23fw = ExportFileHelper.parsePackage(\n                sdksRoot().resolve(\"jc310b43_kit/api_export_files_3.1.0/javacard/framework/javacard/framework.exp\"));\n        Assert.assertEquals(v23fw.getVersion(), ExportFileHelper.ExportFileVersion.V23);\n        Assert.assertEquals(v23fw.getName(), \"javacard.framework\");\n        Assert.assertEquals(v23fw.getAid(), HexUtils.hex2bin(\"A0000000620101\"));\n        Assert.assertEquals(v23fw.getMajor(), 1);\n        Assert.assertEquals(v23fw.getMinor(), 8);\n\n        ExportFileHelper.PackageInfo v23sec = ExportFileHelper.parsePackage(\n                sdksRoot().resolve(\"jc310b43_kit/api_export_files_3.1.0/javacard/security/javacard/security.exp\"));\n        Assert.assertEquals(v23sec.getVersion(), ExportFileHelper.ExportFileVersion.V23);\n        Assert.assertEquals(v23sec.getName(), \"javacard.security\");\n        Assert.assertEquals(v23sec.getAid(), HexUtils.hex2bin(\"A0000000620102\"));\n        Assert.assertEquals(v23sec.getMajor(), 1);\n        Assert.assertEquals(v23sec.getMinor(), 7);\n\n        // Non-library package\n        ExportFileHelper.PackageInfo nonLib = ExportFileHelper.parsePackage(\n                sdksRoot().resolve(\"jc304_kit/classic_simulator/api_export_files/com/sun/javacard/installer/javacard/installer.exp\"));\n        Assert.assertEquals(nonLib.getName(), \"com.sun.javacard.installer\");\n        Assert.assertEquals(nonLib.getAid(), HexUtils.hex2bin(\"A000000062030108\"));\n        Assert.assertFalse(nonLib.isLibrary());\n    }\n\n    @Test(expectedExceptions = IllegalArgumentException.class)\n    public void testBadMagic() throws Exception {\n        ExportFileHelper.parsePackage(new ByteArrayInputStream(HexUtils.hex2bin(\"000000000102\")));\n    }\n\n    @Test(expectedExceptions = IllegalArgumentException.class)\n    public void testBadMajorVersion() throws Exception {\n        ExportFileHelper.parsePackage(new ByteArrayInputStream(HexUtils.hex2bin(\"00FACADE0103\")));\n    }\n\n    @Test(expectedExceptions = IllegalArgumentException.class)\n    public void testBadMinorVersion() throws Exception {\n        ExportFileHelper.parsePackage(new ByteArrayInputStream(HexUtils.hex2bin(\"00FACADE0402\")));\n    }\n}\n"
  },
  {
    "path": "capfile/src/test/java/pro/javacard/sdk/TestSDKs.java",
    "content": "// SPDX-FileCopyrightText: 2024 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.sdk;\n\nimport org.testng.Assert;\nimport org.testng.annotations.Test;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Optional;\nimport java.util.stream.Stream;\n\npublic class TestSDKs {\n\n    static Path sdksRoot() {\n        return Paths.get(System.getProperty(\"user.dir\")).getParent().resolve(\"sdks\");\n    }\n\n    static JavaCardSDK sdk(String name) {\n        return JavaCardSDK.detectSDK(sdksRoot().resolve(name)).orElseThrow(() -> new AssertionError(\"SDK not found: \" + name));\n    }\n\n    @Test\n    public void testDetection() throws Exception {\n        try (Stream<Path> dirs = Files.list(sdksRoot())) {\n            dirs.forEach(dir -> {\n                if (Files.isDirectory(dir)) {\n                    System.out.println(String.format(\"%s: %s\", dir, JavaCardSDK.detectSDK(dir).map(JavaCardSDK::getRelease).orElse(\"not SDK\")));\n                    Assert.assertTrue(JavaCardSDK.detectSDK(dir).isPresent(), \"Failed to detect SDK in \" + dir);\n                }\n            });\n        }\n\n        // Pinned version and release assertions\n        Assert.assertEquals(sdk(\"jc211_kit\").getVersion(), SDKVersion.V211);\n        Assert.assertEquals(sdk(\"jc211_kit\").getRelease(), \"2.1.1\");\n        Assert.assertEquals(sdk(\"jc212_kit\").getVersion(), SDKVersion.V212);\n        Assert.assertEquals(sdk(\"jc221_kit\").getVersion(), SDKVersion.V221);\n        Assert.assertEquals(sdk(\"jc222_kit\").getVersion(), SDKVersion.V222);\n        Assert.assertEquals(sdk(\"jc303_kit\").getVersion(), SDKVersion.V301);\n        Assert.assertEquals(sdk(\"jc304_kit\").getVersion(), SDKVersion.V304);\n        Assert.assertEquals(sdk(\"jc305u1_kit\").getRelease(), \"3.0.5u1\");\n        Assert.assertEquals(sdk(\"jc305u2_kit\").getRelease(), \"3.0.5u2\");\n        Assert.assertEquals(sdk(\"jc305u3_kit\").getRelease(), \"3.0.5u3\");\n        Assert.assertEquals(sdk(\"jc305u4_kit\").getRelease(), \"3.0.5u3\"); // u4 indistinguishable from u3\n        Assert.assertEquals(sdk(\"jc310b43_kit\").getVersion(), SDKVersion.V310);\n        Assert.assertEquals(sdk(\"jc320v25.1_kit\").getVersion(), SDKVersion.V320_25_1);\n\n        // Java class file target versions\n        Assert.assertEquals(SDKVersion.V211.javaVersion(), \"1.1\");\n        Assert.assertEquals(SDKVersion.V222.javaVersion(), \"1.5\");\n        Assert.assertEquals(SDKVersion.V301.javaVersion(), \"1.6\");\n        Assert.assertEquals(SDKVersion.V320_25_1.javaVersion(), \"1.8\");\n\n        // Multi-target SDKs\n        Assert.assertTrue(SDKVersion.V310.targets().contains(SDKVersion.V304));\n        Assert.assertTrue(SDKVersion.V310.targets().contains(SDKVersion.V305));\n        Assert.assertTrue(SDKVersion.V320.targets().contains(SDKVersion.V310));\n\n        // JDK support\n        Assert.assertTrue(SDKVersion.V320_25_1.jdkVersions().contains(21));\n\n        // Version ordering and lookup\n        Assert.assertTrue(SDKVersion.V320.equalOrNewer(SDKVersion.V211));\n        Assert.assertFalse(SDKVersion.V211.equalOrNewer(SDKVersion.V222));\n        Assert.assertEquals(SDKVersion.fromVersion(\"3.0.5\"), Optional.of(SDKVersion.V305));\n        Assert.assertFalse(SDKVersion.fromVersion(\"9.9.9\").isPresent());\n    }\n}\n"
  },
  {
    "path": "fails.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project basedir=\".\" name=\"ant-javacard failed tests\">\n    <include file=\"kits.xml\"/>\n\n    <taskdef name=\"javacard\" classname=\"pro.javacard.ant.JavaCard\" classpath=\"ant-javacard.jar\"/>\n\n    <!-- Different SDK/JDK incompatibilities -->\n    <target name=\"latestkit\">\n        <javacard>\n            <cap jckit=\"${JC310}\" sources=\"src/testapplets/integer\" targetsdk=\"${JC222}\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n    <target name=\"newjdk\">\n        <javacard>\n            <cap jckit=\"${JC304}\" sources=\"src/testapplets/integer\" targetsdk=\"${JC222}\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n    <target name=\"oldkit\">\n        <javacard>\n            <cap jckit=\"${JC222}\" sources=\"src/testapplets/integer\" targetsdk=\"${JC221}\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n    <target name=\"noexport\">\n        <javacard>\n            <cap jckit=\"${JC310}\" sources=\"src/testapplets/integer\" targetsdk=\"3.0.4\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- Library -->\n    <target name=\"test-library\">\n        <javacard jckit=\"${JC305}\">\n            <cap targetsdk=\"${JC304}\" sources=\"src/testapplets/library\" package=\"testapplets.library\"\n                 aid=\"01020304050607\" export=\"testlib\" version=\"0.1\"/>\n        </javacard>\n    </target>\n    <target name=\"test-library-user\" depends=\"test-library\">\n        <javacard>\n            <cap jckit=\"${JC310}\" sources=\"src/testapplets/libraryuser\">\n                <applet class=\"testapplets.libraryuser.LibraryUser\" aid=\"0102030405060708\"/>\n                <import jar=\"testlib/library.jar\"/>\n            </cap>\n        </javacard>\n    </target>\n</project>\n"
  },
  {
    "path": "kits.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project basedir=\".\" name=\"kit defs\">\n    <!-- Some handy shorthands -->\n    <property name=\"JC211\" value=\"sdks/jc211_kit\"/>\n    <property name=\"JC212\" value=\"sdks/jc212_kit\"/>\n    <property name=\"JC221\" value=\"sdks/jc221_kit\"/>\n    <property name=\"JC222\" value=\"sdks/jc222_kit\"/>\n    <property name=\"JC303\" value=\"sdks/jc303_kit\"/>\n    <property name=\"JC304\" value=\"sdks/jc304_kit\"/>\n    <property name=\"JC305_1\" value=\"sdks/jc305u1_kit\"/>\n    <property name=\"JC305_2\" value=\"sdks/jc305u2_kit\"/>\n    <property name=\"JC305_3\" value=\"sdks/jc305u3_kit\"/>\n    <property name=\"JC305\" value=\"sdks/jc305u4_kit\"/>\n    <property name=\"JC310\" value=\"sdks/jc310r20210706_kit\"/>\n    <property name=\"JC320\" value=\"sdks/jc320v24.0_kit\"/>\n    <property name=\"JC320_1\" value=\"sdks/jc320v24.1_kit\"/>\n    <property name=\"JC320_2\" value=\"sdks/jc320v25.0_kit\"/>\n    <property name=\"JC320_3\" value=\"sdks/jc320v25.1_kit\"/>\n</project>    "
  },
  {
    "path": "mvnw",
    "content": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n# ----------------------------------------------------------------------------\n\n# ----------------------------------------------------------------------------\n# Apache Maven Wrapper startup batch script, version 3.3.4\n#\n# Required ENV vars:\n# ------------------\n#   JAVA_HOME - location of a JDK home dir\n#\n# Optional ENV vars\n# -----------------\n#   MAVEN_OPTS - parameters passed to the Java VM when running Maven\n#     e.g. to debug Maven itself, use\n#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n# ----------------------------------------------------------------------------\n\nif [ -z \"$MAVEN_SKIP_RC\" ]; then\n\n  if [ -f /usr/local/etc/mavenrc ]; then\n    . /usr/local/etc/mavenrc\n  fi\n\n  if [ -f /etc/mavenrc ]; then\n    . /etc/mavenrc\n  fi\n\n  if [ -f \"$HOME/.mavenrc\" ]; then\n    . \"$HOME/.mavenrc\"\n  fi\n\nfi\n\n# OS specific support.  $var _must_ be set to either true or false.\ncygwin=false\ndarwin=false\nmingw=false\ncase \"$(uname)\" in\nCYGWIN*) cygwin=true ;;\nMINGW*) mingw=true ;;\nDarwin*)\n  darwin=true\n  # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home\n  # See https://developer.apple.com/library/mac/qa/qa1170/_index.html\n  if [ -z \"$JAVA_HOME\" ]; then\n    if [ -x \"/usr/libexec/java_home\" ]; then\n      JAVA_HOME=\"$(/usr/libexec/java_home)\"\n      export JAVA_HOME\n    else\n      JAVA_HOME=\"/Library/Java/Home\"\n      export JAVA_HOME\n    fi\n  fi\n  ;;\nesac\n\nif [ -z \"$JAVA_HOME\" ]; then\n  if [ -r /etc/gentoo-release ]; then\n    JAVA_HOME=$(java-config --jre-home)\n  fi\nfi\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched\nif $cygwin; then\n  [ -n \"$JAVA_HOME\" ] \\\n    && JAVA_HOME=$(cygpath --unix \"$JAVA_HOME\")\n  [ -n \"$CLASSPATH\" ] \\\n    && CLASSPATH=$(cygpath --path --unix \"$CLASSPATH\")\nfi\n\n# For Mingw, ensure paths are in UNIX format before anything is touched\nif $mingw; then\n  [ -n \"$JAVA_HOME\" ] && [ -d \"$JAVA_HOME\" ] \\\n    && JAVA_HOME=\"$(\n      cd \"$JAVA_HOME\" || (\n        echo \"cannot cd into $JAVA_HOME.\" >&2\n        exit 1\n      )\n      pwd\n    )\"\nfi\n\nif [ -z \"$JAVA_HOME\" ]; then\n  javaExecutable=\"$(which javac)\"\n  if [ -n \"$javaExecutable\" ] && ! [ \"$(expr \"$javaExecutable\" : '\\([^ ]*\\)')\" = \"no\" ]; then\n    # readlink(1) is not available as standard on Solaris 10.\n    readLink=$(which readlink)\n    if [ ! \"$(expr \"$readLink\" : '\\([^ ]*\\)')\" = \"no\" ]; then\n      if $darwin; then\n        javaHome=\"$(dirname \"$javaExecutable\")\"\n        javaExecutable=\"$(cd \"$javaHome\" && pwd -P)/javac\"\n      else\n        javaExecutable=\"$(readlink -f \"$javaExecutable\")\"\n      fi\n      javaHome=\"$(dirname \"$javaExecutable\")\"\n      javaHome=$(expr \"$javaHome\" : '\\(.*\\)/bin')\n      JAVA_HOME=\"$javaHome\"\n      export JAVA_HOME\n    fi\n  fi\nfi\n\nif [ -z \"$JAVACMD\" ]; then\n  if [ -n \"$JAVA_HOME\" ]; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ]; then\n      # IBM's JDK on AIX uses strange locations for the executables\n      JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n      JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n  else\n    JAVACMD=\"$(\n      \\unset -f command 2>/dev/null\n      \\command -v java\n    )\"\n  fi\nfi\n\nif [ ! -x \"$JAVACMD\" ]; then\n  echo \"Error: JAVA_HOME is not defined correctly.\" >&2\n  echo \"  We cannot execute $JAVACMD\" >&2\n  exit 1\nfi\n\nif [ -z \"$JAVA_HOME\" ]; then\n  echo \"Warning: JAVA_HOME environment variable is not set.\" >&2\nfi\n\n# traverses directory structure from process work directory to filesystem root\n# first directory with .mvn subdirectory is considered project base directory\nfind_maven_basedir() {\n  if [ -z \"$1\" ]; then\n    echo \"Path not specified to find_maven_basedir\" >&2\n    return 1\n  fi\n\n  basedir=\"$1\"\n  wdir=\"$1\"\n  while [ \"$wdir\" != '/' ]; do\n    if [ -d \"$wdir\"/.mvn ]; then\n      basedir=$wdir\n      break\n    fi\n    # workaround for JBEAP-8937 (on Solaris 10/Sparc)\n    if [ -d \"${wdir}\" ]; then\n      wdir=$(\n        cd \"$wdir/..\" || exit 1\n        pwd\n      )\n    fi\n    # end of workaround\n  done\n  printf '%s' \"$(\n    cd \"$basedir\" || exit 1\n    pwd\n  )\"\n}\n\n# concatenates all lines of a file\nconcat_lines() {\n  if [ -f \"$1\" ]; then\n    # Remove \\r in case we run on Windows within Git Bash\n    # and check out the repository with auto CRLF management\n    # enabled. Otherwise, we may read lines that are delimited with\n    # \\r\\n and produce $'-Xarg\\r' rather than -Xarg due to word\n    # splitting rules.\n    tr -s '\\r\\n' ' ' <\"$1\"\n  fi\n}\n\nlog() {\n  if [ \"$MVNW_VERBOSE\" = true ]; then\n    printf '%s\\n' \"$1\"\n  fi\n}\n\nBASE_DIR=$(find_maven_basedir \"$(dirname \"$0\")\")\nif [ -z \"$BASE_DIR\" ]; then\n  exit 1\nfi\n\nMAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-\"$BASE_DIR\"}\nexport MAVEN_PROJECTBASEDIR\nlog \"$MAVEN_PROJECTBASEDIR\"\n\ntrim() {\n  # MWRAPPER-139:\n  #   Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.\n  #   Needed for removing poorly interpreted newline sequences when running in more\n  #   exotic environments such as mingw bash on Windows.\n  printf \"%s\" \"${1}\" | tr -d '[:space:]'\n}\n\n##########################################################################################\n# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central\n# This allows using the maven wrapper in projects that prohibit checking in binary data.\n##########################################################################################\nwrapperJarPath=\"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar\"\nif [ -r \"$wrapperJarPath\" ]; then\n  log \"Found $wrapperJarPath\"\nelse\n  log \"Couldn't find $wrapperJarPath, downloading it ...\"\n\n  if [ -n \"$MVNW_REPOURL\" ]; then\n    wrapperUrl=\"$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar\"\n  else\n    wrapperUrl=\"https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar\"\n  fi\n  while IFS=\"=\" read -r key value; do\n    case \"$key\" in wrapperUrl)\n      wrapperUrl=$(trim \"${value-}\")\n      break\n      ;;\n    esac\n  done <\"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties\"\n  log \"Downloading from: $wrapperUrl\"\n\n  if $cygwin; then\n    wrapperJarPath=$(cygpath --path --windows \"$wrapperJarPath\")\n  fi\n\n  if command -v wget >/dev/null; then\n    log \"Found wget ... using wget\"\n    [ \"$MVNW_VERBOSE\" = true ] && QUIET=\"\" || QUIET=\"--quiet\"\n    if [ -z \"$MVNW_USERNAME\" ] || [ -z \"$MVNW_PASSWORD\" ]; then\n      wget ${QUIET:+\"$QUIET\"} \"$wrapperUrl\" -O \"$wrapperJarPath\" || rm -f \"$wrapperJarPath\"\n    else\n      wget ${QUIET:+\"$QUIET\"} --http-user=\"$MVNW_USERNAME\" --http-password=\"$MVNW_PASSWORD\" \"$wrapperUrl\" -O \"$wrapperJarPath\" || rm -f \"$wrapperJarPath\"\n    fi\n  elif command -v curl >/dev/null; then\n    log \"Found curl ... using curl\"\n    [ \"$MVNW_VERBOSE\" = true ] && QUIET=\"\" || QUIET=\"--silent\"\n    if [ -z \"$MVNW_USERNAME\" ] || [ -z \"$MVNW_PASSWORD\" ]; then\n      curl ${QUIET:+\"$QUIET\"} -o \"$wrapperJarPath\" \"$wrapperUrl\" -f -L || rm -f \"$wrapperJarPath\"\n    else\n      curl ${QUIET:+\"$QUIET\"} --user \"$MVNW_USERNAME:$MVNW_PASSWORD\" -o \"$wrapperJarPath\" \"$wrapperUrl\" -f -L || rm -f \"$wrapperJarPath\"\n    fi\n  else\n    log \"Falling back to using Java to download\"\n    javaSource=\"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java\"\n    javaClass=\"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class\"\n    # For Cygwin, switch paths to Windows format before running javac\n    if $cygwin; then\n      javaSource=$(cygpath --path --windows \"$javaSource\")\n      javaClass=$(cygpath --path --windows \"$javaClass\")\n    fi\n    if [ -e \"$javaSource\" ]; then\n      if [ ! -e \"$javaClass\" ]; then\n        log \" - Compiling MavenWrapperDownloader.java ...\"\n        (\"$JAVA_HOME/bin/javac\" \"$javaSource\")\n      fi\n      if [ -e \"$javaClass\" ]; then\n        log \" - Running MavenWrapperDownloader.java ...\"\n        (\"$JAVA_HOME/bin/java\" -cp .mvn/wrapper MavenWrapperDownloader \"$wrapperUrl\" \"$wrapperJarPath\") || rm -f \"$wrapperJarPath\"\n      fi\n    fi\n  fi\nfi\n##########################################################################################\n# End of extension\n##########################################################################################\n\n# If specified, validate the SHA-256 sum of the Maven wrapper jar file\nwrapperSha256Sum=\"\"\nwhile IFS=\"=\" read -r key value; do\n  case \"$key\" in wrapperSha256Sum)\n    wrapperSha256Sum=$(trim \"${value-}\")\n    break\n    ;;\n  esac\ndone <\"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties\"\nif [ -n \"$wrapperSha256Sum\" ]; then\n  wrapperSha256Result=false\n  if command -v sha256sum >/dev/null; then\n    if echo \"$wrapperSha256Sum  $wrapperJarPath\" | sha256sum -c - >/dev/null 2>&1; then\n      wrapperSha256Result=true\n    fi\n  elif command -v shasum >/dev/null; then\n    if echo \"$wrapperSha256Sum  $wrapperJarPath\" | shasum -a 256 -c >/dev/null 2>&1; then\n      wrapperSha256Result=true\n    fi\n  else\n    echo \"Checksum validation was requested but neither 'sha256sum' or 'shasum' are available.\" >&2\n    echo \"Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties.\" >&2\n    exit 1\n  fi\n  if [ $wrapperSha256Result = false ]; then\n    echo \"Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.\" >&2\n    echo \"Investigate or delete $wrapperJarPath to attempt a clean download.\" >&2\n    echo \"If you updated your Maven version, you need to update the specified wrapperSha256Sum property.\" >&2\n    exit 1\n  fi\nfi\n\nMAVEN_OPTS=\"$(concat_lines \"$MAVEN_PROJECTBASEDIR/.mvn/jvm.config\") $MAVEN_OPTS\"\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin; then\n  [ -n \"$JAVA_HOME\" ] \\\n    && JAVA_HOME=$(cygpath --path --windows \"$JAVA_HOME\")\n  [ -n \"$CLASSPATH\" ] \\\n    && CLASSPATH=$(cygpath --path --windows \"$CLASSPATH\")\n  [ -n \"$MAVEN_PROJECTBASEDIR\" ] \\\n    && MAVEN_PROJECTBASEDIR=$(cygpath --path --windows \"$MAVEN_PROJECTBASEDIR\")\nfi\n\n# Provide a \"standardized\" way to retrieve the CLI args that will\n# work with both Windows and non-Windows executions.\nMAVEN_CMD_LINE_ARGS=\"$MAVEN_CONFIG $*\"\nexport MAVEN_CMD_LINE_ARGS\n\nWRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\n# shellcheck disable=SC2086 # safe args\nexec \"$JAVACMD\" \\\n  $MAVEN_OPTS \\\n  $MAVEN_DEBUG_OPTS \\\n  -classpath \"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar\" \\\n  \"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}\" \\\n  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG \"$@\"\n"
  },
  {
    "path": "mvnw.cmd",
    "content": "@REM ----------------------------------------------------------------------------\r\n@REM Licensed to the Apache Software Foundation (ASF) under one\r\n@REM or more contributor license agreements.  See the NOTICE file\r\n@REM distributed with this work for additional information\r\n@REM regarding copyright ownership.  The ASF licenses this file\r\n@REM to you under the Apache License, Version 2.0 (the\r\n@REM \"License\"); you may not use this file except in compliance\r\n@REM with the License.  You may obtain a copy of the License at\r\n@REM\r\n@REM    http://www.apache.org/licenses/LICENSE-2.0\r\n@REM\r\n@REM Unless required by applicable law or agreed to in writing,\r\n@REM software distributed under the License is distributed on an\r\n@REM \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\r\n@REM KIND, either express or implied.  See the License for the\r\n@REM specific language governing permissions and limitations\r\n@REM under the License.\r\n@REM ----------------------------------------------------------------------------\r\n\r\n@REM ----------------------------------------------------------------------------\r\n@REM Apache Maven Wrapper startup batch script, version 3.3.4\r\n@REM\r\n@REM Required ENV vars:\r\n@REM JAVA_HOME - location of a JDK home dir\r\n@REM\r\n@REM Optional ENV vars\r\n@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands\r\n@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending\r\n@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven\r\n@REM     e.g. to debug Maven itself, use\r\n@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\r\n@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files\r\n@REM ----------------------------------------------------------------------------\r\n\r\n@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'\r\n@echo off\r\n@REM set title of command window\r\ntitle %0\r\n@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'\r\n@if \"%MAVEN_BATCH_ECHO%\" == \"on\"  echo %MAVEN_BATCH_ECHO%\r\n\r\n@REM set %HOME% to equivalent of $HOME\r\nif \"%HOME%\" == \"\" (set \"HOME=%HOMEDRIVE%%HOMEPATH%\")\r\n\r\n@REM Execute a user defined script before this one\r\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPre\r\n@REM check for pre script, once with legacy .bat ending and once with .cmd ending\r\nif exist \"%USERPROFILE%\\mavenrc_pre.bat\" call \"%USERPROFILE%\\mavenrc_pre.bat\" %*\r\nif exist \"%USERPROFILE%\\mavenrc_pre.cmd\" call \"%USERPROFILE%\\mavenrc_pre.cmd\" %*\r\n:skipRcPre\r\n\r\n@setlocal\r\n\r\nset ERROR_CODE=0\r\n\r\n@REM To isolate internal variables from possible post scripts, we use another setlocal\r\n@setlocal\r\n\r\n@REM ==== START VALIDATION ====\r\nif not \"%JAVA_HOME%\" == \"\" goto OkJHome\r\n\r\necho. >&2\r\necho Error: JAVA_HOME not found in your environment. >&2\r\necho Please set the JAVA_HOME variable in your environment to match the >&2\r\necho location of your Java installation. >&2\r\necho. >&2\r\ngoto error\r\n\r\n:OkJHome\r\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto init\r\n\r\necho. >&2\r\necho Error: JAVA_HOME is set to an invalid directory. >&2\r\necho JAVA_HOME = \"%JAVA_HOME%\" >&2\r\necho Please set the JAVA_HOME variable in your environment to match the >&2\r\necho location of your Java installation. >&2\r\necho. >&2\r\ngoto error\r\n\r\n@REM ==== END VALIDATION ====\r\n\r\n:init\r\n\r\n@REM Find the project base dir, i.e. the directory that contains the folder \".mvn\".\r\n@REM Fallback to current working directory if not found.\r\n\r\nset MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%\r\nIF NOT \"%MAVEN_PROJECTBASEDIR%\"==\"\" goto endDetectBaseDir\r\n\r\nset EXEC_DIR=%CD%\r\nset WDIR=%EXEC_DIR%\r\n:findBaseDir\r\nIF EXIST \"%WDIR%\"\\.mvn goto baseDirFound\r\ncd ..\r\nIF \"%WDIR%\"==\"%CD%\" goto baseDirNotFound\r\nset WDIR=%CD%\r\ngoto findBaseDir\r\n\r\n:baseDirFound\r\nset MAVEN_PROJECTBASEDIR=%WDIR%\r\ncd \"%EXEC_DIR%\"\r\ngoto endDetectBaseDir\r\n\r\n:baseDirNotFound\r\nset MAVEN_PROJECTBASEDIR=%EXEC_DIR%\r\ncd \"%EXEC_DIR%\"\r\n\r\n:endDetectBaseDir\r\n\r\nIF NOT EXIST \"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\" goto endReadAdditionalConfig\r\n\r\n@setlocal EnableExtensions EnableDelayedExpansion\r\nfor /F \"usebackq delims=\" %%a in (\"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a\r\n@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%\r\n\r\n:endReadAdditionalConfig\r\n\r\nSET MAVEN_JAVA_EXE=\"%JAVA_HOME%\\bin\\java.exe\"\r\nset WRAPPER_JAR=\"%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.jar\"\r\nset WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\r\n\r\nset WRAPPER_URL=\"https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar\"\r\n\r\nFOR /F \"usebackq tokens=1,2 delims==\" %%A IN (\"%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.properties\") DO (\r\n    IF \"%%A\"==\"wrapperUrl\" SET WRAPPER_URL=%%B\r\n)\r\n\r\n@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central\r\n@REM This allows using the maven wrapper in projects that prohibit checking in binary data.\r\nif exist %WRAPPER_JAR% (\r\n    if \"%MVNW_VERBOSE%\" == \"true\" (\r\n        echo Found %WRAPPER_JAR%\r\n    )\r\n) else (\r\n    if not \"%MVNW_REPOURL%\" == \"\" (\r\n        SET WRAPPER_URL=\"%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar\"\r\n    )\r\n    if \"%MVNW_VERBOSE%\" == \"true\" (\r\n        echo Couldn't find %WRAPPER_JAR%, downloading it ...\r\n        echo Downloading from: %WRAPPER_URL%\r\n    )\r\n\r\n    powershell -Command \"&{\"^\r\n\t\t\"$webclient = new-object System.Net.WebClient;\"^\r\n\t\t\"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {\"^\r\n\t\t\"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');\"^\r\n\t\t\"}\"^\r\n\t\t\"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')\"^\r\n\t\t\"}\"\r\n    if \"%MVNW_VERBOSE%\" == \"true\" (\r\n        echo Finished downloading %WRAPPER_JAR%\r\n    )\r\n)\r\n@REM End of extension\r\n\r\n@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file\r\nSET WRAPPER_SHA_256_SUM=\"\"\r\nFOR /F \"usebackq tokens=1,2 delims==\" %%A IN (\"%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.properties\") DO (\r\n    IF \"%%A\"==\"wrapperSha256Sum\" SET WRAPPER_SHA_256_SUM=%%B\r\n)\r\nIF NOT %WRAPPER_SHA_256_SUM%==\"\" (\r\n    powershell -Command \"&{\"^\r\n       \"Import-Module $PSHOME\\Modules\\Microsoft.PowerShell.Utility -Function Get-FileHash;\"^\r\n       \"$hash = (Get-FileHash \\\"%WRAPPER_JAR%\\\" -Algorithm SHA256).Hash.ToLower();\"^\r\n       \"If('%WRAPPER_SHA_256_SUM%' -ne $hash){\"^\r\n       \"  Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';\"^\r\n       \"  Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';\"^\r\n       \"  Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';\"^\r\n       \"  exit 1;\"^\r\n       \"}\"^\r\n       \"}\"\r\n    if ERRORLEVEL 1 goto error\r\n)\r\n\r\n@REM Provide a \"standardized\" way to retrieve the CLI args that will\r\n@REM work with both Windows and non-Windows executions.\r\nset MAVEN_CMD_LINE_ARGS=%*\r\n\r\n%MAVEN_JAVA_EXE% ^\r\n  %JVM_CONFIG_MAVEN_PROPS% ^\r\n  %MAVEN_OPTS% ^\r\n  %MAVEN_DEBUG_OPTS% ^\r\n  -classpath %WRAPPER_JAR% ^\r\n  \"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%\" ^\r\n  %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*\r\nif ERRORLEVEL 1 goto error\r\ngoto end\r\n\r\n:error\r\nset ERROR_CODE=1\r\n\r\n:end\r\n@endlocal & set ERROR_CODE=%ERROR_CODE%\r\n\r\nif not \"%MAVEN_SKIP_RC%\"==\"\" goto skipRcPost\r\n@REM check for post script, once with legacy .bat ending and once with .cmd ending\r\nif exist \"%USERPROFILE%\\mavenrc_post.bat\" call \"%USERPROFILE%\\mavenrc_post.bat\"\r\nif exist \"%USERPROFILE%\\mavenrc_post.cmd\" call \"%USERPROFILE%\\mavenrc_post.cmd\"\r\n:skipRcPost\r\n\r\n@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'\r\nif \"%MAVEN_BATCH_PAUSE%\"==\"on\" pause\r\n\r\nif \"%MAVEN_TERMINATE_CMD%\"==\"on\" exit %ERROR_CODE%\r\n\r\ncmd /C exit /B %ERROR_CODE%\r\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>pom</packaging>\n    <groupId>com.github.martinpaljak</groupId>\n    <artifactId>ant-javacard-package</artifactId>\n    <version>26.03.08-SNAPSHOT</version>\n    <description>Easy to use Ant task for building JavaCard applets</description>\n    <name>ant-javacard package</name>\n    <url>https://github.com/martinpaljak/ant-javacard</url>\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <!-- GH Actions fails sometimes on first try -->\n        <maven.wagon.http.retryHandler.count>2</maven.wagon.http.retryHandler.count>\n        <project.build.outputTimestamp>2026-03-08T04:57:03Z</project.build.outputTimestamp>\n    </properties>\n    <repositories>\n        <repository>\n            <id>javacard-pro</id>\n            <name>javacard.pro</name>\n            <url>https://mvn.javacard.pro/maven/</url>\n        </repository>\n        <repository>\n            <id>javacard-pro-snapshots</id>\n            <url>https://mvn.javacard.pro/maven/SNAPSHOTS/</url>\n        </repository>\n    </repositories>\n    <!-- Default is publishing to private Maven repo on javacard.pro -->\n    <distributionManagement>\n        <repository>\n            <id>javacard</id>\n            <url>scpexe://mvn@mvn.javacard.pro/home/mvn/maven/</url>\n        </repository>\n        <snapshotRepository>\n            <id>javacard</id>\n            <url>scpexe://mvn@mvn.javacard.pro/home/mvn/maven/SNAPSHOTS</url>\n        </snapshotRepository>\n    </distributionManagement>\n    <modules>\n        <module>capfile</module>\n        <module>task</module>\n    </modules>\n    <build>\n        <pluginManagement>\n            <plugins>\n                <plugin>\n                    <groupId>org.openrewrite.maven</groupId>\n                    <artifactId>rewrite-maven-plugin</artifactId>\n                    <version>6.37.0</version>\n                    <configuration>\n                        <runPerSubmodule>true</runPerSubmodule>\n                        <activeRecipes>\n                            <!-- Braces everywhere -->\n                            <recipe>org.openrewrite.staticanalysis.NeedBraces</recipe>\n                            <!-- Standard modifier ordering -->\n                            <recipe>org.openrewrite.staticanalysis.ModifierOrder</recipe>\n                            <!-- Final everywhere -->\n                            <recipe>org.openrewrite.staticanalysis.FinalizePrivateFields</recipe>\n                        </activeRecipes>\n                    </configuration>\n                    <dependencies>\n                        <dependency>\n                            <groupId>org.openrewrite.recipe</groupId>\n                            <artifactId>rewrite-static-analysis</artifactId>\n                            <version>2.33.1</version>\n                        </dependency>\n                    </dependencies>\n                </plugin>\n                <plugin>\n                    <groupId>com.github.spotbugs</groupId>\n                    <artifactId>spotbugs-maven-plugin</artifactId>\n                    <version>4.9.8.3</version>\n                    <configuration>\n                        <excludeFilterFile>spotbugs.xml</excludeFilterFile>\n                    </configuration>\n                </plugin>\n                <plugin>\n                    <groupId>org.jacoco</groupId>\n                    <artifactId>jacoco-maven-plugin</artifactId>\n                    <version>0.8.14</version>\n                </plugin>\n            </plugins>\n        </pluginManagement>\n        <extensions>\n            <extension>\n                <groupId>org.apache.maven.wagon</groupId>\n                <artifactId>wagon-ssh-external</artifactId>\n                <version>3.5.3</version>\n            </extension>\n        </extensions>\n        <plugins>\n            <!-- Minimum Maven version. Not really relevant with wrapper -->\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-enforcer-plugin</artifactId>\n                <version>3.6.2</version>\n                <executions>\n                    <execution>\n                        <id>enforce-maven-version</id>\n                        <goals>\n                            <goal>enforce</goal>\n                        </goals>\n                        <configuration>\n                            <rules>\n                                <requireJavaVersion>\n                                    <version>[11,)</version>\n                                    <message>Project requires JDK 11+</message>\n                                </requireJavaVersion>\n                                <requireMavenVersion>\n                                    <version>[3.9.15,)</version>\n                                </requireMavenVersion>\n                                <requirePluginVersions/>\n                            </rules>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n            <!-- Compiler -->\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.15.0</version>\n                <executions>\n                    <execution>\n                        <id>default-compile</id>\n                        <configuration>\n                            <!-- compile everything to ensure module-info contains right entries -->\n                            <release>11</release>\n                            <compilerArgs combine.children=\"append\">\n                                <arg>-Werror</arg>\n                                <arg>-Xlint:all</arg>\n                                <arg>-Xlint:-options</arg>\n                            </compilerArgs>\n                        </configuration>\n                    </execution>\n                    <execution>\n                        <!-- Run tests with 11 -->\n                        <id>default-testCompile</id>\n                        <configuration>\n                            <!-- compile everything to ensure module-info contains right entries -->\n                            <release>11</release>\n                        </configuration>\n                    </execution>\n                    <execution>\n                        <id>base-compile</id>\n                        <goals>\n                            <goal>compile</goal>\n                        </goals>\n                        <!-- recompile everything for 1.8 except the module-info.java -->\n                        <configuration>\n                            <source>8</source>\n                            <target>8</target>\n                            <excludes>\n                                <exclude>module-info.java</exclude>\n                            </excludes>\n                            <compilerArgs combine.children=\"append\">\n                                <arg>-Xlint:-options</arg>\n                            </compilerArgs>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-antrun-plugin</artifactId>\n                <version>3.2.0</version>\n                <inherited>false</inherited>\n                <executions>\n                    <execution>\n                        <id>ant-dist</id>\n                        <phase>package</phase>\n                        <goals>\n                            <goal>run</goal>\n                        </goals>\n                        <configuration>\n                            <target>\n                                <!-- Pass the plugin classpath to antrun, which is normally not there -->\n                                <property name=\"maven.plugin.classpath\" refid=\"maven.plugin.classpath\"/>\n                                <ant antfile=\"build.xml\" target=\"dist\" inheritAll=\"true\"/>\n                            </target>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n            <!-- Version fixes -->\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-clean-plugin</artifactId>\n                <version>3.5.0</version>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-install-plugin</artifactId>\n                <version>3.1.4</version>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-deploy-plugin</artifactId>\n                <version>3.1.4</version>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-site-plugin</artifactId>\n                <version>3.21.0</version>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <version>3.5.5</version>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-jar-plugin</artifactId>\n                <version>3.5.0</version>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-resources-plugin</artifactId>\n                <version>3.5.0</version>\n            </plugin>\n        </plugins>\n    </build>\n\n    <!-- Metadata -->\n    <licenses>\n        <license>\n            <name>MIT</name>\n            <url>https://github.com/martinpaljak/ant-javacard/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <url>https://github.com/martinpaljak/ant-javacard</url>\n    </scm>\n    <profiles>\n        <profile>\n            <id>check</id>\n            <build>\n                <plugins>\n                    <plugin>\n                        <groupId>org.openrewrite.maven</groupId>\n                        <artifactId>rewrite-maven-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <phase>process-sources</phase>\n                                <goals>\n                                    <goal>dryRun</goal>\n                                </goals>\n                                <configuration>\n                                    <failOnDryRunResults>true</failOnDryRunResults>\n                                </configuration>\n                            </execution>\n                        </executions>\n                    </plugin>\n                    <plugin>\n                        <groupId>com.github.spotbugs</groupId>\n                        <artifactId>spotbugs-maven-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <phase>verify</phase>\n                                <goals>\n                                    <goal>check</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                    </plugin>\n                    <plugin>\n                        <groupId>org.jacoco</groupId>\n                        <artifactId>jacoco-maven-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <id>prepare-agent</id>\n                                <goals>\n                                    <goal>prepare-agent</goal>\n                                </goals>\n                            </execution>\n                            <execution>\n                                <id>report</id>\n                                <phase>verify</phase>\n                                <goals>\n                                    <goal>report</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>fixup</id>\n            <build>\n                <plugins>\n                    <plugin>\n                        <groupId>org.openrewrite.maven</groupId>\n                        <artifactId>rewrite-maven-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <phase>process-sources</phase>\n                                <goals>\n                                    <goal>run</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n    </profiles>\n</project>\n"
  },
  {
    "path": "spotbugs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<FindBugsFilter xmlns=\"https://github.com/spotbugs/filter/3.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd\">\n</FindBugsFilter>\n"
  },
  {
    "path": "src/testapplets/empty/Empty.java",
    "content": "// SPDX-FileCopyrightText: 2024 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage testapplets.empty;\n\nimport javacard.framework.APDU;\nimport javacard.framework.Applet;\nimport javacard.framework.ISO7816;\nimport javacard.framework.ISOException;\nimport javacard.security.CryptoException;\n\npublic class Empty extends Applet {\n\n    private Empty(byte[] parameters, short offset, byte length) {\n        register(parameters, (short) (offset + 1), parameters[offset]);\n    }\n\n    public static void install(byte[] parameters, short offset, byte length) {\n        new Empty(parameters, offset, length);\n    }\n\n    public void process(APDU apdu) throws ISOException {\n        if (selectingApplet())\n            return;\n        byte[] buffer = apdu.getBuffer();\n        if (buffer[ISO7816.OFFSET_LC] != (short) 6)\n            CryptoException.throwIt((short) 0x6666);\n        apdu.setIncomingAndReceive();\n        apdu.setOutgoingAndSend((short) 0, (short) 11);\n    }\n}\n"
  },
  {
    "path": "src/testapplets/fail/Fail.java",
    "content": "// SPDX-FileCopyrightText: 2024 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage testapplets.fail;\n\nimport javacard.framework.APDU;\nimport javacard.framework.Applet;\nimport javacard.framework.ISO7816;\nimport javacard.framework.ISOException;\nimport javacard.security.CryptoException;\n\npublic class Fail extends Applet {\n\n    private Fail(byte[] parameters, short offset, byte length) {\n        register(parameters, (short) (offset + 1), parameters[offset]);\n    }\n\n    public static void install(byte[] parameters, short offset, byte length) {\n        new Fail(parameters, offset, length);\n    }\n\n    public void process(APDU apdu) throws ISOException {\n        if (selectingApplet())\n            return;\n        byte[] buffer = apdu.getBuffer();\n        short i = 5;\n        if (buffer[i + buffer[ISO7816.OFFSET_CLA]] != (short) 6)\n            CryptoException.throwIt((short) 0x6666);\n        apdu.setIncomingAndReceive();\n        apdu.setOutgoingAndSend((short) 0, (short) 11);\n    }\n}\n"
  },
  {
    "path": "src/testapplets/integer/EmptyInt.java",
    "content": "// SPDX-FileCopyrightText: 2024 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage testapplets.integer;\n\nimport javacard.framework.APDU;\nimport javacard.framework.Applet;\nimport javacard.framework.ISOException;\n\npublic class EmptyInt extends Applet {\n\n\tprivate EmptyInt(byte[] parameters, short offset, byte length) {\n\t\tint everything = 42;\n\t\tregister(parameters, (short) (offset + 1), parameters[offset]);\n\t}\n\n\tpublic static void install(byte[] parameters, short offset, byte length) {\n\t\tnew EmptyInt(parameters, offset, length);\n\t}\n\n\tpublic void process(APDU arg0) throws ISOException {\n\n\t}\n\n}\n"
  },
  {
    "path": "src/testapplets/library/SomeLibrary.java",
    "content": "// SPDX-FileCopyrightText: 2024 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage testapplets.library;\n\n// Just to have an import\nimport javacard.security.RandomData;\n\npublic class SomeLibrary {\n\tpublic static final short TRUE = (short) 0x5AA5;\n\tpublic static final short FALSE = (short) 0xA55A;\n\n\tpublic static short booleantest(boolean b) {\n\t\treturn b ? TRUE : FALSE;\n\t}\n\n\tpublic static RandomData getRandom() {\n\t\treturn RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);\n\t}\n}\n"
  },
  {
    "path": "src/testapplets/libraryuser/LibraryUser.java",
    "content": "// SPDX-FileCopyrightText: 2024 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage testapplets.libraryuser;\n\nimport javacard.framework.APDU;\nimport javacard.framework.Applet;\nimport javacard.framework.ISOException;\nimport testapplets.library.SomeLibrary;\n\npublic class LibraryUser extends Applet {\n\n    private final short value;\n\n    private LibraryUser(boolean bvalue) {\n        value = SomeLibrary.booleantest(bvalue);\n    }\n\n    public static void install(byte[] parameters, short offset, byte length) {\n        new LibraryUser(true).register(parameters, (short) (offset + 1), parameters[offset]);\n    }\n\n    public void process(APDU arg0) throws ISOException {\n        SomeLibrary.booleantest(true);\n    }\n}\n"
  },
  {
    "path": "src/testapplets/multiapp/First.java",
    "content": "// SPDX-FileCopyrightText: 2024 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage testapplets.multiapp;\n\nimport javacard.framework.APDU;\nimport javacard.framework.Applet;\nimport javacard.framework.ISOException;\nimport javacard.security.CryptoException;\n\npublic class First extends Applet {\n\n\tprivate First(byte[] parameters, short offset, byte length) {\n\t\tregister(parameters, (short) (offset + 1), parameters[offset]);\n\t}\n\n\tpublic static void install(byte[] parameters, short offset, byte length) {\n\t\tnew First(parameters, offset, length);\n\t}\n\n\tpublic void process(APDU arg0) throws ISOException {\n\t\tCryptoException.throwIt((short) 0x6666);\n\t}\n}\n"
  },
  {
    "path": "src/testapplets/multiapp/Second.java",
    "content": "// SPDX-FileCopyrightText: 2024 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage testapplets.multiapp;\n\nimport javacard.framework.APDU;\nimport javacard.framework.Applet;\nimport javacard.framework.ISOException;\nimport javacard.security.CryptoException;\n\npublic class Second extends Applet {\n\n\tprivate Second(byte[] parameters, short offset, byte length) {\n\t\tregister(parameters, (short) (offset + 1), parameters[offset]);\n\t}\n\n\tpublic static void install(byte[] parameters, short offset, byte length) {\n\t\tnew Second(parameters, offset, length);\n\t}\n\n\tpublic void process(APDU arg0) throws ISOException {\n\t\tCryptoException.throwIt((short) 0x6666);\n\t}\n}\n"
  },
  {
    "path": "src/testapplets/stringdefs/Empty.java",
    "content": "// SPDX-FileCopyrightText: 2024 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage testapplets.stringdefs;\n\nimport javacard.framework.APDU;\nimport javacard.framework.Applet;\nimport javacard.framework.ISOException;\nimport javacard.security.CryptoException;\nimport javacardx.annotations.StringDef;\nimport javacardx.annotations.StringPool;\n\n@StringPool(value = {\n\t\t@StringDef(name = \"hello\", value = \"Hello World!\"),\n},\n\t\tname = \"HelloWorldStrings\")\n\npublic class Empty extends Applet {\n\n\tprivate Empty(byte[] parameters, short offset, byte length) {\n\t\tregister(parameters, (short) (offset + 1), parameters[offset]);\n\t}\n\n\tpublic static void install(byte[] parameters, short offset, byte length) {\n\t\tnew Empty(parameters, offset, length);\n\t}\n\n\tpublic void process(APDU arg0) throws ISOException {\n\t\tCryptoException.throwIt((short)HelloWorldStrings.hello.length);\n\t}\n}\n"
  },
  {
    "path": "task/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <!-- Not using metacard, as this is meant to be compilable with Java 1.8 as well -->\n    <parent>\n        <groupId>com.github.martinpaljak</groupId>\n        <artifactId>ant-javacard-package</artifactId>\n        <version>26.03.08-SNAPSHOT</version>\n    </parent>\n    <artifactId>ant-javacard</artifactId>\n    <description>Easy to use Ant task for building JavaCard applets</description>\n    <name>ant-javacard task</name>\n    <dependencies>\n        <dependency>\n            <groupId>org.apache.ant</groupId>\n            <artifactId>ant</artifactId>\n            <version>1.10.17</version>\n            <scope>provided</scope>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>com.github.martinpaljak</groupId>\n            <artifactId>capfile</artifactId>\n            <version>${project.version}</version>\n            <scope>compile</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.testng</groupId>\n            <artifactId>testng</artifactId>\n            <version>7.12.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-simple</artifactId>\n            <version>2.0.17</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-jar-plugin</artifactId>\n                <configuration>\n                    <archive>\n                        <manifest>\n                            <mainClass>pro.javacard.ant.DummyMain</mainClass>\n                        </manifest>\n                        <manifestEntries>\n                            <Implementation-Version>${project.version}</Implementation-Version>\n                        </manifestEntries>\n                    </archive>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "task/src/main/java/pro/javacard/ant/DummyMain.java",
    "content": "// SPDX-FileCopyrightText: 2015 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.ant;\n\nimport pro.javacard.capfile.CAPFile;\nimport pro.javacard.capfile.HexUtils;\nimport pro.javacard.sdk.ExportFileHelper;\nimport pro.javacard.sdk.JavaCardSDK;\nimport pro.javacard.sdk.OffCardVerifier;\nimport pro.javacard.sdk.VerifierError;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.StandardCopyOption;\n\nimport java.security.MessageDigest;\nimport java.security.ProtectionDomain;\nimport java.util.Arrays;\nimport java.util.Vector;\nimport java.util.stream.Collectors;\n\npublic final class DummyMain {\n\n    static Path rename(Path path, String template) throws IOException {\n        CAPFile cap = CAPFile.fromFile(path);\n        boolean isLibrary = cap.getAppletAIDs().isEmpty();\n        String effectiveTemplate = template != null ? template\n                : isLibrary ? \"%n_%a_%v_%h.cap\" : \"%n_%a_%h_%j.cap\";\n        Path output = Paths.get(Misc.capFileName(cap, effectiveTemplate));\n        if (!path.toAbsolutePath().normalize().equals(output.toAbsolutePath().normalize())) {\n            Files.copy(path, output, StandardCopyOption.REPLACE_EXISTING);\n        }\n        return output;\n    }\n\n    static int runcycle(String[] argv) throws IOException {\n        Vector<String> args = new Vector<>(Arrays.asList(argv));\n\n        if (args.size() >= 1 && args.get(0).equals(\"-r\")) {\n            args.remove(0);\n            if (args.isEmpty()) {\n                System.err.println(\"Usage: java -jar ant-javacard.jar -r <capfile>\");\n                return 1;\n            }\n            final String capfile = args.remove(0);\n            Path path = Paths.get(capfile);\n            if (!Files.isRegularFile(path) || !capfile.endsWith(\".cap\")) {\n                System.err.println(\"Not a valid CAP file: \" + capfile);\n                return 1;\n            }\n            try {\n                String template = System.getenv(\"CAP_NAME_TEMPLATE\");\n                if (template != null && template.contains(\"%J\")) {\n                    System.err.println(\"CAP_NAME_TEMPLATE must not contain %J (JDK version is unknown for rename)\");\n                    return 1;\n                }\n                Path output = rename(path, template);\n                System.out.println(output);\n                if (path.toAbsolutePath().normalize().equals(output.toAbsolutePath().normalize())) {\n                    System.out.println(\"Already has standard name\");\n                }\n                return 0;\n            } catch (Exception e) {\n                System.err.println(String.format(\"Failed to process CAP file: %s: %s\", e.getClass().getSimpleName(), e.getMessage()));\n                return 1;\n            }\n        } else if (args.isEmpty()) {\n            ProtectionDomain pd = DummyMain.class.getProtectionDomain();\n            System.out.println(String.format(\"This is an ANT task (ant-javacard %s)\", DummyMain.class.getPackage().getImplementationVersion()));\n            System.out.println(\"Read usage instructions from https://github.com/martinpaljak/ant-javacard#syntax\");\n\n            if (pd != null && pd.getCodeSource() != null && pd.getCodeSource().getLocation() != null) {\n                try {\n                    System.out.println();\n                    String f = pd.getCodeSource().getLocation().getPath();\n                    Path p = Paths.get(f);\n                    byte[] sha256 = MessageDigest.getInstance(\"SHA-256\").digest(Files.readAllBytes(p));\n                    System.out.println(String.format(\"SHA256 (%s) = %s\", f, HexUtils.bin2hex(sha256).toLowerCase()));\n                } catch (Exception e) {\n                    System.out.println(\"Could not verify integrity: \" + e.getMessage());\n                }\n            }\n            System.out.println();\n            System.out.println(\"But you can use it to dump/verify CAP files, like this:\");\n            System.out.println(\"$ java -jar ant-javacard.jar <capfile>\");\n            System.out.println();\n            System.out.println(\"Or copy a CAP file with a standard name into current directory:\");\n            System.out.println(\"$ java -jar ant-javacard.jar -r <capfile>\");\n            return 1;\n        } else if (args.size() == 1) {\n            // Simple dumping of capfile\n            final String capfile = args.remove(0);\n\n            Path path = Paths.get(capfile);\n            if (Files.isRegularFile(path) && capfile.endsWith(\".cap\")) {\n                try {\n                    CAPFile cap = CAPFile.fromBytes(Files.readAllBytes(path));\n                    cap.dump(System.out);\n                    return 0;\n                } catch (Exception e) {\n                    System.err.println(String.format(\"Failed to read/parse CAP file: %s: %s\", e.getClass().getSimpleName(), e.getMessage()));\n                    return 1;\n                }\n            } else if (Files.isRegularFile(path) && capfile.endsWith(\".exp\")) {\n                try {\n                    System.out.println(String.format(\"%s: %s\", path, ExportFileHelper.parsePackage(path)));\n                    return 0;\n                } catch (Exception e) {\n                    System.err.println(String.format(\"Failed to read/parse EXP file: %s: %s\", e.getClass().getSimpleName(), e.getMessage()));\n                    return 1;\n                }\n            } else {\n                System.err.println(\"Usage: java -jar ant-javacard.jar <capfile|expfile>\");\n                return 1;\n            }\n        } else {\n            // Verification of capfile\n            final Path sdkpath = Paths.get(args.remove(0));\n            // Targetsdk path is a folder\n            final Path targetsdkpath;\n            final String capfile;\n            final String next = args.remove(0);\n            if (Files.isDirectory(Paths.get(next))) {\n                targetsdkpath = Paths.get(next);\n                capfile = args.remove(0);\n            } else {\n                capfile = next;\n                targetsdkpath = sdkpath;\n            }\n            // If jarfile is given, exports from jar files are extracted internally.\n            Vector<File> exps = args.stream().map(File::new).collect(Collectors.toCollection(Vector::new));\n\n            CAPFile cap = CAPFile.fromBytes(Files.readAllBytes(Paths.get(capfile)));\n            try {\n                JavaCardSDK sdk = JavaCardSDK.detectSDK(sdkpath).orElseThrow(() -> new VerifierError(\"No SDK detected in \" + sdkpath));\n                JavaCardSDK target = JavaCardSDK.detectSDK(targetsdkpath).orElseThrow(() -> new VerifierError(\"No target SDK detected with \" + targetsdkpath));\n\n                OffCardVerifier verifier = OffCardVerifier.withSDK(sdk);\n\n                cap.dump(System.out);\n\n                verifier.verifyAgainst(new File(capfile), target, exps);\n                System.out.println(String.format(\"Verified %s with SDK v%s against SDK v%s\", capfile, sdk.getVersion(), target.getVersion()));\n                return 0;\n            } catch (VerifierError e) {\n                System.err.println(\"Verification failed: \" + e.getMessage());\n                return 1;\n            }\n        }\n    }\n\n    public static void main(String[] argv) {\n        try {\n            runcycle(argv);\n        } catch (Throwable e) {\n            Misc.cleanTemp();\n            System.err.println(String.format(\"Error: %s: %s\", e.getClass().getSimpleName(), e.getMessage()));\n            if (System.getenv(\"ANT_JAVACARD_DEBUG\") != null) {\n                e.printStackTrace();\n            }\n            System.exit(1);\n        }\n    }\n\n}\n"
  },
  {
    "path": "task/src/main/java/pro/javacard/ant/HelpingBuildException.java",
    "content": "// SPDX-FileCopyrightText: 2015 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.ant;\n\nimport org.apache.tools.ant.BuildException;\n\npublic class HelpingBuildException extends BuildException {\n    private static final long serialVersionUID = -2365126253968479314L;\n\n    public HelpingBuildException(String msg) {\n        super(msg + \"\\n\\nPLEASE READ https://github.com/martinpaljak/ant-javacard#readme\");\n    }\n}\n"
  },
  {
    "path": "task/src/main/java/pro/javacard/ant/JCApplet.java",
    "content": "// SPDX-FileCopyrightText: 2015 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.ant;\n\nimport org.apache.tools.ant.BuildException;\n\n// Just for Ant\npublic class JCApplet {\n    String klass = null;\n    byte[] aid = null;\n\n    public JCApplet() {\n    }\n\n    public void setClass(String msg) {\n        klass = msg;\n    }\n\n    public void setAID(String msg) {\n        try {\n            aid = Misc.stringToBin(msg);\n            if (aid.length < 5 || aid.length > 16) {\n                throw new BuildException(\"Applet AID must be between 5 and 16 bytes: \" + aid.length);\n            }\n        } catch (IllegalArgumentException e) {\n            throw new BuildException(\"Not a valid applet AID: \" + e.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "task/src/main/java/pro/javacard/ant/JCCap.java",
    "content": "// SPDX-FileCopyrightText: 2015 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.ant;\n\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Project;\nimport org.apache.tools.ant.Task;\nimport org.apache.tools.ant.taskdefs.Jar;\nimport org.apache.tools.ant.taskdefs.Java;\nimport org.apache.tools.ant.taskdefs.Javac;\nimport org.apache.tools.ant.types.Environment;\nimport org.apache.tools.ant.types.FileSet;\nimport pro.javacard.capfile.CAPFile;\nimport pro.javacard.capfile.HexUtils;\nimport pro.javacard.sdk.JavaCardSDK;\nimport pro.javacard.sdk.OffCardVerifier;\nimport pro.javacard.sdk.SDKVersion;\nimport pro.javacard.sdk.VerifierError;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.*;\nimport java.util.*;\nimport java.util.logging.Logger;\nimport java.util.regex.Pattern;\n\nimport static pro.javacard.sdk.SDKVersion.*;\n\n// <cap ...>...</cap> and actual execution of core task.\npublic class JCCap extends Task {\n\n    static final String DEFAULT_CAP_NAME_TEMPLATE = \"%n_%a_%h_%j_%J.cap\";\n    static final String DEFAULT_CAP_NAME_TEMPLATE_LIB = \"%n_%a_%v_%h_%J.cap\";\n\n    private final String master_jckit_path;\n    private JavaCardSDK jckit = null;\n    private String classes_path = null;\n    private String sources_path = null;\n    private String sources2_path = null;\n    private String includes = null;\n    private String excludes = null;\n    private String package_name = null;\n    private byte[] package_aid = null;\n    private String package_version = null;\n    private final List<JCApplet> raw_applets = new ArrayList<>();\n    private final List<JCImport> raw_imports = new ArrayList<>();\n    private final List<JCSources> raw_sources = new ArrayList<>();\n    private String output_cap = null;\n    private String output_exp = null;\n    private String output_jar = null;\n    private String output_jca = null;\n    private String jckit_path = null;\n    private JavaCardSDK targetsdk = null;\n    private String raw_targetsdk = null;\n\n    private boolean verify = true;\n    private boolean debug = false;\n    private boolean strip = false;\n    private boolean ints = false;\n    private boolean exportmap = false;\n    static final String _logconf;\n\n    static final String LOGHACK = \"_ANT_JAVACARD_LOGHACK\";\n    static final boolean loghack = Boolean.parseBoolean(System.getenv().getOrDefault(LOGHACK, \"true\"));\n\n    static {\n        if (loghack) {\n            // Setting the java.util.logging configuration for convert task will prevent the creation of ~/java0.log.0 file\n            Path logconf = Misc.makeTemp(\"logging\").resolve(\"logging.properties\");\n            _logconf = logconf.toAbsolutePath().normalize().toString();\n            try {\n                Files.write(logconf, String.format(\"handlers = java.util.logging.ConsoleHandler%n.level = WARNING\").getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);\n            } catch (IOException e) {\n                System.err.println(\"Could not write temporary logging configuration: \" + e.getMessage());\n            }\n        } else {\n            _logconf = null;\n            System.err.println(\"Loghack disabled\");\n        }\n        if (System.getenv().containsKey(LOGHACK)) {\n            System.err.println(\"log level at start: \" + Logger.getLogger(\"\").getLevel());\n        }\n    }\n\n    public JCCap(String master_jckit_path) {\n        this.master_jckit_path = master_jckit_path;\n    }\n\n    public void setJCKit(String msg) {\n        jckit_path = msg;\n    }\n\n    public void setOutput(String msg) {\n        output_cap = msg;\n    }\n\n    public void setExport(String msg) {\n        output_exp = msg;\n    }\n\n    public void setJar(String msg) {\n        output_jar = msg;\n    }\n\n    public void setJca(String msg) {\n        output_jca = msg;\n    }\n\n    public void setPackage(String msg) {\n        package_name = msg;\n    }\n\n    public void setClasses(String msg) {\n        classes_path = msg;\n    }\n\n    public void setVersion(String msg) {\n        package_version = msg;\n    }\n\n    public void setSources(String arg) {\n        sources_path = arg;\n    }\n\n    public void setSources2(String arg) {\n        sources2_path = arg;\n    }\n\n    public void setIncludes(String arg) {\n        includes = arg;\n    }\n\n    public void setExcludes(String arg) {\n        excludes = arg;\n    }\n\n    public void setVerify(boolean arg) {\n        verify = arg;\n    }\n\n    public void setDebug(boolean arg) {\n        debug = arg;\n    }\n\n    public void setStrip(boolean arg) {\n        strip = arg;\n    }\n\n    public void setInts(boolean arg) {\n        ints = arg;\n    }\n\n    public void setExportmap(boolean arg) {\n        exportmap = arg;\n    }\n\n    public void setTargetsdk(String arg) {\n        raw_targetsdk = arg;\n    }\n\n    public void setAID(String msg) {\n        try {\n            package_aid = Misc.stringToBin(msg);\n            if (package_aid.length < 5 || package_aid.length > 16) {\n                throw new BuildException(String.format(\"Package AID must be between 5 and 16 bytes: %s (%d)\", HexUtils.bin2hex(package_aid), package_aid.length));\n            }\n\n        } catch (IllegalArgumentException e) {\n            throw new BuildException(\"Not a correct package AID: \" + e.getMessage());\n        }\n    }\n\n    // Many applets inside one package\n    public JCApplet createApplet() {\n        JCApplet applet = new JCApplet();\n        raw_applets.add(applet);\n        return applet;\n    }\n\n    // Many imports inside one package\n    public JCImport createImport() {\n        JCImport imp = new JCImport();\n        raw_imports.add(imp);\n        return imp;\n    }\n\n    // To support usage from Gradle, where import is a reserved name\n    public JCImport createJimport() {\n        return this.createImport();\n    }\n\n    // Nested <sources path=\"\" includes=\"\" excludes=\"\"/> elements\n    public JCSources createSources() {\n        JCSources src = new JCSources();\n        raw_sources.add(src);\n        return src;\n    }\n\n    private Optional<JavaCardSDK> findSDK() {\n        // try local configuration first\n        if (jckit_path != null) {\n            return JavaCardSDK.detectSDK(getProject().resolveFile(jckit_path).toPath());\n        }\n        // then try the master configuration\n        if (master_jckit_path != null) {\n            return JavaCardSDK.detectSDK(getProject().resolveFile(master_jckit_path).toPath());\n        }\n        // now check via ant property\n        String propPath = getProject().getProperty(\"jc.home\");\n        if (propPath != null) {\n            return JavaCardSDK.detectSDK(getProject().resolveFile(propPath).toPath());\n        }\n        // finally via the environment\n        String envPath = System.getenv(\"JC_HOME\");\n        if (envPath != null) {\n            return JavaCardSDK.detectSDK(getProject().resolveFile(envPath).toPath());\n        }\n        // return null if no options\n        return Optional.empty();\n    }\n\n    // Check that arguments are sufficient and do some DWIM\n    private void check() {\n        jckit = findSDK().orElseThrow(() -> new HelpingBuildException(\"No usable JavaCard SDK referenced\"));\n\n        log(\"INFO: using JavaCard \" + jckit.getVersion() + \" SDK in \" + jckit.getRoot() + \" with JDK \" + Misc.getCurrentJDKVersion(), Project.MSG_INFO);\n\n        if (raw_targetsdk != null) {\n            Optional<SDKVersion> targetVersion = SDKVersion.fromVersion(raw_targetsdk);\n            if (targetVersion.isPresent() && !jckit.getVersion().targets().isEmpty()) {\n                SDKVersion target = targetVersion.get();\n                if (jckit.getVersion().equals(target)) {\n                    log(\"WARN: \\\"targetsdk\\\" ignored as it matches \\\"jckit\\\" version\", Project.MSG_WARN);\n                } else {\n                    if (jckit.getVersion().targets().contains(target)) {\n                        targetsdk = jckit.target(target);\n                    } else {\n                        throw new HelpingBuildException(String.format(\"Can not target JavaCard %s with JavaCard kit %s\", target, jckit.getVersion()));\n                    }\n                }\n            } else {\n                // Resolve target\n                targetsdk = JavaCardSDK.detectSDK(getProject().resolveFile(raw_targetsdk).toPath()).orElseThrow(() -> new HelpingBuildException(\"Invalid \\\"targetsdk\\\": \" + raw_targetsdk));\n                // NOTE: verification will fail, as 3.1.0 (applies to all \"modern multi-target\" SDK-s)\n                // will require version 2.3 export files (only available as part of newer SDK-s).\n                // Verification is default, so fail early.\n                // This also means that using an older SDK as path reference will actually use export files from current multi-target SDK\n                if (!jckit.getVersion().targets().isEmpty() && !targetsdk.getVersion().equalOrNewer(V304)) {\n                    throw new HelpingBuildException(String.format(\"targetsdk %s is not compatible with jckit %s\", targetsdk.getVersion(), jckit.getVersion()));\n                }\n            }\n        }\n\n        if (targetsdk == null) {\n            targetsdk = jckit;\n        } else {\n            if (jckit.getRoot() != targetsdk.getRoot()) {\n                log(String.format(\"INFO: targeting JavaCard %s SDK in %s\", targetsdk.getVersion(), targetsdk.getRoot()), Project.MSG_INFO);\n            } else {\n                log(\"INFO: targeting JavaCard \" + targetsdk.getVersion(), Project.MSG_INFO);\n            }\n        }\n\n        // Warn about deprecation in future\n        if (sources_path != null && sources2_path != null) {\n            log(\"WARN: sources2 is deprecated in favor of multiple paths in sources\", Project.MSG_WARN);\n        }\n\n        // Nested <sources> and flat sources/sources2 attributes are mutually exclusive\n        if (!raw_sources.isEmpty() && (sources_path != null || sources2_path != null)) {\n            throw new HelpingBuildException(\"Can not use both nested <sources> elements and sources/sources2 attributes\");\n        }\n\n        // Shorthand for simple small projects - use Maven conventions\n        if (sources_path == null && classes_path == null && raw_sources.isEmpty()) {\n            if (getProject().resolveFile(\"src/main/javacard\").isDirectory()) {\n                sources_path = \"src/main/javacard\";\n            } else if (getProject().resolveFile(\"src/main/java\").isDirectory()) {\n                sources_path = \"src/main/java\";\n            }\n        }\n\n        // sources or classes must be set\n        if (sources_path == null && classes_path == null && raw_sources.isEmpty()) {\n            throw new HelpingBuildException(\"Must specify \\\"sources\\\" or \\\"classes\\\"\");\n        }\n\n        // Check package version\n        if (package_version == null) {\n            package_version = \"0.0\";\n        } else {\n            // Allowed values are 0..127\n            if (!package_version.matches(\"^[0-9]{1,3}\\\\.[0-9]{1,3}$\")) {\n                throw new HelpingBuildException(\"Invalid package version: \" + package_version);\n            }\n            if (Arrays.stream(package_version.split(\"\\\\.\")).map(e -> Integer.parseInt(e, 10)).anyMatch(e -> (e < 0 || e > 127))) {\n                throw new HelpingBuildException(\"Illegal package version value: \" + package_version);\n            }\n        }\n\n        // Check imports\n        for (JCImport a : raw_imports) {\n            if (a.jar != null && !getProject().resolveFile(a.jar).isFile()) {\n                throw new BuildException(\"Import JAR does not exist: \" + a.jar);\n            }\n            if (a.exps != null && !getProject().resolveFile(a.exps).isDirectory()) {\n                throw new BuildException(\"Import EXP files folder does not exist: \" + a.exps);\n            }\n        }\n\n        // Check nested sources\n        for (JCSources s : raw_sources) {\n            if (s.path == null) {\n                throw new BuildException(\"Nested <sources> element must have a \\\"path\\\" attribute\");\n            }\n            if (!getProject().resolveFile(s.path).isDirectory()) {\n                throw new BuildException(\"Sources path does not exist: \" + s.path);\n            }\n        }\n\n        // Construct applets and fill in missing bits from package info, if necessary\n        int applet_counter = 0;\n        for (JCApplet a : raw_applets) {\n            // Keep count for automagic numbering\n            applet_counter = applet_counter + 1;\n\n            if (a.klass == null) {\n                throw new HelpingBuildException(\"Applet class is missing\");\n            }\n            // If package name is present, must match the applet\n            if (package_name != null) {\n                if (!a.klass.contains(\".\")) {\n                    a.klass = String.format(\"%s.%s\", package_name, a.klass);\n                } else if (!a.klass.startsWith(package_name)) {\n                    throw new HelpingBuildException(String.format(\"Applet class %s is not in package %s\", a.klass, package_name));\n                }\n            } else {\n                if (a.klass.contains(\".\")) {\n                    String pkgname = a.klass.substring(0, a.klass.lastIndexOf(\".\"));\n                    log(\"INFO: setting package name to \" + pkgname, Project.MSG_INFO);\n                    package_name = pkgname;\n                } else {\n                    throw new HelpingBuildException(\"Applet must be in a package!\");\n                }\n            }\n\n            // If applet AID is present, must match the package AID\n            if (package_aid != null) {\n                if (a.aid != null) {\n                    // RID-s must match\n                    if (!Arrays.equals(Arrays.copyOf(package_aid, 5), Arrays.copyOf(a.aid, 5))) {\n                        throw new HelpingBuildException(\"Package RID does not match Applet RID\");\n                    }\n                } else {\n                    // make \"magic\" applet AID from package_aid + counter\n                    a.aid = Arrays.copyOf(package_aid, package_aid.length + 1);\n                    a.aid[package_aid.length] = (byte) applet_counter;\n                    log(\"INFO: generated applet AID: \" + HexUtils.bin2hex(a.aid) + \" for \" + a.klass, Project.MSG_INFO);\n                }\n            } else {\n                // if package AID is empty, just set it to the minimal from\n                // applet\n                if (a.aid != null) {\n                    package_aid = Arrays.copyOf(a.aid, 5);\n                } else {\n                    throw new HelpingBuildException(\"Both package AID and applet AID are missing!\");\n                }\n            }\n        }\n\n        // Check package AID\n        if (package_aid == null) {\n            throw new HelpingBuildException(\"Must specify package AID\");\n        }\n\n        // Package name must be present if no applets\n        if (raw_applets.isEmpty()) {\n            if (package_name == null) {\n                throw new HelpingBuildException(\"Must specify package name if no applets\");\n            }\n            log(String.format(\"Building library from package %s (AID: %s)\", package_name, HexUtils.bin2hex(package_aid)), Project.MSG_INFO);\n        } else {\n            log(String.format(\"Building CAP with %d applet%s from package %s (AID: %s)\", applet_counter, applet_counter > 1 ? \"s\" : \"\", package_name, HexUtils.bin2hex(package_aid)), Project.MSG_INFO);\n            for (JCApplet app : raw_applets) {\n                log(String.format(\"%s %s\", app.klass, HexUtils.bin2hex(app.aid)), Project.MSG_INFO);\n            }\n        }\n        if (output_exp != null) {\n            // Last component of the package\n            String ln = Misc.lastName(package_name);\n            output_jar = new File(output_exp, ln + \".jar\").toString();\n        }\n        // Default output name\n        if (output_cap == null) {\n            output_cap = raw_applets.size() == 0 ? DEFAULT_CAP_NAME_TEMPLATE_LIB : DEFAULT_CAP_NAME_TEMPLATE;\n        }\n    }\n\n    // To lessen the java.nio and apache.ant namespace clash...\n    private org.apache.tools.ant.types.Path mkPath(String name) {\n        if (name == null) {\n            return new org.apache.tools.ant.types.Path(getProject());\n        }\n        return new org.apache.tools.ant.types.Path(getProject(), name);\n    }\n\n    private void compile() {\n        Project project = getProject();\n        setTaskName(\"compile\");\n\n        // construct javac task\n        Javac j = new Javac();\n        j.setProject(project);\n        // See https://github.com/martinpaljak/ant-javacard/pull/96\n        j.setEncoding(\"utf-8\");\n        j.setTaskName(\"compile\");\n\n        org.apache.tools.ant.types.Path sources = mkPath(null);\n\n        if (!raw_sources.isEmpty()) {\n            // Per-path includes/excludes via nested <sources> elements\n            setTaskName(\"sources\");\n            Path mergedDir = Misc.makeTemp(\"sources-\" + runIdentifier());\n            for (JCSources src : raw_sources) {\n                File srcDir = project.resolveFile(src.path);\n                FileSet fs = new FileSet();\n                fs.setDir(srcDir);\n                fs.setProject(project);\n                if (src.includes != null) {\n                    fs.setIncludes(src.includes);\n                }\n                if (src.excludes != null) {\n                    fs.setExcludes(src.excludes);\n                }\n                String[] matched = fs.getDirectoryScanner(project).getIncludedFiles();\n                for (String rel : matched) {\n                    Path from = srcDir.toPath().resolve(rel);\n                    Path to = mergedDir.resolve(rel);\n                    log(\"using \" + from, Project.MSG_INFO);\n                    try {\n                        Path parent = to.getParent();\n                        if (parent != null) {\n                            Files.createDirectories(parent);\n                        }\n                        Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING);\n                    } catch (IOException e) {\n                        throw new BuildException(\"Failed to copy source file: \" + from, e);\n                    }\n                }\n            }\n            setTaskName(\"compile\");\n            sources.append(mkPath(mergedDir.toAbsolutePath().toString()));\n        } else {\n            // Legacy: flat sources/sources2/includes/excludes attributes\n            String pattern = Pattern.quote(File.pathSeparator);\n            String[] sources_paths = sources_path.split(pattern);\n            for (String path : sources_paths) {\n                sources.append(mkPath(path));\n            }\n\n            // Old style - second folder\n            if (sources2_path != null) {\n                sources.append(mkPath(sources2_path));\n            }\n\n            if (includes != null) {\n                j.setIncludes(includes);\n            }\n\n            if (excludes != null) {\n                j.setExcludes(excludes);\n            }\n        }\n        j.setSrcdir(sources);\n\n        // We resolve files to compile based on the sources/includes/excludes parameters, so don't set sourcepath\n        j.setSourcepath(new org.apache.tools.ant.types.Path(project, null));\n\n        log(\"Compiling files from \" + sources, Project.MSG_INFO);\n\n        // determine output directory\n        Path tmp;\n        if (classes_path != null) {\n            // if specified use that\n            tmp = project.resolveFile(classes_path).toPath();\n            if (!Files.exists(tmp)) {\n                try {\n                    Files.createDirectories(tmp);\n                } catch (IOException e) {\n                    throw new BuildException(\"Could not create classes folder \" + tmp.toAbsolutePath());\n                }\n            }\n        } else {\n            // else generate temporary folder\n            tmp = Misc.makeTemp(\"classes-\" + runIdentifier());\n            classes_path = tmp.toAbsolutePath().toString();\n        }\n\n        j.setDestdir(tmp.toFile());\n        // See \"Setting Java Compiler Options\" in User Guide\n        j.setDebug(true);\n        j.setDebugLevel(\"lines,vars,source\");\n\n        // set the best option supported by jckit\n        String javaVersion = jckit.getVersion().javaVersion();\n        // Warn in human-readable way if Java not compatible with JC Kit\n        // See https://github.com/martinpaljak/ant-javacard/issues/79\n        int jdkver = Misc.getCurrentJDKVersion();\n\n        if (!jckit.getVersion().jdkVersions().contains(jdkver)) {\n            if (jdkver > 17 && !jckit.getVersion().isOneOf(V320_25_0)) {\n                // JDK 21 can't create 1.7 class files, last version supported by JC kit 3.2\n                throw new HelpingBuildException(\"JDK 17 is the latest supported JDK.\");\n            } else if (jckit.getVersion().isOneOf(V211, V212, V221, V222) && jdkver > 8) {\n                // JDK 8 is the last version capable of creating 1.2 class files, latest version supported by all 2.x JC kits\n                throw new HelpingBuildException(\"Use JDK 8 with JavaCard kit v2.x\");\n            } else if (jdkver > 11 && !jckit.getVersion().isOneOf(V310, V320, V320_24_1, V320_25_0)) {\n                // JDK 17+ minimal class file target is 1.7, but need 1.6\n                throw new HelpingBuildException(String.format(\"Can't use JDK %d with JavaCard kit %s (use JDK 11)\", jdkver, jckit.getVersion()));\n            } else if (jdkver == 8 && jckit.getVersion().isOneOf(V320)) {\n                // 24.1 requires JDK-11 to run (while 24.0 and 25.1 can work with JDK-8, encourage updating)\n                throw new HelpingBuildException(String.format(\"Should not use JDK %d with JavaCard kit %s (use JDK 11 or 17)\", jdkver, jckit.getVersion()));\n            }\n        }\n        j.setTarget(javaVersion);\n        j.setSource(javaVersion);\n\n        j.setIncludeantruntime(false);\n        j.createCompilerArg().setValue(\"-Xlint\");\n        j.createCompilerArg().setValue(\"-Xlint:-options\");\n        j.createCompilerArg().setValue(\"-Xlint:-serial\");\n        if (jckit.getVersion().isOneOf(V304, V305, V310)) {\n            //-processor com.oracle.javacard.stringproc.StringConstantsProcessor \\\n            //                -processorpath \"JCDK_HOME/lib/tools.jar;JCDK_HOME/lib/api_classic_annotations.jar\" \\\n            j.createCompilerArg().setLine(\"-processor com.oracle.javacard.stringproc.StringConstantsProcessor\");\n            org.apache.tools.ant.types.Path pcp = new Javac().createClasspath();\n            for (Path jar : jckit.getCompilerJars()) {\n                pcp.append(mkPath(jar.toString()));\n            }\n            j.createCompilerArg().setLine(\"-processorpath \\\"\" + pcp.toString() + \"\\\"\");\n            j.createCompilerArg().setValue(\"-Xlint:all,-processing\");\n        }\n\n        j.setFailonerror(true);\n        j.setFork(true);\n        j.setListfiles(true);\n\n        // set classpath\n        org.apache.tools.ant.types.Path cp = j.createClasspath();\n        JavaCardSDK sdk = targetsdk == null ? jckit : targetsdk;\n        for (Path jar : sdk.getApiJars()) {\n            cp.append(mkPath(jar.toString()));\n        }\n        for (JCImport i : raw_imports) {\n            // Support import clauses with only jar or exp values\n            if (i.jar != null) {\n                cp.append(mkPath(i.jar));\n            }\n        }\n        j.execute();\n    }\n\n    private void addKitClasses(Java j) {\n        // classpath to jckit bits\n        org.apache.tools.ant.types.Path cp = j.createClasspath();\n        for (Path jar : jckit.getToolJars()) {\n            cp.append(mkPath(jar.toString()));\n        }\n        j.setClasspath(cp);\n    }\n\n    private void convert(Path applet_folder, Set<Path> exps) {\n        setTaskName(\"convert\");\n        // construct java task\n        Java j = new Java(this);\n        j.setTaskName(\"convert\");\n        // XXX: JC 25.0 does not exit with error on conversion issues.\n        j.setFailonerror(true);\n        j.setFork(true);\n\n        // add classpath for SDK tools\n        addKitClasses(j);\n\n        // set class depending on SDK\n        if (jckit.getVersion().equalOrNewer(V301)) {\n            j.setClassname(\"com.sun.javacard.converter.Main\");\n\n            // Don't create java0.log.0 files in home folder\n            // As a Java process is executed, we need to store it in a config file\n            if (loghack) {\n                Environment.Variable jclog = new Environment.Variable();\n                jclog.setKey(\"java.util.logging.config.file\");\n                jclog.setValue(_logconf);\n                j.addSysproperty(jclog);\n            }\n            // XXX: See https://community.oracle.com/message/10452555\n            // This is disabled, because for whatever reason, having jc.home property set, the above logging suppression does not work.\n            // make all shows no need for it on macos either.\n            //Environment.Variable jchome = new Environment.Variable();\n            //jchome.setKey(\"jc.home\");\n            //jchome.setValue(jckit.getRoot().toString());\n            //j.addSysproperty(jchome);\n        } else {\n            j.setClassname(\"com.sun.javacard.converter.Converter\");\n        }\n\n        // output path\n        j.createArg().setLine(\"-d '\" + applet_folder + \"'\");\n\n        // classes for conversion\n        j.createArg().setLine(\"-classdir '\" + classes_path + \"'\");\n\n        // construct export path\n        StringJoiner expstringbuilder = new StringJoiner(File.pathSeparator);\n\n        // Add targetSDK export files or the -target option\n        if (jckit.getVersion().targets().contains(targetsdk.getVersion())) {\n            j.createArg().setLine(\"-target \" + targetsdk.getVersion().toString());\n        } else {\n            expstringbuilder.add(targetsdk.getExportDir().toString());\n        }\n\n        // imports\n        for (Path imp : exps) {\n            expstringbuilder.add(imp.toString());\n        }\n        if (expstringbuilder.length() > 0) {\n            j.createArg().setLine(\"-exportpath '\" + expstringbuilder + \"'\");\n        }\n\n        // always be a little verbose\n        j.createArg().setLine(\"-verbose\");\n        j.createArg().setLine(\"-nobanner\");\n\n        // simple options\n        if (debug) {\n            j.createArg().setLine(\"-debug\");\n        }\n        if (!verify && !jckit.getVersion().isOneOf(V211, V212)) {\n            j.createArg().setLine(\"-noverify\");\n        }\n        if (jckit.getVersion().equalOrNewer(V301)) {\n            j.createArg().setLine(\"-useproxyclass\");\n        }\n        if (ints) {\n            j.createArg().setLine(\"-i\");\n        }\n        if (exportmap) {\n            j.createArg().setLine(\"-exportmap\");\n        }\n\n        // determine output types\n        String outputs = \"CAP\";\n        if (output_exp != null || (raw_applets.size() > 0 && verify)) {\n            outputs += \" EXP\";\n        }\n        if (output_jca != null) {\n            outputs += \" JCA\";\n        }\n        j.createArg().setLine(\"-out \" + outputs);\n\n        // define applets\n        for (JCApplet app : raw_applets) {\n            j.createArg().setLine(\"-applet \" + Misc.hexAID(app.aid) + \" \" + app.klass);\n        }\n\n        // package properties\n        j.createArg().setLine(String.format(\"%s %s %s\", package_name, Misc.hexAID(package_aid), package_version));\n\n        // report the command\n        log(\"command: \" + j.getCommandLine(), Project.MSG_DEBUG);\n\n        // execute the converter\n        j.execute();\n\n    }\n\n    // Return an identifier that uniquely identifies \"this run\", so that temporary\n    // subfolder in $ANT_JAVACARD_TMP would be sufficiently scoped to a <cap/>\n    private int runIdentifier() {\n        return Objects.hashCode(this);\n    }\n\n    @Override\n    public void execute() {\n        Project project = getProject();\n        setTaskName(\"javacard\");\n\n        // perform checks\n        check();\n\n        try {\n            // Compile first if necessary\n            if (sources_path != null || !raw_sources.isEmpty()) {\n                compile();\n            }\n\n            // Create temporary folder and add to cleanup\n            Path applet_folder = Misc.makeTemp(\"applet-\" + runIdentifier());\n\n            // Construct exportpath\n            Set<Path> exps = new TreeSet<>();\n\n            // add imports\n            for (JCImport imp : raw_imports) {\n                // Support import clauses with only jar or exp values\n                final Path f;\n                if (imp.exps != null) {\n                    f = Paths.get(imp.exps).toAbsolutePath();\n                } else {\n                    try {\n                        // Assume exp files in jar\n                        f = Misc.makeTemp(\"imports-\" + runIdentifier());\n                        OffCardVerifier.extractExps(project.resolveFile(imp.jar).toPath(), f);\n                    } catch (IOException e) {\n                        throw new BuildException(\"Can not extract EXP files from JAR\", e);\n                    }\n                }\n                exps.add(f);\n            }\n\n            // perform conversion\n            convert(applet_folder, exps);\n\n            // Copy results\n            // Last component of the package\n            String ln = Misc.lastName(package_name);\n            // directory of package\n            String pkgPath = package_name.replace(\".\", File.separator);\n            Path pkgDir = applet_folder.resolve(pkgPath);\n            Path jcsrc = pkgDir.resolve(\"javacard\");\n            // Interesting paths inside the JC folder\n            Path cap = jcsrc.resolve(ln + \".cap\");\n            Path exp = jcsrc.resolve(ln + \".exp\");\n            Path jca = jcsrc.resolve(ln + \".jca\");\n\n            // XXX: JC 25.0 does not exit with error on conversion issues, so manually check\n            if (!Files.exists(cap)) {\n                throw new BuildException(\"CAP file not generated, check conversion for errors!\");\n            }\n\n            // Verify\n            if (verify) {\n                setTaskName(\"verify\");\n                OffCardVerifier verifier = OffCardVerifier.withSDK(jckit);\n                // Add current export file\n                exps.add(exp);\n                exps.add(targetsdk.getExportDir());\n                try {\n                    verifier.verify(cap, new ArrayList<>(exps));\n                    log(String.format(\"Verification of %s passed\", cap), Project.MSG_INFO);\n                } catch (VerifierError | IOException e) {\n                    throw new BuildException(String.format(\"Verification of %s failed: %s\", cap, e.getMessage()));\n                }\n            }\n\n            setTaskName(\"cap\");\n            // Copy resources to final destination\n            try {\n                // check that a CAP file got created\n                if (!Files.exists(cap)) {\n                    throw new BuildException(\"Can not find CAP in \" + jcsrc);\n                }\n\n                // copy CAP file\n                CAPFile capfile = CAPFile.fromBytes(Files.readAllBytes(cap));\n\n                // Create output name, if not given.\n                output_cap = capFileName(capfile, output_cap);\n\n                // resolve output path\n                Path outCap = project.resolveFile(output_cap).toPath();\n\n                // strip classes, if asked\n                if (strip) {\n                    CAPFile.strip(cap);\n                }\n\n                // perform the copy\n                Files.copy(cap, outCap, StandardCopyOption.REPLACE_EXISTING);\n                // report destination\n                log(\"CAP saved to \" + outCap, Project.MSG_INFO);\n\n                // copy EXP file\n                if (output_exp != null) {\n                    setTaskName(\"exp\");\n                    // check that an EXP file got created\n                    if (!Files.exists(exp)) {\n                        throw new BuildException(\"Can not find EXP in \" + jcsrc);\n                    }\n                    // resolve output directory\n                    Path outExp = project.resolveFile(output_exp).toPath();\n                    // determine package directories\n                    Path outExpPkg = outExp.resolve(pkgPath);\n                    Path outExpPkgJc = outExpPkg.resolve(\"javacard\");\n                    // create directories\n                    if (!Files.exists(outExpPkgJc)) {\n                        Files.createDirectories(outExpPkgJc);\n                    }\n                    // perform the copy\n                    Path exp_file = outExpPkgJc.resolve(exp.getFileName());\n\n                    Files.copy(exp, exp_file, StandardCopyOption.REPLACE_EXISTING);\n                    // report destination\n                    log(\"EXP saved to \" + exp_file, Project.MSG_INFO);\n                    // add the export directory to the export path for verification\n                    exps.add(outExp);\n                }\n\n                // copy JCA file\n                if (output_jca != null) {\n                    setTaskName(\"jca\");\n                    // check that a JCA file got created\n                    if (!Files.exists(jca)) {\n                        throw new BuildException(\"Can not find JCA in \" + jcsrc);\n                    }\n                    // resolve output path\n                    outCap = project.resolveFile(output_jca).toPath();\n                    Files.copy(jca, outCap, StandardCopyOption.REPLACE_EXISTING);\n                    log(\"JCA saved to \" + outCap.toAbsolutePath(), Project.MSG_INFO);\n                }\n\n                // create JAR file\n                if (output_jar != null) {\n                    setTaskName(\"jar\");\n                    File outJar = project.resolveFile(output_jar);\n                    // create a new JAR task\n                    Jar jarz = new Jar();\n                    jarz.setProject(project);\n                    jarz.setTaskName(\"jar\");\n                    jarz.setDestFile(outJar);\n                    // include class files\n                    FileSet jarcls = new FileSet();\n                    jarcls.setDir(project.resolveFile(classes_path));\n                    jarz.add(jarcls);\n                    // include conversion output\n                    FileSet jarout = new FileSet();\n                    jarout.setDir(applet_folder.toFile());\n                    jarz.add(jarout);\n                    // create the JAR\n                    jarz.execute();\n                    log(\"JAR saved to \" + outJar.getAbsolutePath(), Project.MSG_INFO);\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n                throw new BuildException(\"Can not copy output CAP, EXP or JCA\", e);\n            }\n        } finally {\n            Misc.cleanTemp();\n        }\n    }\n\n    private String capFileName(CAPFile cap, String template) {\n        // Override %n with build-context class name (more reliable than CAP metadata for 2.x)\n        String commonNameOverride = null;\n        if (cap.getAppletAIDs().size() == 1 && !cap.getFlags().contains(\"exports\")) {\n            commonNameOverride = raw_applets.get(0).klass;\n        }\n        return Misc.capFileName(cap, template, commonNameOverride);\n    }\n}\n"
  },
  {
    "path": "task/src/main/java/pro/javacard/ant/JCImport.java",
    "content": "// SPDX-FileCopyrightText: 2015 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.ant;\n\n// Just for Ant: <import exps=\"\" jar=\"\"/>\npublic class JCImport {\n    String exps = null;\n    String jar = null;\n\n    public void setExps(String msg) {\n        exps = msg;\n    }\n\n    public void setJar(String msg) {\n        jar = msg;\n    }\n}\n"
  },
  {
    "path": "task/src/main/java/pro/javacard/ant/JCSources.java",
    "content": "// SPDX-FileCopyrightText: 2026 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.ant;\n\n// Just for Ant: <sources path=\"\" includes=\"\" excludes=\"\"/>\npublic class JCSources {\n    String path = null;\n    String includes = null;\n    String excludes = null;\n\n    public void setPath(String msg) {\n        path = msg;\n    }\n\n    public void setIncludes(String msg) {\n        includes = msg;\n    }\n\n    public void setExcludes(String msg) {\n        excludes = msg;\n    }\n}\n"
  },
  {
    "path": "task/src/main/java/pro/javacard/ant/JavaCard.java",
    "content": "// SPDX-FileCopyrightText: 2015 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.ant;\n\nimport org.apache.tools.ant.Project;\nimport org.apache.tools.ant.Task;\n\nimport java.util.Arrays;\nimport java.util.Vector;\nimport java.util.stream.IntStream;\n\n// <javacard jckit=\"${env.JCKIT}\">...</javacard>\n// This is a wrapper task that can contain one or more <cap> subtasks for building capfiles.\npublic final class JavaCard extends Task {\n\n    private final int[] lts = new int[]{8, 11, 17, 21};\n    private String master_jckit_path = null;\n    private final Vector<JCCap> packages = new Vector<>();\n\n    public void setJCKit(String msg) {\n        master_jckit_path = msg;\n    }\n\n    public JCCap createCap() {\n        JCCap pkg = new JCCap(master_jckit_path);\n        packages.add(pkg);\n        return pkg;\n    }\n\n    @Override\n    public void execute() {\n        Thread cleanup = new Thread(() -> {\n            log(\"Ctrl-C, cleaning up\", Project.MSG_INFO);\n            Misc.cleanTemp();\n        });\n        Runtime.getRuntime().addShutdownHook(cleanup);\n        String ver = JavaCard.class.getPackage().getImplementationVersion();\n        log(\"ant-javacard \" + (ver == null ? \"development\" : ver), Project.MSG_INFO);\n        if (IntStream.of(lts).noneMatch(x -> x == Misc.getCurrentJDKVersion())) {\n            log(\"Please consider using a LTS JDK version: \" + Arrays.toString(lts), Project.MSG_WARN);\n        }\n        try {\n            for (JCCap p : packages) {\n                p.execute();\n            }\n        } finally {\n            Runtime.getRuntime().removeShutdownHook(cleanup);\n        }\n    }\n\n}\n"
  },
  {
    "path": "task/src/main/java/pro/javacard/ant/Misc.java",
    "content": "// SPDX-FileCopyrightText: 2015 Martin Paljak <martin@martinpaljak.net>\n// SPDX-License-Identifier: MIT\n\npackage pro.javacard.ant;\n\nimport pro.javacard.capfile.CAPFile;\nimport pro.javacard.capfile.HexUtils;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.nio.file.*;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.StringJoiner;\n\nfinal class Misc {\n\n    static List<Path> temporary = new ArrayList<>();\n\n    static int getCurrentJDKVersion() {\n        String v = System.getProperty(\"java.version\", \"0.0.0\");\n        if (v.startsWith(\"1.8.\")) {\n            v = \"8.\" + v.substring(4);\n        }\n        int dot = v.indexOf(\".\");\n        return Integer.parseInt(v.substring(0, dot == -1 ? v.length() : dot));\n    }\n\n    static String hexAID(byte[] aid) {\n        StringJoiner hexaid = new StringJoiner(\":\");\n        for (byte b : aid) {\n            hexaid.add(String.format(\"0x%02X\", b));\n        }\n        return hexaid.toString();\n    }\n\n    // For cleaning up temporary files\n    static void rmminusrf(Path path) {\n        try {\n            Files.walkFileTree(path, new SimpleFileVisitor<Path>() {\n                @Override\n                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)\n                        throws IOException {\n                    Files.delete(file);\n                    return FileVisitResult.CONTINUE;\n                }\n\n                @Override\n                public FileVisitResult postVisitDirectory(Path dir, IOException e)\n                        throws IOException {\n                    if (e == null) {\n                        Files.delete(dir);\n                        return FileVisitResult.CONTINUE;\n                    } else {\n                        // directory iteration failed\n                        throw e;\n                    }\n                }\n            });\n        } catch (FileNotFoundException | NoSuchFileException e) {\n            // Already gone - do nothing.\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    static byte[] stringToBin(String s) {\n        s = s.toLowerCase().replaceAll(\" \", \"\").replaceAll(\":\", \"\");\n        s = s.replaceAll(\"0x\", \"\").replaceAll(\"\\n\", \"\").replaceAll(\"\\t\", \"\");\n        s = s.replaceAll(\";\", \"\");\n        return HexUtils.hex2bin(s);\n    }\n\n    // foo.bar.Baz -> Baz; Foo -> Foo\n    static String lastName(String fqdn) {\n        String ln = fqdn;\n        if (ln.lastIndexOf(\".\") != -1) {\n            ln = ln.substring(ln.lastIndexOf(\".\") + 1);\n        }\n        return ln;\n    }\n\n    static Path makeTemp(String sub) {\n        try {\n            if (System.getenv(\"ANT_JAVACARD_TMP\") != null) {\n                Path tmp = Paths.get(System.getenv(\"ANT_JAVACARD_TMP\"), sub);\n                // NOTE: would like to make sure that the folder is cleaned, but tmp/imports is shared between\n                // all imports and would result in just final import files to survive.\n                Files.createDirectories(tmp);\n                return tmp;\n            } else {\n                Path p = Files.createTempDirectory(\"jccpro\");\n                temporary.add(p);\n                return p;\n            }\n        } catch (IOException e) {\n            throw new RuntimeException(\"Can not make temporary folder\", e);\n        }\n    }\n\n    static String commonName(CAPFile cap) {\n        if (cap.getAppletAIDs().size() == 1 && !cap.getFlags().contains(\"exports\")) {\n            String className = cap.getApplets().values().iterator().next();\n            if (className != null) {\n                return lastName(className);\n            }\n        }\n        return cap.getPackageName();\n    }\n\n    static String capFileName(CAPFile cap, String template) {\n        return capFileName(cap, template, null);\n    }\n\n    static String capFileName(CAPFile cap, String template, String commonNameOverride) {\n        String commonName = commonNameOverride == null ? commonName(cap) : lastName(commonNameOverride);\n        String hash = HexUtils.bin2hex(cap.getLoadFileDataHash(\"SHA-256\")).toLowerCase();\n\n        String name = template;\n        name = name.replace(\"%H\", hash);\n        name = name.replace(\"%h\", hash.substring(0, 8));\n        name = name.replace(\"%n\", commonName);\n        name = name.replace(\"%p\", cap.getPackageName());\n        name = name.replace(\"%a\", cap.getPackageAID().toString());\n        name = name.replace(\"%v\", \"v\" + cap.getPackageVersion());\n        name = name.replace(\"%j\", cap.guessJavaCardVersion().orElse(\"unknown\"));\n        name = name.replace(\"%g\", cap.guessGlobalPlatformVersion().orElse(\"unknown\"));\n        name = name.replace(\"%J\", String.format(\"jdk%d\", getCurrentJDKVersion()));\n        return name;\n    }\n\n    static void cleanTemp() {\n        // Do not clean temporary files if manually set temporary path is set. This is useful for debugging.\n        if (System.getenv(\"ANT_JAVACARD_TMP\") != null) {\n            return;\n        }\n\n        if (Boolean.parseBoolean(System.getenv().getOrDefault(\"_ANT_JAVACARD_LITTER\", \"false\"))) {\n            System.err.println(\"Littering filesystem due to _ANT_JAVACARD_LITTER\");\n            return;\n        }\n\n        // Clean temporary files.\n        for (Path f : temporary) {\n            if (Files.exists(f)) {\n                rmminusrf(f);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests-1.8.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project basedir=\".\" default=\"test\" name=\"ant-javacard tests\">\n    <import file=\"kits.xml\"/>\n    <!-- Build test applets -->\n    <target name=\"test\" depends=\"jcpro,test-library-user,test-multiapp,test-no-output,test-sdks,test-stringdef,test-targetsdk,test-oldcross,test-exp-version\"/>\n    <!-- Different SDK-s-->\n    <target name=\"test-sdks\">\n        <javacard>\n            <!-- JC 3.1.0 -->\n            <cap jckit=\"${JC310}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <!-- JC 3.0.5 -->\n            <cap jckit=\"${JC305}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <!-- JC 3.0.4 -->\n            <cap jckit=\"${JC304}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <!-- JC 3.0.1 -->\n            <cap jckit=\"${JC303}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC222}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC212}\" sources=\"src/testapplets/empty\" verify=\"false\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap debug=\"true\" jckit=\"${JC221}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC305}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC305_2}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC305_1}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC304}\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC211}\" sources=\"src/testapplets/empty\" verify=\"false\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- Output file generation -->\n    <target name=\"test-no-output\" depends=\"jcpro\">\n        <javacard jckit=\"${JC304}\">\n            <cap sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- Library -->\n    <target name=\"test-library-old\" depends=\"jcpro\">\n        <javacard jckit=\"${JC305}\">\n            <cap targetsdk=\"${JC222}\" sources=\"src/testapplets/library\" package=\"testapplets.library\" aid=\"01020304050607\" export=\"testlib\" version=\"0.127\"/>\n        </javacard>\n    </target>\n    <!-- Library -->\n    <target name=\"test-library\" depends=\"jcpro,test-library-old\">\n        <javacard jckit=\"${JC310}\">\n            <cap targetsdk=\"3.0.4\" sources=\"src/testapplets/library\" package=\"testapplets.library\" aid=\"01020304050607\" export=\"testlib\" version=\"0.127\"/>\n        </javacard>\n    </target>\n    <!-- JC 2.2.2 with 3.0.4 library -->\n    <target name=\"test-library-user\" depends=\"jcpro,test-library\">\n        <javacard>\n            <cap jckit=\"${JC222}\" sources=\"src/testapplets/libraryuser\">\n                <applet class=\"testapplets.libraryuser.LibraryUser\" aid=\"0102030405060708\"/>\n                <import jar=\"testlib/library.jar\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- JC 3.0.4 multiapp -->\n    <target name=\"test-multiapp\" depends=\"jcpro\">\n        <javacard>\n            <cap jckit=\"${JC303}\" output=\"Multiapp.cap\" sources=\"src/testapplets/multiapp\" aid=\"010203040506\">\n                <applet class=\"testapplets.multiapp.First\" aid=\"01020304050607\"/>\n                <applet class=\"testapplets.multiapp.Second\" aid=\"0102030405060708\"/>\n                <import jar=\"testlib/library.jar\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- Stringdefs -->\n    <target name=\"test-stringdef\" depends=\"jcpro\">\n        <javacard>\n            <cap jckit=\"${JC304}\" output=\"StringDefs.cap\" sources=\"src/testapplets/stringdefs\" aid=\"010203040506\">\n                <applet class=\"testapplets.stringdefs.Empty\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- targetsdk -->\n    <target name=\"test-targetsdk\" depends=\"jcpro\">\n        <javacard>\n            <cap jckit=\"${JC305}\" targetsdk=\"${JC221}\" sources=\"src/testapplets/empty\" aid=\"010203040506\">\n                <applet class=\"testapplets.empty.Empty\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- javaversion -->\n    <target name=\"test-javaversion\" depends=\"jcpro\">\n        <javacard>\n            <cap jckit=\"${JC304}\" javaversion=\"1.6\" sources=\"src/testapplets/empty\" aid=\"010203040506\">\n                <applet class=\"testapplets.empty.Empty\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- Cross-compile against old SDK -->\n    <target name=\"test-oldcross\" depends=\"jcpro\">\n        <javacard jckit=\"${JC305}\">\n            <!-- Verification is supported for 2.2.1 and upwards-->\n            <cap targetsdk=\"${JC211}\" sources=\"src/testapplets/empty\" verify=\"false\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n    <target name=\"test-exp-version\" depends=\"jcpro\">\n        <javacard jckit=\"${JC310}\">\n            <cap targetsdk=\"3.1.0\" sources=\"src/testapplets/empty\" verify=\"true\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap targetsdk=\"3.0.5\" sources=\"src/testapplets/empty\" verify=\"true\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap targetsdk=\"3.0.4\" sources=\"src/testapplets/empty\" verify=\"true\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <!-- Verification with 3.1.0 is not supported for 3.0.1 and downwards, even if conversion can work -->\n        </javacard>\n    </target>\n</project>\n"
  },
  {
    "path": "tests-11.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project basedir=\".\" default=\"test\" name=\"ant-javacard tests\">\n    <import file=\"kits.xml\"/>\n    <!-- Build test applets -->\n    <target name=\"test\" depends=\"jcpro,test-library-user,test-multiapp,test-no-output,test-sdks,test-stringdef,test-targetsdk,test-oldcross,test-jca\"/>\n    <!-- Different SDK-s-->\n    <target name=\"test-sdks\">\n        <javacard>\n            <!-- JC 3.0.4 -->\n            <cap jckit=\"${JC304}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <!-- JC 3.0.1 -->\n            <cap jckit=\"${JC303}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC305}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC305_2}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC305_1}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC304}\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC310}\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- Output file generation -->\n    <target name=\"test-no-output\" depends=\"jcpro\">\n        <javacard jckit=\"${JC304}\">\n            <cap sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- Library -->\n    <target name=\"test-library\" depends=\"jcpro\">\n        <javacard jckit=\"${JC305}\">\n            <cap targetsdk=\"${JC222}\" sources=\"src/testapplets/library\" package=\"testapplets.library\" aid=\"01020304050607\" export=\"testlib\" version=\"0.1\" strip=\"false\"/>\n        </javacard>\n        <fail message=\"export file missing\">\n            <condition>\n                 <not>\n                   <available file=\"testlib/testapplets/library/javacard/library.exp\"/>\n                 </not>\n             </condition>\n         </fail>\n    </target>\n    <!-- JC 2.2.2 with 3.0.4 library -->\n    <target name=\"test-library-user\" depends=\"jcpro,test-library\">\n        <javacard>\n            <cap jckit=\"${JC303}\" sources=\"src/testapplets/libraryuser\">\n                <applet class=\"testapplets.libraryuser.LibraryUser\" aid=\"0102030405060708\"/>\n                <import jar=\"testlib/library.jar\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- JC 3.0.4 multiapp -->\n    <target name=\"test-multiapp\" depends=\"jcpro\">\n        <javacard>\n            <cap jckit=\"${JC303}\" output=\"Multiapp.cap\" sources=\"src/testapplets/multiapp\" aid=\"010203040506\">\n                <applet class=\"testapplets.multiapp.First\" aid=\"01020304050607\"/>\n                <applet class=\"testapplets.multiapp.Second\" aid=\"0102030405060708\"/>\n                <import jar=\"testlib/library.jar\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- Stringdefs -->\n    <target name=\"test-stringdef\" depends=\"jcpro\">\n        <javacard>\n            <cap jckit=\"${JC304}\" output=\"StringDefs.cap\" sources=\"src/testapplets/stringdefs\" aid=\"010230040506\">\n                <applet class=\"testapplets.stringdefs.Empty\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- targetsdk -->\n    <target name=\"test-targetsdk\" depends=\"jcpro\">\n        <javacard>\n            <cap jckit=\"${JC305}\" targetsdk=\"${JC221}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- javaversion -->\n    <target name=\"test-javaversion\" depends=\"jcpro\">\n        <javacard>\n            <cap jckit=\"${JC304}\" javaversion=\"1.6\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- jca -->\n    <target name=\"test-jca\" depends=\"jcpro\">\n        <javacard>\n            <cap jckit=\"${JC304}\" sources=\"src/testapplets/empty\" jca=\"foobar.jca\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n        <fail message=\"jca file missing\">\n            <condition>\n                 <not>\n                   <available file=\"foobar.jca\"/>\n                 </not>\n             </condition>\n         </fail>\n    </target>\n    <!-- Cross-compile against old SDK -->\n    <target name=\"test-oldcross\" depends=\"jcpro\">\n        <javacard jckit=\"${JC305}\">\n            <!-- Verification is supported for 2.2.1 and upwards-->\n            <cap targetsdk=\"${JC211}\" sources=\"src/testapplets/empty\" verify=\"false\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n</project>\n"
  },
  {
    "path": "tests-17.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project basedir=\".\" default=\"test\" name=\"ant-javacard tests\">\n    <import file=\"kits.xml\"/>\n\n    <!-- Build test applets -->\n    <target name=\"test\" depends=\"jcpro,test-sdks\"/>\n\n    <!-- Different SDK-s-->\n    <target name=\"test-sdks\">\n        <javacard>\n            <!-- JC 3.1.0 -->\n            <cap jckit=\"${JC310}\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC310}\" targetsdk=\"3.0.4\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC310}\" targetsdk=\"3.0.5\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC310}\" targetsdk=\"3.1.0\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n            <!-- JC 3.2.0 -->\n            <cap jckit=\"${JC320}\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC320_3}\" targetsdk=\"3.0.4\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC320_2}\" targetsdk=\"3.0.5\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC320_1}\" targetsdk=\"3.1.0\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n</project>\n"
  },
  {
    "path": "tests-21.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project basedir=\".\" default=\"test\" name=\"ant-javacard tests\">\n    <import file=\"kits.xml\"/>\n\n    <!-- Build test applets -->\n    <target name=\"test\" depends=\"jcpro,test-sdks,test-nested-sources\"/>\n\n    <!-- Different SDK-s-->\n    <target name=\"test-sdks\">\n        <javacard>\n            <!-- JC 3.2.0 -->\n            <cap jckit=\"${JC320_2}\" sources=\"src/testapplets/empty\" ints=\"true\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC320_2}\" targetsdk=\"3.0.4\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC320_2}\" targetsdk=\"3.0.5\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC320_2}\" targetsdk=\"3.1.0\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- Nested sources with per-path includes/excludes -->\n    <target name=\"test-nested-sources\" depends=\"jcpro\">\n        <javacard>\n            <cap jckit=\"${JC320_2}\" aid=\"010203040506\">\n                <applet class=\"testapplets.empty.Empty\"/>\n                <sources path=\"src/testapplets/empty\"/>\n            </cap>\n        </javacard>\n    </target>\n</project>\n"
  },
  {
    "path": "tests-fail.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project basedir=\".\" default=\"fail\" name=\"ant-javacard failing tests\">\n    <import file=\"kits.xml\"/>\n\n    <!-- Build test applets -->\n    <target name=\"fail\" depends=\"jcpro,fail-sdks\"/>\n\n    <!-- Different SDK-s-->\n    <target name=\"fail-sdks\">\n        <javacard>\n            <!-- JC 3.2.0 -->\n            <cap jckit=\"${JC320_2}\" sources=\"src/testapplets/fail\" ints=\"false\">\n                <applet class=\"testapplets.fail.Fail\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n</project>\n"
  },
  {
    "path": "tests.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project basedir=\".\" default=\"test\" name=\"ant-javacard tests\">\n    <include file=\"kits.xml\"/>\n    <!-- Build test applets -->\n    <target name=\"test\" depends=\"jcpro,test-library-user,test-multiapp,test-no-output,test-sdks,test-stringdef,test-targetsdk,test-oldcross\"/>\n    <!-- Different SDK-s-->\n    <target name=\"test-sdks\">\n        <javacard>\n            <!-- JC 3.0.4 -->\n            <cap jckit=\"${JC304}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <!-- JC 3.0.1 -->\n            <cap jckit=\"${JC303}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC222}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC212}\" sources=\"src/testapplets/empty\" verify=\"false\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap debug=\"true\" jckit=\"${JC221}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC305}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC305_2}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC305_1}\" sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC304}\" sources=\"src/testapplets/integer\" ints=\"true\">\n                <applet class=\"testapplets.integer.EmptyInt\" aid=\"0102030405060708\"/>\n            </cap>\n            <cap jckit=\"${JC211}\" sources=\"src/testapplets/empty\" verify=\"false\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- Output file generation -->\n    <target name=\"test-no-output\" depends=\"jcpro\">\n        <javacard jckit=\"${JC304}\">\n            <cap sources=\"src/testapplets/empty\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- Library -->\n    <target name=\"test-library\" depends=\"jcpro\">\n        <javacard jckit=\"${JC305}\">\n            <cap targetsdk=\"${JC222}\" sources=\"src/testapplets/library\" package=\"testapplets.library\" aid=\"01020304050607\" export=\"testlib\" version=\"0.1\"/>\n        </javacard>\n    </target>\n    <!-- JC 2.2.2 with 3.0.4 library -->\n    <target name=\"test-library-user\" depends=\"jcpro,test-library\">\n        <javacard>\n            <cap jckit=\"${JC222}\" sources=\"src/testapplets/libraryuser\">\n                <applet class=\"testapplets.libraryuser.LibraryUser\" aid=\"0102030405060708\"/>\n                <import jar=\"testlib/library.jar\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- JC 3.0.4 multiapp -->\n    <target name=\"test-multiapp\" depends=\"jcpro\">\n        <javacard>\n            <cap jckit=\"${JC303}\" output=\"Multiapp.cap\" sources=\"src/testapplets/multiapp\" aid=\"010203040506\">\n                <applet class=\"testapplets.multiapp.First\" aid=\"01020304050607\"/>\n                <applet class=\"testapplets.multiapp.Second\" aid=\"0102030405060708\"/>\n                <import jar=\"testlib/library.jar\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- Stringdefs -->\n    <target name=\"test-stringdef\" depends=\"jcpro\">\n        <javacard>\n            <cap jckit=\"${JC304}\" output=\"StringDefs.cap\" sources=\"src/testapplets/stringdefs\" aid=\"010203040506\">\n                <applet class=\"testapplets.stringdefs.Empty\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- targetsdk -->\n    <target name=\"test-targetsdk\" depends=\"jcpro\">\n        <javacard>\n            <cap jckit=\"${JC305}\" targetsdk=\"${JC221}\" sources=\"src/testapplets/empty\" aid=\"010203040506\">\n                <applet class=\"testapplets.empty.Empty\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- javaversion -->\n    <target name=\"test-javaversion\" depends=\"jcpro\">\n        <javacard>\n            <cap jckit=\"${JC304}\" javaversion=\"1.6\" sources=\"src/testapplets/empty\" aid=\"010203040506\">\n                <applet class=\"testapplets.empty.Empty\"/>\n            </cap>\n        </javacard>\n    </target>\n    <!-- Cross-compile against old SDK -->\n    <target name=\"test-oldcross\" depends=\"jcpro\">\n        <javacard jckit=\"${JC305}\">\n            <!-- Verification is supported for 2.2.1 and upwards-->\n            <cap targetsdk=\"${JC211}\" sources=\"src/testapplets/empty\" verify=\"false\">\n                <applet class=\"testapplets.empty.Empty\" aid=\"0102030405060708\"/>\n            </cap>\n        </javacard>\n    </target>\n</project>\n"
  }
]