diff --git a/.github/workflows/gradle-release.yml b/.github/workflows/gradle-release.yml new file mode 100644 index 00000000000..90bc5907ab7 --- /dev/null +++ b/.github/workflows/gradle-release.yml @@ -0,0 +1,106 @@ +name: Create Release + +on: + push: + tags: + - "v*.*.*" + +permissions: + contents: write + +jobs: + create_release: + name: Create Release + runs-on: ubuntu-latest + steps: + - name: Create Release + id: create_release + uses: softprops/action-gh-release@v2 + with: + name: ${{ github.ref_name }} + draft: false + prerelease: false + generate_release_notes: false + + build_release: + name: Build Release + needs: create_release + strategy: + matrix: + os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest] + include: + - os: ubuntu-latest + release_suffix: ubuntu + - os: ubuntu-24.04-arm + release_suffix: ubuntu-arm + - os: macos-latest + release_suffix: mac + - os: windows-latest + release_suffix: windows + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: gradle + cache-dependency-path: | + build.gradle + code/gradle/autobuild.gradle + code/gradle/distribution.gradle + code/gradle/release.gradle + code/gradle/reporting.gradle + code/gradle/plugins.gradle + + # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. + # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + cache-disabled: false + cache-read-only: false + cache-overwrite-existing: true + + - uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/build/jre + ${{ github.workspace }}/build/libs + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ matrix.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + ${{ matrix.os }}-gradle + + - name: Build the image + if: success() + run: ./gradlew jpackage + + - name: Upload release assets for macos + uses: actions/upload-artifact@v4 + if: matrix.os == 'macos-latest' + with: + name: ${{ matrix.os }} + path: ${{ github.workspace }}/build/jpackage/*.dmg + + - name: Upload release assets for ubuntu + uses: actions/upload-artifact@v4 + if: matrix.os == 'ubuntu-latest' + with: + name: ${{ matrix.os }} + path: ${{ github.workspace }}/build/jpackage/*.deb + + - name: Upload release assets for windows + uses: actions/upload-artifact@v4 + if: matrix.os == 'windows-latest' + with: + name: ${{ matrix.os }} + path: ${{ github.workspace }}/build/jpackage/*.msi + + - name: Release - ${{ matrix.os }} + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ needs.create_release.outputs.tag-name }} + files: ${{ github.workspace }}/build/jpackage/pcgen-*.* diff --git a/.github/workflows/gradle-test.yml b/.github/workflows/gradle-test.yml new file mode 100644 index 00000000000..316dd7f991f --- /dev/null +++ b/.github/workflows/gradle-test.yml @@ -0,0 +1,91 @@ +# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle + +name: Build PCGen with Gradle + +on: + pull_request: + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: gradle + cache-dependency-path: | + build.gradle + code/gradle/autobuild.gradle + code/gradle/distribution.gradle + code/gradle/release.gradle + code/gradle/reporting.gradle + code/gradle/plugins.gradle + + # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. + # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + cache-disabled: false + cache-read-only: false + cache-overwrite-existing: true + + - uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/build/jre + ${{ github.workspace }}/build/libs + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + ${{ runner.os }}-gradle + + - name: Build with Gradle Wrapper + run: ./gradlew build + + - name: Run tests + run: ./gradlew test itest datatest slowtest + + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: | + build/test-results/**/*.xml + build/test-results/**/*.trx + build/test-results/**/*.json + + - name: Run Coverage + run: ./gradlew testCoverage + + - name: Upload Report to artifacts + uses: actions/upload-artifact@v4 + with: + name: testCoverage + path: ${{ github.workspace }}/build/reports/jacoco/testCoverage/html + + - name: Jacoco Report to PR + id: jacoco + uses: madrapps/jacoco-report@v1.7.2 + with: + paths: | + ${{ github.workspace }}/build/reports/jacoco/testCoverage/testCoverage.xml + token: ${{ secrets.GITHUB_TOKEN }} + title: '## :construction: PCGen Code Coverage' + update-comment: true + debug-mode: false + + - name: Get the Coverage info + run: | + echo "Total coverage ${{ steps.jacoco.outputs.coverage-overall }}" + echo "Changed Files coverage ${{ steps.jacoco.outputs.coverage-changed-files }}" diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml deleted file mode 100644 index 829c5554dba..00000000000 --- a/.github/workflows/gradle.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Java CI - -on: [push] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - name: Checkout Code - uses: actions/checkout@v3 - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - distribution: 'temurin' - java-version: '17' - - name: Build with Gradle - run: ./gradlew build diff --git a/.gitignore b/.gitignore index d55f55791dd..9513db7ac5a 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,7 @@ code/build.eclipse/ # IntelliJ IDE .idea/ +.run/ out/ pcgendev.iml pcgen.iml diff --git a/.run/Main.run.xml b/.run/Main.run.xml deleted file mode 100644 index 38778279d24..00000000000 --- a/.run/Main.run.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - \ No newline at end of file diff --git a/PCGen-Formula/code/standards/suppressions.xml b/PCGen-Formula/code/standards/checkstyle-suppressions.xml similarity index 55% rename from PCGen-Formula/code/standards/suppressions.xml rename to PCGen-Formula/code/standards/checkstyle-suppressions.xml index 417315de6d2..8caabe23f62 100644 --- a/PCGen-Formula/code/standards/suppressions.xml +++ b/PCGen-Formula/code/standards/checkstyle-suppressions.xml @@ -1,10 +1,8 @@ - + "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN" + "https://checkstyle.org/dtds/suppressions_1_2.dtd"> - \ No newline at end of file diff --git a/PCGen-Formula/code/standards/checkstyle.xml b/PCGen-Formula/code/standards/checkstyle.xml index bf29e09ecab..61b573a988f 100644 --- a/PCGen-Formula/code/standards/checkstyle.xml +++ b/PCGen-Formula/code/standards/checkstyle.xml @@ -1,6 +1,7 @@ - - + @@ -83,12 +84,8 @@ - - - - + - diff --git a/PCGen-Formula/code/standards/spotbugs_ignore.xml b/PCGen-Formula/code/standards/spotbugs_ignore.xml index a971a85cc42..21362c07cbe 100644 --- a/PCGen-Formula/code/standards/spotbugs_ignore.xml +++ b/PCGen-Formula/code/standards/spotbugs_ignore.xml @@ -9,8 +9,8 @@ - - + + diff --git a/PCGen-Formula/gradle/reporting.gradle b/PCGen-Formula/gradle/reporting.gradle index f0a467db0d2..5da1da56aa2 100644 --- a/PCGen-Formula/gradle/reporting.gradle +++ b/PCGen-Formula/gradle/reporting.gradle @@ -8,7 +8,7 @@ checkstyle { configFile = new File('code/standards/checkstyle.xml') - configProperties = [ "suppressionFile" : project(':').file('code/standards/suppressions.xml')] + configProperties = [ "suppressionFile" : project(':').file('code/standards/checkstyle-suppressions.xml')] ignoreFailures = true showViolations = false sourceSets = [] @@ -31,4 +31,6 @@ spotbugs { toolVersion = "3.1.8" } -task allReports { dependsOn = ['checkstyleMain', 'pmdMain', 'spotbugsMain'] } +tasks.register('allReports') { + dependsOn = ['checkstyleMain', 'pmdMain', 'spotbugsMain'] +} diff --git a/PCGen-base/code/standards/checkstyle.xml b/PCGen-base/code/standards/checkstyle.xml index ce082f322b8..c6f6fad8e15 100644 --- a/PCGen-base/code/standards/checkstyle.xml +++ b/PCGen-base/code/standards/checkstyle.xml @@ -1,6 +1,7 @@ - - + diff --git a/PCGen-base/code/standards/spotbugs_ignore.xml b/PCGen-base/code/standards/spotbugs_ignore.xml index cf5e8286f4b..166b44426f6 100644 --- a/PCGen-base/code/standards/spotbugs_ignore.xml +++ b/PCGen-base/code/standards/spotbugs_ignore.xml @@ -9,8 +9,8 @@ - - + + diff --git a/README.md b/README.md index 57a710b1fe4..040f0b6c335 100644 --- a/README.md +++ b/README.md @@ -58,21 +58,21 @@ It supports numerous game systems, most notably: 1. Run installer and follow instruction - Windows: Open `pcgen-6.09.xx_win_install.exe` - Mac: - - - `dmg`: Open `dmg` and drag into Applications. Right click on `PcGen` and click open. - - - `pkg`: Right click and `pkg` and click open and click `open` on security warning due to application being unsigned. + - - `dmg`: Open `dmg` and drag into Applications. Right-click on `PcGen` and click open. + - - `pkg`: Right-click and `pkg` and click open and click `open` on security warning due to application being unsigned. -1. You should be able to launch PcGen as normal application. - - Mac: You may need to on first launch right click on application and then click `open`. +1. You should be able to launch PcGen as a normal application. + - Mac: You may need to on first launch right-click on application and then click `open`. # PCGen Needs You -PCGen is an open source program driven by contributors, without your help no new fixes or content can be added. -If you can program Java or want to contribute to expanding the book support please consider joining our team. -Many of the original members have become inactive and we need new contributors to keep the project going. +PCGen is an open-source program driven by contributors; without your help, no new fixes or content can be added. +If you can program Java or want to contribute to expanding the book support, please consider joining our team. +Many of the original members have become inactive, and we need new contributors to keep the project going. To join our group: - Join our [Discord](https://discord.gg/M7GH5BS) -- Post in the volunteers channel to get access to the [Slack](https://slack.com). A senior member will add you by email. +- Post in the volunteer channel to get access to the [Slack](https://slack.com). A senior member will add you by email. - Make an account on the [JIRA] bug tracker. See [CODE] and [DATA] issues. Work is tracked here to easily generate release notes. - Review the [Basic Workflow](#basic-workflow) & [Development Setup](#development-setup) below to get started. @@ -82,28 +82,28 @@ Browse it when you have time, it can provide some insight into certain parts of # PCGen LST Tutorial Andrew has made a series of videos [explaining the LST files](https://www.youtube.com/watch?v=LhGkqdXNtOw&list=PLLa5A1qjBOPekqEC_R9BAZW-8q5IT-klM). -These are mainly targetted at new DATA contributors adding new books/content and fixing bugs. -You can of course ask questions in the discord or slack if you are unsure. +These are mainly targeted at new DATA contributors adding new books/content and fixing bugs. +You can, of course, ask questions in the discord or Slack if you are unsure. Programmers may want to review these if they work on the LST parsing or related systems. # Basic Workflow -1. Get a bug from [JIRA] primarily from the [CODE] or [DATA] sections. Alternatively if you want to propose a new feature/change, make a JIRA entry to track it. +1. Get a bug from [JIRA] primarily from the [CODE] or [DATA] sections. Alternatively, if you want to propose a new feature/change, make a JIRA entry to track it. 1. Create a branch in your fork of [PCGen] during development. It is good to name branches after ticket numbers, like fix_code_3444 or fix_data_3322. -1. Work until the feature or bug is finished. Add tests if needed, especially if new code added. -1. Push all work up into your copy of [PCGen]. Try to ensure build passes BEFORE submitting a pull request. -1. Submit pull request from your fork to master and respond to review by members. -1. Go back to first step. +1. Work until the feature or bug is finished. Add tests if needed, especially if new code is added. +1. Push all work up into your copy of [PCGen]. Try to ensure the build passes BEFORE submitting a pull request. +1. Submit a pull request from your fork to master and respond to a review by members. +1. Go back to the first step. # Development Setup These steps will guide you to a basic setup for development. -These steps should work for Linux, Mac or Windows. Where steps differ it will be highlighted. -If you have trouble, feel free to ask in the discord or slack once you have joined. +These steps should work for Linux, Mac or Windows. Where steps differ, it will be highlighted. +If you have trouble, feel free to ask in the Discord or Slack once you have joined. ### Running Commands Anything `written like this` should be executed in a terminal. -For Windows this means opening the start menu and typing cmd.exe or Powershell. The latter is more modern if available. +For Windows this means opening the start menu and typing cmd.exe or PowerShell. The latter is more modern if available. For Linux or Mac, whatever default terminal you have is fine. ### Install Java @@ -128,17 +128,17 @@ You can install git on debian machines: On Windows, [Git For Windows](https://gitforwindows.org) is a good choice. Download and install. Be sure to install both the GUI & command line version. The default options are fine. -If you do not know about git, reading the first 3 or 4 chapters of [Pro Git](https://git-scm.com/book/en/v2) -will go a long way. It is designed about command line but all principles apply to the GUI version. +If you do not know about git, reading the first three or four chapters of [Pro Git](https://git-scm.com/book/en/v2) +will go a long way. It is designed about command line, but all principles apply to the GUI version. ### Fork and Clone PCGen -Log in to github and go to [PCGen] in your browser. +Log in to GitHub and go to [PCGen] in your browser. Fork the project to have your own copy. -Clone the fork locally, if you use ssh for instance it should be: +Clone the fork locally, if you use ssh for instance, it should be: git clone git@github.com:USERNAME/pcgen.git -Where USERNAME is your github username. +Where USERNAME is your GitHub username. This can be done on the command line, or else open the git GUI and clone from there. ### Stay Up To Date @@ -147,39 +147,39 @@ Run the following command: git remote add upstream https://github.com/PCGen/pcgen -This sets up the project for upstream rebasing, to keep you level with changes. -You can rebase the master with latest changes with the following. It can be done from GUI as well. +This sets up the project for upstream rebasing to keep you level with changes. +You can rebase the master with the latest changes with the following. It can be done from GUI as well. git checkout master && git fetch upstream && git rebase master ### Get an IDE This step is optional. You are free to program in what you prefer, these are several popular IDEs for Java. -If you are new we would suggest IntelliJ. Follow download/setup instructions then continue. -These IDEs have git and gradle plugins either out of box or that can be installed. +If you are new, we would suggest IntelliJ. Follow download/setup instructions, then continue. +These IDEs have git and gradle plugins either out of the box or that can be installed. - [IntelliJ Community](https://www.jetbrains.com/idea) - [Eclipse](https://www.eclipse.org) - [Netbeans](https://netbeans.org) Once setup, open your IDE of choice. You should be able to import the cloned fork. -Import the [PCGen] fork as a gradle project. From this point, you can work on the project. +Import the [PCGen] fork as a Gradle project. From this point, you can work on the project. For IntelliJ Community do: File > New > Project from Existing Sources ... All of these IDEs have git and gradle plugins that can be used instead of commands. # Essential Gradle Tasks -This is a __quick__ rundown on gradle. You can get more information from [gradle docs](https://gradle.org/guides). +This is a __quick__ rundown on Gradle. You can get more information from [gradle docs](https://gradle.org/guides). Gradle is like make, it allows you to run commands to build projects. The tasks can depend on one another and will ensure all dependencies are met before running. These commands will be the same if you use a GUI to execute them. -Note: `./gradlew` indicates you are executing the gradle wrapper command binary that comes with PCGen's source tree. -This will automatically download and use the latest version of gradle. If you have gralde installed, you just +Note: `./gradlew` indicates you are executing the Gradle wrapper command binary that comes with PCGen's source tree. +This will automatically download and use the latest version of Gradle. If you have Gradle installed, you just substitute `./gradlew` for `gradle` on the command line. ### See All Available Commands ./gradlew tasks -### Just (Re)Compile Java +### (Re)Compile Java ./gradlew compileJava ### Build All Required Files @@ -193,7 +193,7 @@ substitute `./gradlew` for `gradle` on the command line. ### Run Full Test Suite Do this primarily __before__ pull requests. -This is almost exactly the command Travis runs to verify, if it fails locally you will fail the build and your PR will not be merged. +This is almost exactly the command Travis runs to verify, if it fails locally you will fail the build, and your PR will not be merged. ./gradlew clean build copyToOutput test compileSlowtest datatest pfinttest allReports buildDist diff --git a/appveyor.yml b/appveyor.yml index f058d367c3d..05bc8305779 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,7 +5,7 @@ pull_requests: nuget: disable_publish_on_pr: true - + install: - cmd: | choco install gradle nsis @@ -13,22 +13,22 @@ build_script: gradle build --info --no-daemon test_script: - gradle test compileSlowtest datatest pfinttest buildDist buildNsis --info --no-daemon + gradle test compileSlowtest datatest pfinttest buildDist --info --no-daemon environment: matrix: - JAVA_HOME: C:\Program Files\Java\jdk11 - + artifacts: - path: '**/release/*.exe' name: PCGen Installer - + - path: '**/build/distributions/*.zip' name: PCGen Zip Distribution - path: '**/build/distributions/*.tar' name: PCGen Tar Distribution - + matrix: fast_finish: true diff --git a/build-gradle.xml b/build-gradle.xml deleted file mode 100644 index ef523e76e59..00000000000 --- a/build-gradle.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/build.gradle b/build.gradle index 9bb7667a8be..e8093cd5c59 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ * Run the character integration tests: gradle inttest */ -// import Ant helper static values to differ system families +import groovy.json.JsonSlurper import org.apache.commons.lang3.StringUtils import org.apache.tools.ant.filters.FixCrLfFilter @@ -26,25 +26,20 @@ plugins { id 'application' // Creating application bundles id 'build-dashboard' // Produces a build report id 'checkstyle' // Checkstyle for Java, configured further below - id 'com.github.spotbugs' version '6.4.7' // Spotbugs for Java - id 'java' // Core java / javac + id 'com.github.spotbugs' version '6.4.7' // Spotbugs for Java id 'maven-publish' // Publishing to Maven Central id 'pmd' // PMD for Java, configured further below id 'idea' // For IntelliJ IDEA users id 'de.undercouch.download' version '5.6.0' // Shows download percentage - id 'edu.sc.seis.launch4j' version '4.0.0' // Creates launch4j id 'com.github.ben-manes.versions' version '0.53.0' // Checks versions for plugins and dependencies id 'com.dorongold.task-tree' version '4.0.1' // Prints the task dependency tree id 'org.openjfx.javafxplugin' version '0.1.0' // JavaFX support id 'org.beryx.runtime' version '2.0.1' // Creates custom runtimes + id 'jacoco' // Code coverage } -/** - * Set the version and the modules we want from JavaFX (not everything) - */ -javafx { - version = "21" - modules = [ 'javafx.controls', 'javafx.web', 'javafx.swing', 'javafx.fxml', 'javafx.graphics' ] +jacoco { + toolVersion = '0.8.14' } // Set the groupId as it helps with Maven @@ -56,13 +51,6 @@ description = """PCGen""" // Default task if you just run ./gradlew defaultTasks 'build' -// Configure Java, in particular the version to test/compile/run with -java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } -} - // Define properties for the build (directories) ext { // Where code gets compiled to @@ -80,8 +68,31 @@ ext { // Installers are placed here releaseDir = layout.projectDirectory.dir("build/release") - // JavaFX version - javaFXVersion = "21.0.5" + // Java version (always latest release). It will affect JavaFX version too. + javaVersion = 25 + + latestJavaVersion = { majorJavaVersion -> + def uri = new URI("https://api.adoptium.net/v3/assets/feature_releases/${majorJavaVersion}/ga?architecture=x64&page=0&page_size=1&project=jdk&sort_order=DESC&vendor=eclipse") + def parsedJson = new JsonSlurper().parse(uri.toURL()) + + return parsedJson.first() + .version_data.with { [major, minor, security].join('.') } + }(javaVersion) +} + +// Configure Java, in particular the version to test/compile/run with +java { + toolchain { + languageVersion = JavaLanguageVersion.of(javaVersion) + } +} + +/** + * Set the version and the modules we want from JavaFX (not everything) + */ +javafx { + version = javaVersion + modules = [ 'javafx.controls', 'javafx.swing', 'javafx.fxml', 'javafx.graphics', 'javafx.web' ] } application { @@ -107,16 +118,19 @@ repositories { //} ivy { // TODO Enable HTTPS (e.g., add letsencrypt) for that location - name "fileRepo" - url 'http://pc-gen.org/librepo/' + name = 'fileRepo' + url = 'http://pc-gen.org/librepo/' allowInsecureProtocol = true } // Use Maven plugin to reference freehep (https://java.freehep.org/) artifact repository // TODO Which libs do we pull from here? + /* maven { name = 'free' url = 'https://java.freehep.org/maven2' } + + */ maven { name = 'local' url = 'installers' @@ -125,7 +139,7 @@ repositories { // TODO Which libs do we pull from here? maven { name = 'jboss' - url "https://repository.jboss.org/nexus/content/repositories/thirdparty-releases/" + url = 'https://repository.jboss.org/nexus/content/repositories/thirdparty-releases/' } } @@ -217,46 +231,40 @@ tasks.register("cleanMasterSheets", Delete) { * https://docs.gradle.org/current/userguide/dependency_downgrade_and_exclude.html */ dependencies { - implementation group: 'commons-io', name: 'commons-io', version: '2.21.0' - - implementation group: 'commons-io', name: 'commons-io', version:'2.21.0' - - implementation group: 'org.springframework', name: 'spring-web', version:'6.2.12' - implementation group: 'org.springframework', name: 'spring-beans', version:'6.2.12' - implementation group: 'org.springframework', name: 'spring-core', version:'6.2.12' - implementation group: 'org.apache.commons', name: 'commons-lang3', version:'3.20.0' - implementation group: 'xalan', name: 'serializer', version: '2.7.3' - implementation('org.apache.xmlgraphics:fop:2.11') - { + implementation 'commons-io:commons-io:2.21.0' + implementation 'org.springframework:spring-web:6.2.12' + implementation 'org.springframework:spring-beans:6.2.12' + implementation 'org.springframework:spring-core:6.2.12' + implementation 'org.apache.commons:commons-lang3:3.20.0' + implementation 'xalan:serializer:2.7.3' + implementation('org.apache.xmlgraphics:fop:2.11') { exclude group: 'xml-apis', module: 'xml-apis' } - implementation group: 'org.apache.commons', name: 'commons-collections4', version: '4.5.0' - implementation group: 'org.scijava', name: 'jep', version: '2.4.2' - implementation group: 'org.freemarker', name: 'freemarker', version: '2.3.34' - implementation group: 'org.jdom', name: 'jdom2', version: '2.0.6.1' - implementation('xalan:xalan:2.7.3') - { + implementation 'org.apache.commons:commons-collections4:4.5.0' + implementation 'org.scijava:jep:2.4.2' + implementation 'org.freemarker:freemarker:2.3.34' + implementation 'org.jdom:jdom2:2.0.6.1' + implementation('xalan:xalan:2.7.3') { exclude group: 'xml-apis', module: 'xml-apis' } - implementation group: 'net.sourceforge.argparse4j', name: 'argparse4j', version: '0.9.0' - implementation group: 'org.xmlunit', name: 'xmlunit-core', version: '2.11.0' - implementation group: 'org.controlsfx', name: 'controlsfx', version: '11.2.2' - - implementation group: 'net.sourceforge.pcgen', name: 'PCGen-base', version: '1.0.170' + implementation 'net.sourceforge.argparse4j:argparse4j:0.9.0' + implementation 'org.xmlunit:xmlunit-core:2.11.0' + implementation 'org.controlsfx:controlsfx:11.2.2' + implementation 'net.sourceforge.pcgen:PCGen-base:1.0.170' // The latest Base, but pcgen core would need refactoring to support it. //implementation group: 'net.sourceforge.pcgen', name: 'PCGen-base', version:'1.0.237' // Use this if you're working from your local PCGen Base //implementation files("../pcgen-base/PCGen-base/build/libs/PCgen-base-1.0.jar") - implementation group: 'net.sourceforge.pcgen', name: 'PCGen-Formula', version: '1.0.200' + implementation 'net.sourceforge.pcgen:PCGen-Formula:1.0.200' // The latest Formula, but pcgen core would need refactoring to support it. //implementation group: 'net.sourceforge.pcgen', name: 'PCGen-Formula', version:'1.0.266' // Use if you're working from your local PCGen Formula //implementation files("../pcgen-formula/PCGen-formula/build/libs/PCgen-formula-1.0.jar") - compileOnly group: 'org.jetbrains', name: 'annotations', version:'26.0.2-1' - compileOnly group: 'com.yuvimasory', name: 'orange-extensions', version: '1.3.0' - compileOnly group: 'com.github.spotbugs', name: 'spotbugs-annotations', version: '4.9.8' + compileOnly 'org.jetbrains:annotations:26.0.2-1' + compileOnly 'com.yuvimasory:orange-extensions:1.3.0' + compileOnly 'com.github.spotbugs:spotbugs-annotations:4.9.8' testImplementation group: 'org.junit.platform', name: 'junit-platform-runner', version: '1.14.1' testImplementation group: 'org.junit.platform', name: 'junit-platform-launcher', version: '1.14.1' @@ -270,37 +278,10 @@ dependencies { testImplementation group: 'org.xmlunit', name: 'xmlunit-matchers', version: '2.11.0' spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.14.0' - pmd 'net.sourceforge.pmd:pmd-ant:7.19.0' + pmd 'net.sourceforge.pmd:pmd-ant:7.19.0' // workaround: https://github.com/pmd/pmd/issues/4554 pmd 'net.sourceforge.pmd:pmd-java:7.19.0' } -// Properties to support Ant builds -def antPluginTasks = [] -ant.importBuild('build-gradle.xml') { - def (projectName, taskName) = it.tokenize('.') - - if (taskName == null && projectName.startsWith("jar") && - (projectName.endsWith("plugins") || projectName.endsWith("plugin"))) { - antPluginTasks.push(projectName) - } - return it -} -ant.properties['src.java.dir']="code/src/java" -ant.properties['build.classes.dir']="build/classes/java/main" - -// All plugins depend on compiled .class files -antPluginTasks.each { - tasks.named(it) { - dependsOn tasks.named("compileJava") - } -} - -// Configuration for creating JARs from all of the plugins -configure('jar-all-plugins') { - group = BasePlugin.BUILD_GROUP // Or use 'build' - description = 'Create the plugin jars' -} - ext { classpath = "" configurations.runtimeClasspath.each { lib -> classpath += " libs/${lib.name} "} @@ -308,15 +289,17 @@ ext { jar { // Pick up some configuration to JAR up the plugins - it.dependsOn 'jar-all-plugins' + it.dependsOn "jarAllPlugins" // If we detect duplicate JARs ignore the subsequent ones duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Create the manifest for the main JAR manifest { - attributes 'Implementation-Title': 'PCGen', 'Implementation-Version': archiveVersion, - 'Main-Class': 'pcgen.system.Main', 'Class-Path': classpath + attributes 'Implementation-Title': 'PCGen', + 'Implementation-Version': archiveVersion, + 'Main-Class': 'pcgen.system.Main', + 'Class-Path': classpath } from { @@ -330,29 +313,29 @@ jar { * Create a Java runtime built from Java modules */ runtime { - options = ['--compress', 'zip-6'] // Equivalent to the old '2' value pre Java 21 + options = [] modules = [ - 'jdk.httpserver', - 'java.management', - 'java.rmi', - 'java.desktop', - 'java.xml', - 'java.sql', - 'jdk.unsupported', - 'java.prefs', - 'java.logging', - 'java.naming', - 'jdk.xml.dom', - 'jdk.unsupported.desktop', - 'java.datatransfer', - 'java.scripting', - 'jdk.jfr', - 'jdk.jsobject', - 'javafx.controls', - 'javafx.web', - 'javafx.swing', - 'javafx.fxml', - 'javafx.graphics' + 'jdk.httpserver', + 'java.management', + 'java.rmi', + 'java.desktop', + 'java.xml', + 'java.sql', + 'jdk.unsupported', + 'java.prefs', + 'java.logging', + 'java.naming', + 'jdk.xml.dom', + 'jdk.unsupported.desktop', +// 'java.datatransfer', + 'java.scripting', + 'jdk.jfr', + 'jdk.jsobject', + 'javafx.controls', + 'javafx.web', + 'javafx.swing', + 'javafx.fxml', + 'javafx.graphics' ] // We support Mac/Win/Linux x86-64, Mac/Linux aarch64 @@ -368,15 +351,14 @@ runtime { installerOptions = ["--app-version", "${version.replaceAll('-SNAPSHOT', '')}", "--license-file", licenseFile.asFile.absolutePath] def hostArchitecture = System.getProperty("os.arch") - def osFamily = (Os.isFamily(Os.FAMILY_MAC) - ? "mac" - : (Os.isFamily(Os.FAMILY_UNIX) - ? "linux" - : (Os.isFamily(Os.FAMILY_WINDOWS) ? "windows" : "unknown"))) + def osFamily = + Os.isFamily(Os.FAMILY_MAC) ? "mac" : + Os.isFamily(Os.FAMILY_UNIX) ? "linux" : + Os.isFamily(Os.FAMILY_WINDOWS) ? "windows" : "unknown" def targetPlatform = ["x86_64" : "x64", "amd64" : "x64", "aarch64" : "aarch64"] - // formats a string such as "mac-aarch64", or "linux-x64" + // formats a string such as "mac -aarch64", or "linux-x64" targetPlatformName = "${osFamily}-${targetPlatform.get(hostArchitecture, "NOT SUPPORTED")}" if (osFamily == "mac") { @@ -405,7 +387,7 @@ tasks.named("jpackageImage") { if (Os.isFamily(Os.FAMILY_MAC)) { into layout.buildDirectory.dir("jpackage/PcGen.app/Contents/app") } else { - into layout.buildDirectory.dir("jpackage/PcGen/bin") + into layout.buildDirectory.dir("jpackage/PcGen") } } @@ -414,7 +396,9 @@ tasks.named("jpackageImage") { copy { from layout.projectDirectory.dir("installers/mac-installer") include "MacDirLauncher" - fileMode 0755 + filePermissions { + unix(0755) + } into layout.buildDirectory.dir("jpackage/PcGen.app/Contents/MacOS") } ant.replace(file: layout.buildDirectory.file("jpackage/PcGen.app/Contents/Info.plist").get(), @@ -441,13 +425,12 @@ tasks.register("converterJar", Jar) { include 'none' } -artifacts { - archives converterJar +tasks.named("assemble") { + dependsOn converterJar } tasks.register("copyToLibs", Copy) { - dependsOn startScripts, distTar, distZip, installDist - mustRunAfter createExe + dependsOn startScripts, installDist doFirst { println("IN copyToLibs") @@ -458,7 +441,7 @@ tasks.register("copyToLibs", Copy) { } tasks.register("copyToOutput", Copy) { - dependsOn copyToLibs, createExe, converterJar, jar + dependsOn copyToLibs, converterJar, jar doFirst { println("IN copyToOutput") @@ -466,7 +449,6 @@ tasks.register("copyToOutput", Copy) { from layout.buildDirectory.file("libs/pcgen-${version}.jar"), layout.buildDirectory.file("libs/pcgen-${version}-batch-convert.jar"), - layout.buildDirectory.file("launch4j/pcgen.exe"), layout.projectDirectory.file("code/pcgen.bat") from(layout.projectDirectory.file("code/pcgen.sh")) { filter(FixCrLfFilter, eol:FixCrLfFilter.CrLf.newInstance("lf")) @@ -529,9 +511,12 @@ tasks.named("build") { } tasks.register("downloadJRE") { - def major = 21 + notCompatibleWithConfigurationCache("Gradle download extension not compatible") + + def major = project.ext.javaVersion def archs = ['x64', 'aarch64'] def osList = ['windows', 'mac', 'linux'] + def baseJreDir = project.ext.jresDir doLast { println("Downloading JDKs for use in creating the runtime bundles.") @@ -546,12 +531,12 @@ tasks.register("downloadJRE") { if (arch == "aarch64" && os == "windows") return def url = "https://api.adoptium.net/v3/binary/latest/${major}/ga/${os}/${arch}/jdk/hotspot/normal/eclipse" - def jreDownloadDest = layout.projectDirectory.file("jre/${os}/jre_${arch}.${extension}") - def jreDir = layout.projectDirectory.dir("jre/${os}/jre_${arch}").asFile + def jreDownloadDest = baseJreDir.file("${os}/jre_${arch}.${extension}") + def jreDir = baseJreDir.dir("${os}/jre_${arch}").asFile def jreRelease = (os == "mac" - ? layout.projectDirectory.dir("jre/${os}/jre_${arch}/Contents/Home/release").asFile - : layout.projectDirectory.dir("jre/${os}/jre_${arch}/release").asFile) - def outputDir = layout.projectDirectory.dir("jre/${os}/jre_${arch}") + ? baseJreDir.dir("${os}/jre_${arch}/Contents/Home/release").asFile + : baseJreDir.dir("${os}/jre_${arch}/release").asFile) + def outputDir = baseJreDir.dir("${os}/jre_${arch}") def shouldDownloadJDK = true try { @@ -570,7 +555,7 @@ tasks.register("downloadJRE") { return } else if (jreDir.exists()) { println "Delete the previously downloaded JDK at ${jreDir} as outdated. JavaFX modules will be deleted from jdkDir/jmods." - project.delete(jreDir) + delete(jreDir) } println("Downloading Java ${major} ${arch} for ${os} from ${url}") @@ -580,11 +565,11 @@ tasks.register("downloadJRE") { overwrite false } - layout.projectDirectory.dir("jre/${os}") + baseJreDir.dir("${os}") .asFileTree .matching { include "*.${extension}" } .each { simLib -> - def archFile = layout.projectDirectory.file("jre/${os}/${simLib.name}") + def archFile = baseJreDir.file("${os}/${simLib.name}") copy { println "* Unpack $simLib.name to ${outputDir}" @@ -602,7 +587,7 @@ tasks.register("downloadJRE") { } } - project.delete(archFile) + delete(archFile) } file("${projectDir}/jre/${os}/jre_${arch}").list().each { f -> @@ -616,13 +601,16 @@ tasks.register("downloadJRE") { } tasks.register("downloadJavaFXModules") { + notCompatibleWithConfigurationCache("Gradle download extension not compatible") + mustRunAfter downloadJRE // We support Windows/Mac/Linux - x64, and Mac/Linux - aarch64 - // (JavaFX doesn't support Linux/aarch64 in 21 LTS, use 21.0.1 instead) - def major = project.ext.javaFXVersion + def major = project.javaVersion def archs = ['x64', 'aarch64'] def osList = ["windows", "mac", "linux"] + def baseJreDir = project.ext.jresDir + def projectDirPath = projectDir.absolutePath inputs.property("major", major) @@ -640,20 +628,17 @@ tasks.register("downloadJavaFXModules") { archAppend = "-x64" } else if (arch == "aarch64" && os != "windows") { archAppend = "-aarch64" - if (os == "linux") { - major = "21.0.1" // TODO other versions don't supply aarch64 for Linux, use 21.0.1 instead - } } else { return } // URLs look like this: https://download2.gluonhq.com/openjfx/21.0.2/openjfx-21.0.2_osx-aarch64_bin-jmods.zip def url = "https://download2.gluonhq.com/openjfx/${major}/openjfx-${major}_${osPackage}${archAppend}_bin-jmods.zip" - def jmodsZip = layout.projectDirectory.file("jre/${os}/jmods${arch}.zip") + def jmodsZip = baseJreDir.file("${os}/jmods${arch}.zip") // If we don't already have the jmods, download them and unzip them to the correct place. if (!jmodsZip.asFile.exists()) { - println("Downloading JavaFX mods ${major} ${arch} for ${os} from ${url} to ${projectDir}/jre/${os}/") + println("Downloading JavaFX mods ${major} ${arch} for ${os} from ${url} to ${projectDirPath}/jre/${os}/") download.run { src url @@ -664,8 +649,8 @@ tasks.register("downloadJavaFXModules") { copy { def zipFile = jmodsZip def outputDir = (os == "mac") - ? "${projectDir}/jre/${os}/jre_${arch}/Contents/Home/jmods" - : "${projectDir}/jre/${os}/jre_${arch}/jmods" + ? "${projectDirPath}/jre/${os}/jre_${arch}/Contents/Home/jmods" + : "${projectDirPath}/jre/${os}/jre_${arch}/jmods" println "Unzipping ${zipFile} to ${outputDir}" from zipTree(zipFile) @@ -697,7 +682,7 @@ tasks.register("downloadJavaFXLocal", Download) { .filter(i -> Os.isFamily(i.key)) .map { it.value } .findFirst() - .orElseThrow { new GradleException("Unsupported OS detected: '${osName}'. Supported families are: ${Os.FAMILY_WINDOWS}, ${Os.FAMILY_MAC} and ${Os.FAMILY_UNIX}") } + .orElseThrow { new GradleException("Unsupported OS detected: '${osName}'. Supported families are: ${supportedOS.keySet().join(", ")}") } def supportedArch = ["x86_64" : "-x64", "amd64" : "-x64", @@ -710,12 +695,12 @@ tasks.register("downloadJavaFXLocal", Download) { } doFirst { - println("Downloading JavaFX SDK for use in local testing.") + println("Downloading JavaFX SDK for use in local testing for ${currentOS} ${hostArchitecture}.") } - def fileName = "openjfx-${project.ext.javaFXVersion}_${currentOS}${archAppend}_bin-sdk.zip" + def fileName = "openjfx-${project.ext.latestJavaVersion}_${currentOS}${archAppend}_bin-sdk.zip" - src "https://download2.gluonhq.com/openjfx/${project.ext.javaFXVersion}/${fileName}" + src "https://download2.gluonhq.com/openjfx/${project.ext.latestJavaVersion}/${fileName}" dest layout.projectDirectory.file("mods/${fileName}") overwrite false useETag true @@ -748,8 +733,27 @@ tasks.named("jre") { dependsOn downloadJRE, downloadJavaFXModules } +tasks.register("testCoverage", JacocoReport) { + dependsOn test + group = "Reporting" + description = "Generate Jacoco coverage reports for the test build." + + reports { + html.required.set(true) + xml.required.set(true) + } + + classDirectories.from = fileTree(dir: layout.buildDirectory.dir("classes/java/main")) + + sourceDirectories.from = files([ + "$project.projectDir/code/src/java" + ]) + + executionData.from = fileTree(dir: layout.buildDirectory.dir("jacoco")).include("*.exec") +} + tasks.named("test", Test) { - exclude 'pcgen/testsupport/**' + exclude 'test/pcgen/testsupport/**' useJUnitPlatform() } @@ -768,17 +772,14 @@ tasks.register("slowtest", Test) { classpath = sourceSets.slowtest.runtimeClasspath systemProperties['jar.path'] = jar.getArchiveFile().get().getAsFile() forkEvery = 1 - // Exclude the two suites to avoid duplicate test runs. - exclude 'AllJUnitTests.class' - exclude 'pcgen/core/AllJUnitTests.class' } tasks.register("datatest", Test) { dependsOn jar testClassesDirs = sourceSets.slowtest.output.classesDirs classpath = sourceSets.slowtest.runtimeClasspath - include 'pcgen/persistence/lst/DataTest.class' - include 'pcgen/persistence/lst/DataLoadTest.class' + include 'main/pcgen/persistence/lst/DataTest.class' + include 'main/pcgen/persistence/lst/DataLoadTest.class' } tasks.register("inttest", Test) { @@ -786,7 +787,7 @@ tasks.register("inttest", Test) { testClassesDirs = sourceSets.slowtest.output.classesDirs classpath = sourceSets.slowtest.runtimeClasspath forkEvery = 1 - include 'pcgen/inttest/**/*Test.class' + include 'slowtest/pcgen/inttest/**/*Test.class' } tasks.register("sfinttest", Test) { @@ -794,7 +795,7 @@ tasks.register("sfinttest", Test) { testClassesDirs = sourceSets.slowtest.output.classesDirs classpath = sourceSets.slowtest.runtimeClasspath forkEvery = 1 - include 'pcgen/inttest/game_starfinder/*Test.class' + include 'slowtest/pcgen/inttest/game_starfinder/*Test.class' } tasks.register("pfinttest", Test) { @@ -802,7 +803,7 @@ tasks.register("pfinttest", Test) { testClassesDirs = sourceSets.slowtest.output.classesDirs classpath = sourceSets.slowtest.runtimeClasspath forkEvery = 1 - include 'pcgen/inttest/game_pathfinder/*Test.class' + include 'slowtest/pcgen/inttest/game_pathfinder/*Test.class' } tasks.register("rsrdinttest", Test) { @@ -810,7 +811,7 @@ tasks.register("rsrdinttest", Test) { testClassesDirs = sourceSets.slowtest.output.classesDirs classpath = sourceSets.slowtest.runtimeClasspath forkEvery = 1 - include 'pcgen/inttest/game_35e/*Test.class' + include 'slowtest/pcgen/inttest/game_35e/*Test.class' } tasks.register("srdinttest", Test) { @@ -818,7 +819,7 @@ tasks.register("srdinttest", Test) { testClassesDirs = sourceSets.slowtest.output.classesDirs classpath = sourceSets.slowtest.runtimeClasspath forkEvery = 1 - include 'pcgen/inttest/game_3e/*Test.class' + include 'slowtest/pcgen/inttest/game_3e/*Test.class' } tasks.register("msrdinttest", Test) { @@ -826,13 +827,14 @@ tasks.register("msrdinttest", Test) { testClassesDirs = sourceSets.slowtest.output.classesDirs classpath = sourceSets.slowtest.runtimeClasspath forkEvery = 1 - include 'pcgen/inttest/game_modern/*Test.class' + include 'slowtest/pcgen/inttest/game_modern/*Test.class' } allprojects { tasks.withType(Javadoc).configureEach { options.addBooleanOption('Xdoclint:none', true) } + tasks.withType(Test).configureEach { maxHeapSize = "1024m" maxParallelForks = 1 @@ -848,6 +850,7 @@ allprojects { '-Dprism.order=sw', '-Dprism.verbose=true', '-Djavafx.macosx.embedded=true', + '-Djava.security.manager=disallow', "--module-path", layout.projectDirectory.dir("mods/lib"), "--add-modules", "javafx.controls,javafx.web,javafx.swing,javafx.fxml,javafx.graphics", @@ -860,9 +863,11 @@ allprojects { '--add-opens', 'javafx.graphics/com.sun.glass.ui=ALL-UNNAMED', ] } + tasks.withType(JavaCompile).configureEach { dependsOn extractJavaFXLocal options.fork = true + options.release = project.ext.javaVersion doFirst { if (name.toLowerCase().contains("test")) { @@ -949,10 +954,11 @@ apply from: 'code/gradle/distribution.gradle' apply from: 'code/gradle/autobuild.gradle' // depends on distribution.gradle apply from: 'code/gradle/reporting.gradle' apply from: 'code/gradle/release.gradle' +apply from: 'code/gradle/plugins.gradle' tasks.register("allTasks") { - dependsOn build, slowtest, javadoc, buildNsis, allReports - description = "Runs tasks build, slowtest, javadoc, buildNsis and allReports" + dependsOn build, slowtest, javadoc, allReports + description = "Runs tasks build, slowtest, javadoc and allReports" } // TODO This clean is not as nuclear as it perhaps should be. @@ -960,21 +966,5 @@ tasks.register("allTasks") { // you will also need to clean out output/ bin/ // This task is in the end, because other scripts contribute to the building process tasks.named("clean") { - dependsOn tasks.named("clean-plugins"), cleanOutput, cleanJre, cleanMods, cleanNsis, cleanMasterSheets - - doLast { - // Delete a file generated by pluginbuild.xml - project.delete(layout.projectDirectory.file("code/manifest")) - - // Delete plugin folders - project.delete( - layout.projectDirectory.dir("plugins/bonusplugins"), - layout.projectDirectory.dir("plugins/converterplugins"), - layout.projectDirectory.dir("plugins/jepplugins"), - layout.projectDirectory.dir("plugins/lstplugins"), - layout.projectDirectory.dir("plugins/outputplugins"), - layout.projectDirectory.dir("plugins/preplugins"), - layout.projectDirectory.dir("plugins/systemlstplugins") - ) - } + dependsOn cleanPlugins, cleanOutput, cleanJre, cleanMods, cleanMasterSheets } diff --git a/code/gradle/distribution.gradle b/code/gradle/distribution.gradle index 182e5ea4ea4..42964dc8127 100644 --- a/code/gradle/distribution.gradle +++ b/code/gradle/distribution.gradle @@ -98,18 +98,21 @@ application { tasks.named("run") { dependsOn assemble, extractJavaFXLocal + + def modsLibPath = layout.projectDirectory.dir("mods/lib").asFile.absolutePath + // Required to fixed incorrectly added "--module-path" and adds correct modules. doFirst { jvmArgs = ["-ea", // -ea enables assertions "--enable-preview", // enable Java preview features - "--module-path", layout.projectDirectory.dir("mods/lib").asFile.absolutePath, - "--add-modules", "javafx.controls,javafx.web,javafx.swing,javafx.fxml,javafx.graphics" + "--module-path", modsLibPath, + "--add-modules", "javafx.controls,javafx.web,javafx.swing,javafx.fxml,javafx.graphics", + "--enable-native-access", "javafx.graphics,javafx.web" // JDK-8347744 ] } } tasks.named("installDist") { - dependsOn createExe doLast{ delete { delete fileTree(layout.buildDirectory.dir("install/pcgen/lib")) { @@ -119,14 +122,6 @@ tasks.named("installDist") { } } -tasks.named("distTar") { - dependsOn createExe -} - -tasks.named("distZip") { - dependsOn createExe -} - tasks.register("testZip", Zip) { dependsOn converterJar, copyToLibs, copyToOutput archiveClassifier.set('test') @@ -158,7 +153,7 @@ tasks.register("docsZip", Zip) { } tasks.register("programZip", Zip) { - dependsOn converterJar, createExe + dependsOn converterJar archiveClassifier.set('program') into(zipRootFolder) { with programDistsImage diff --git a/code/gradle/plugins.gradle b/code/gradle/plugins.gradle new file mode 100644 index 00000000000..9cc15cd9f99 --- /dev/null +++ b/code/gradle/plugins.gradle @@ -0,0 +1,61 @@ + +def PLUGINS_GROUP = "PCGen plugins" + +def manifestFile = "code/manifest" + +def srcJavaDir = "code/src/java" +def buildClassesDir = "build/classes/java/main" + +def pluginsDir = "${projectDir}/plugins" + +tasks.register("cleanPlugins", Delete) { + description = "Clean all working files" + group = PLUGINS_GROUP + + delete fileTree(dir: pluginsDir, include: "*.jar") +} + +def createJarTask = {taskName, archiveName, description, includePattern -> + tasks.register(taskName, Jar) { + this.description = description + group = PLUGINS_GROUP + dependsOn tasks.named("compileJava") + + archiveFileName = archiveName + manifest { + attributes( + "Implementation-Title": "PCGen ${taskName.replace('jar', '').toLowerCase()} plugins", + "Implementation-Version": project.version, + "Class-Path": "lib/pcgen.jar", + "Built-Date": new Date(), + "Built-JDK": "${System.getProperty('java.vm.version')} (${System.getProperty('java.vendor')})", + "Created-By": "Gradle ${project.gradle.gradleVersion}" + ) + } + + destinationDirectory = file("${pluginsDir}") + + from("${buildClassesDir}") { + include includePattern + } + } +} + +createJarTask("jarExportPlugins", "exportplugins.jar", "Build (Link) Export Token plugin jar files", "plugin/exporttokens/**/*.class") +createJarTask("jarBonusPlugins", "bonusplugins.jar", "Build (Link) plugin Bonus Token jar files", "plugin/bonustokens/**/*.class") +createJarTask("jarLstPlugins", "lstplugins.jar", "Build (Link) plugin LST Token jar files", "plugin/lsttokens/**/*.class") +createJarTask("jarPrePlugins", "preplugins.jar", "Build (Link) Prereq Token plugin jar files", "plugin/pretokens/**/*.class") +createJarTask("jarConverterPlugins", "converterplugins.jar", "Build (Link) Converter plugin jar files", "plugin/converter/**/*.class") +createJarTask("jarModifierPlugins", "modifierplugins.jar", "Build (Link) Lst Modifier Token plugin jar files", "plugin/modifier/**/*.class") +createJarTask("jarPrimitivePlugins", "primitiveplugins.jar", "Build (Link) Lst Primitive Token plugin jar files", "plugin/primitive/**/*.class") +createJarTask("jarQualifierPlugins", "qualifierplugins.jar", "Build (Link) Lst Compatibility Token plugin jar files", "plugin/qualifier/**/*.class") +createJarTask("jarFunctionPlugins", "functionplugins.jar", "Build (Link) Custom Function jar files", "plugin/function/**/*.class") +createJarTask("jarGroupingPlugins", "groupingplugins.jar", "Build (Link) Grouping plugin jar files", "plugin/grouping/**/*.class") +createJarTask("jarJepCommandsPlugins", "jepcommandsplugins.jar", "Build (Link) Jep Command plugin jar files", "plugin/jepcommands/**/*.class") + +tasks.register("jarAllPlugins") { + description = "Create the plugin jars" + group = BasePlugin.BUILD_GROUP + dependsOn jarExportPlugins, jarBonusPlugins, jarLstPlugins, jarPrePlugins, jarConverterPlugins, jarModifierPlugins, + jarPrimitivePlugins, jarQualifierPlugins, jarFunctionPlugins, jarGroupingPlugins, jarJepCommandsPlugins +} diff --git a/code/gradle/release.gradle b/code/gradle/release.gradle index 768dc842053..b31a257b2a4 100644 --- a/code/gradle/release.gradle +++ b/code/gradle/release.gradle @@ -3,7 +3,6 @@ * and production releases are made. * * Usage: gradle prepareRelease - * Usage: gradle buildNsis * Usage: gradle pcgenRelease * * Release script @@ -12,7 +11,7 @@ * b. Commit new version * c. Clean, Build and check * d. Run slow tests - * 2. Manual testing using product of gradle fullZip or gradle buildNsis + * 2. Manual testing using product of gradle fullZip * 3. gradle pcgenRelease * a. Build * b. Assemble distributables @@ -123,76 +122,6 @@ ext { installerVerNum = result[0].toInteger() + "." + result[1].toInteger() + "." + result[2].take(2).toInteger() + ".0" } -tasks.register("layoutNsisBase", Copy) { - dependsOn runtimeZip, createExe, copyToLibs, jar, converterJar - - description="Assemble the required files for all PCGen installations" - - //doFirst { - // Release notes are required! Causes Autobuild Problem - //assert file(releaseNotes).exists() - //} - - into nsisBaseFolder - from(layout.projectDirectory.dir("installers/win-installer/Local")) { - into "../Local" - } - from(layout.projectDirectory.dir("installers/win-installer")) { - into "/docs/acknowledgments/" - include "PCGenLicense.txt" - } - from(layout.projectDirectory) { - include 'logging.properties' - include 'system/**' // Later we want to split out the game modes to optional - include 'docs/**' - include 'preview/**' - include 'characters/*.pcg' - } - from(layout.buildDirectory.dir("launch4j")) { - include 'pcgen.exe' - } - from(layout.buildDirectory.dir("install/pcgen")) { - include 'pcgen.bat' - include 'pcgen' - filter(FixCrLfFilter, eol:FixCrLfFilter.CrLf.newInstance("lf")) - filePermissions { - unix(0755) - } - } - with basePlugins - with nonPdfOutput - with baseData -} - -// See: https://github.com/TheBoegl/gradle-launch4j -launch4j { - mainClassName = 'pcgen.system.Main' - icon = layout.projectDirectory.file("installers/win-installer/Local/pcgen.ico").asFile.absolutePath - requiresJdk = false // false means it'll grab a JRE first if available - maxHeapPercent = 80 - stayAlive = false - requires64Bit = false // false means it auto-selects 64 or 32 bit as required. - bundledJrePath = '.' - dontWrapJar = true - splashFileName = layout.projectDirectory.file("installers/win-installer/Local/splash.bmp").asFile.absolutePath - splashWaitForWindows = true - splashTimeout = 60 - splashTimeoutError = true - // Can't be longer than 150 characters - copyright = "PCGen's source-code is distributed under the GNU Lesser General Public License (LGPL)" - version = "${shortVerNum}" - jvmOptions = ["-Dsun.java2d.dpiaware=false"] - downloadUrl = "http://pcgen.org/download/" -} - -tasks.register("layoutNsisOptional", Copy) { - dependsOn copyToOutput, tasks.named("runtime") - description = "Assemble the optional files for PCGen installations" - into nsisOptionFolder - - with pdfLibs, gmgenPlugins, pdfOutput, optionalData, lib32, lib64 -} - // Generate the publisher/data list for the Windows installer script tasks.register("genDataList") { inputs.dir layout.projectDirectory.dir("data") @@ -331,96 +260,16 @@ tasks.register("genDataList") { } } -// Create the version specific config files for NSIS -tasks.register("genProjectNsis") { - def licenseFile = layout.projectDirectory.file("code/LICENSE") - inputs.file licenseFile - inputs.property("plainVerNum", plainVerNum) - inputs.property("installerVerNum", installerVerNum) - - def projectNsh = layout.projectDirectory.file("installers/win-installer/project.nsh") - def constantsNsh = layout.projectDirectory.file("installers/win-installer/includes/constants.nsh") - - outputs.files projectNsh, constantsNsh - - doLast { - projectNsh.asFile - .text = """ -; Projects file generated by release.gradle -!define PROJECT_BUILD_DIR "${layout.buildDirectory.dir(".").get()}" -!define PROJECT_DIST_BIN_DIR "${layout.buildDirectory.dir("nsis").get()}" -!define PROJECT_DIST_DIR "${layout.buildDirectory.dir(".").get()}" -!define PROJECT_FINAL_NAME "pcgen" -!define PROJECT_LICENSE_FILE "${licenseFile.asFile.absolutePath}" -!define PROJECT_LICENSE_TEXT "This program is Licensed under The GNU Lesser General Public License, Version 2.1." -!define PROJECT_NAME "PCGen" -!define PROJECT_ORGANIZATION "SourceForge" -!define PROJECT_REG_KEY "SOFTWARE\\SourceForge\\PCGen\\${plainVerNum}" -!define PROJECT_REG_UNINSTALL_KEY "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\PCGen ${plainVerNum}" -!define PROJECT_STARTMENU_FOLDER "\$SMPROGRAMS\\SourceForge\\PCGen ${plainVerNum}" -!define PROJECT_URL "http://pcgen.sourceforge.net/" -!define PROJECT_VERSION "${plainVerNum}" -!define INSTALLER_VERSION "${installerVerNum}" -""" - - constantsNsh.asFile - .text = """ -; Constants file generated by release.gradle -!define SIMPVER "${shortVerNum}" -!define LONGVER "${plainVerNum}" -!define OutDir "${layout.buildDirectory.dir("nsisRelease").get()}" -!define SrcDir "${layout.buildDirectory.dir("nsis").get()}" -""" - } -} - -// Call NSIS to generate windows installer - Removed "genDataList" -tasks.register("buildNsis", Exec) { - dependsOn layoutNsisBase, layoutNsisOptional, genProjectNsis, downloadJRE, genDataList - mustRunAfter createExe - - def makensisPaths = [windows: "C:/Program Files (x86)/NSIS/makensis.exe"] - def currentOs = System.getProperty('os.name').toLowerCase(Locale.ROOT); - def makensis = makensisPaths.getOrDefault(currentOs, "makensis") - def pcgenNsi = layout.projectDirectory.file("installers/win-installer/pcgen.nsi").asFile.absolutePath - def stdout = new ByteArrayOutputStream() - - doFirst { - println("Running makensis from the path '${makensis}':") - mkdir layout.buildDirectory.dir("nsis") - mkdir layout.buildDirectory.dir("nsisRelease") - } - workingDir layout.buildDirectory.dir("nsis") - - standardOutput = stdout; - errorOutput = stdout; - - if (currentOs == "windows") { - commandLine makensis, "/V4", "/WX", "/Onsis.output", pcgenNsi - } else { - commandLine makensis, "-V4", "-WX", "-Onsis.output", pcgenNsi - } - - doLast { - def nsisOutput = layout.buildDirectory.file("nsis/nsis.output").get() - println "NSIS output is located in : ${nsisOutput.asFile.absolutePath}" - } -} - -tasks.register("cleanNsis", Delete) { - delete nsisBaseFolder, nsisOptionFolder, layout.buildDirectory.dir("nsis/Local") -} - tasks.register("sourcesJar", Jar) { - dependsOn classes, copyToOutput, createExe, distTar, distZip, layoutNsisBase, startScripts - duplicatesStrategy DuplicatesStrategy.INCLUDE + dependsOn classes, copyToOutput, distTar, distZip, startScripts + duplicatesStrategy = DuplicatesStrategy.INCLUDE description = "build source jar file" archiveClassifier.set('sources') from sourceSets.main.allSource } tasks.register("assembleArtifacts", Copy) { - dependsOn build, runtimeZip, sourcesJar, buildNsis + dependsOn build, runtimeZip, sourcesJar if (Os.isFamily(Os.FAMILY_MAC) || Os.isFamily(Os.FAMILY_UNIX)) { @@ -429,11 +278,7 @@ tasks.register("assembleArtifacts", Copy) { description = "Create the release artifacts and get them into the release folder." into releaseDir - // buildNsis puts the exe into the release folder directly - from(layout.buildDirectory.dir("nsisRelease")){ - include '*.exe' - } from(layout.buildDirectory.dir("libs")){ include 'pcgen*-sources.jar' } diff --git a/code/gradle/reporting.gradle b/code/gradle/reporting.gradle index 4eaf13fffbf..f521206a4c9 100644 --- a/code/gradle/reporting.gradle +++ b/code/gradle/reporting.gradle @@ -9,7 +9,7 @@ checkstyle { configFile = new File('code/standards/checkstyle.xml') configProperties = [samedir: "${rootDir}/code/standards"] showViolations = true - toolVersion = '10.20.1' + toolVersion = '12.1.2' sourceSets = [] } @@ -19,7 +19,7 @@ pmd { ruleSetFiles = files('code/standards/ruleset.xml') consoleOutput = true sourceSets = [] - toolVersion = "7.7.0" + toolVersion = "7.18.0" incrementalAnalysis = true } @@ -28,7 +28,7 @@ spotbugs { def classLoader = plugins["com.github.spotbugs"].class.classLoader def SpotBugsConfidence = classLoader.findLoadedClass("com.github.spotbugs.snom.Confidence") - toolVersion = '4.8.6' + toolVersion = '4.9.8' excludeFilter = file("$rootProject.projectDir/code/standards/spotbugs_ignore.xml") omitVisitors = ['Naming', 'CrossSiteScripting', 'DontUseEnum', 'DoInsideDoPrivileged'] reportLevel = SpotBugsConfidence.LOW @@ -39,7 +39,7 @@ spotbugsMain { reports { xml.required = false html.required = true - html.stylesheet resources.text.fromFile('code/standards/fancy-hist.xsl') + html.stylesheet = resources.text.fromFile('code/standards/fancy-hist.xsl') } } diff --git a/code/pluginbuild.xml b/code/pluginbuild.xml deleted file mode 100644 index f7905f0ecd9..00000000000 --- a/code/pluginbuild.xml +++ /dev/null @@ -1,7653 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Manifest-Version: 1.2 -Main-Class: ${manifest.main.class} -Class-Path: ${manifest.extra.jars} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/code/src/itest/pcgen/output/FreeMarkerTest.java b/code/src/itest/pcgen/output/FreeMarkerTest.java index 59363157558..8e2828cf8fc 100644 --- a/code/src/itest/pcgen/output/FreeMarkerTest.java +++ b/code/src/itest/pcgen/output/FreeMarkerTest.java @@ -1,16 +1,16 @@ /* * Copyright (c) 2015 Tom Parker - * + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA @@ -39,7 +39,7 @@ public class FreeMarkerTest extends AbstractOutputTestCase @BeforeEach @Override - public void setUp() throws Exception + public void setUp() { super.setUp(); CDOMWrapperInfoFacet wiFacet = diff --git a/code/src/itest/pcgen/output/testsupport/AbstractOutputTestCase.java b/code/src/itest/pcgen/output/testsupport/AbstractOutputTestCase.java index e207171f1da..7934faf3148 100644 --- a/code/src/itest/pcgen/output/testsupport/AbstractOutputTestCase.java +++ b/code/src/itest/pcgen/output/testsupport/AbstractOutputTestCase.java @@ -1,16 +1,16 @@ /* * Copyright (c) 2014-15 Tom Parker - * + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA @@ -42,9 +42,9 @@ public abstract class AbstractOutputTestCase { protected DataSetID dsid; protected CharID id; - + @BeforeEach - public void setUp() throws Exception + public void setUp() { Locale.setDefault(Locale.US); dsid = DataSetID.getID(); diff --git a/code/src/java/pcgen/cdom/base/Constants.java b/code/src/java/pcgen/cdom/base/Constants.java index 6c95580490c..bd602422eca 100644 --- a/code/src/java/pcgen/cdom/base/Constants.java +++ b/code/src/java/pcgen/cdom/base/Constants.java @@ -28,7 +28,6 @@ @SuppressWarnings("PMD.ConstantsInInterface") public interface Constants { - /******************************************************************** * Static definitions of Equipment location strings ********************************************************************/ @@ -111,7 +110,7 @@ public interface Constants String INTERNAL_WEAPON_PROF = "PCGENi_WEAPON_PROFICIENCY"; //$NON-NLS-1$ /** Line Separator. */ - String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$ + String LINE_SEPARATOR = System.lineSeparator(); //$NON-NLS-1$ /** The constant string "None". */ String NONE = "None"; //$NON-NLS-1$ diff --git a/code/src/java/pcgen/core/GameMode.java b/code/src/java/pcgen/core/GameMode.java index 92df328e63f..40e188e26ea 100644 --- a/code/src/java/pcgen/core/GameMode.java +++ b/code/src/java/pcgen/core/GameMode.java @@ -187,7 +187,7 @@ public GameMode(final String modeName) } /** - * Apply the stored preferences to the game mode. + * Apply the stored preferences to the game mode. */ public void applyPreferences() { @@ -240,7 +240,7 @@ public List getACTypeRemoveString(final String ACType) } /** - * Retrieve the correct case of the supplied ACType name. + * Retrieve the correct case of the supplied ACType name. * @param acType The name to be found. * @return The name in the correct case. */ @@ -636,7 +636,7 @@ public Set getWeaponTypes() /** * Gets the abbreviation for the given weapon Type. - * + * * @param type * The Type * @return The abbreviation for the given Type @@ -1939,7 +1939,7 @@ public void clearLoadContext() * Takes references and abbreviations that have been placed into the * LoadContext for this GameMode and copies those references and * abbreviations into the given ReferenceContext - * + * * @param referenceContext * The Reference Context into which the references from this * GameMode should be copied. @@ -1960,20 +1960,24 @@ static void resolveReferenceManufacturer(AbstractReferenceC { String identityName = rm.getPersistentFormat(); ReferenceManufacturer mfg = rc.getManufacturerByFormatName(identityName, rm.getReferenceClass()); - // If format fails, fall back to class + // If a format fails, fall back to class if ((mfg == null) && (identityName.indexOf('=') == -1)) { Class cl = rm.getReferenceClass(); mfg = rc.getManufacturer(cl); } - if (mfg!=null) { - for (CDOMReference ref : rm.getAllReferences()) { + if (mfg != null) + { + for (CDOMReference ref : rm.getAllReferences()) + { ((TransparentReference) ref).resolve(mfg); } rm.injectConstructed(mfg); } else - System.out.println("idname="+identityName+" had a null mfg - skipping"); + { + Logging.debugPrint("idname = " + identityName + " had a null mfg - skipping"); + } } public LoadContext getContext() @@ -2250,7 +2254,7 @@ public String getEquipTypeIcon(String equipType) } /** - * Retrieve the priority of the listed type;s icon. A higher number means a higher + * Retrieve the priority of the listed type;s icon. A higher number means a higher * priority, generally the highest priority icon will be used. * @param equipType The equipment type * @return The priority, or 0 if none is known. diff --git a/code/src/java/pcgen/core/Globals.java b/code/src/java/pcgen/core/Globals.java index 15b8e1caeaf..c6ef2a83378 100644 --- a/code/src/java/pcgen/core/Globals.java +++ b/code/src/java/pcgen/core/Globals.java @@ -540,8 +540,8 @@ public static void clearCampaignsForRefresh() /** * Check if enough data has been loaded to support character creation. - * Will also report to the log the number of items of each of the - * necessary types that are currently loaded. + * Will also report to the log the number of items of each of the + * necessary types that are currently loaded. * @return true or false */ public static boolean displayListsHappy() @@ -722,7 +722,7 @@ public static boolean selectPaper(final String paperName) } /** - * Apply the user's preferences to the initial state of the Globals. + * Apply the user's preferences to the initial state of the Globals. */ public static void initPreferences() { @@ -789,8 +789,8 @@ static List sortPObjectList(final List - * + * @param + * * @param aList * @return Sorted list of Pcgen Objects */ @@ -1033,7 +1033,7 @@ else if (fType.equals("mac_user")) private static int bonusParsing(final String l, final int level, int num, final PlayerCharacter aPC) { - // should be in format levelnum,rangenum[,numchoices] + // should be in format levelnum,rangenum[,numchoices] final StringTokenizer aTok = new StringTokenizer(l, "|", false); final int startLevel = Integer.parseInt(aTok.nextToken()); final String rangeLevelFormula = aTok.nextToken(); diff --git a/code/src/java/pcgen/core/PlayerCharacter.java b/code/src/java/pcgen/core/PlayerCharacter.java index cde653a123c..447da86e5d5 100644 --- a/code/src/java/pcgen/core/PlayerCharacter.java +++ b/code/src/java/pcgen/core/PlayerCharacter.java @@ -501,7 +501,7 @@ public class PlayerCharacter implements Cloneable, VariableContainer private final CNAbility bonusLanguageAbility = CNAbilityFactory.getCNAbility(AbilityCategory.LANGBONUS, Nature.VIRTUAL, Globals.getContext() - .getReferenceContext().getManufacturerId(AbilityCategory.LANGBONUS).getActiveObject("*LANGBONUS")); + .getReferenceContext().getManufacturerId(AbilityCategory.LANGBONUS).getActiveObject("*LANGBONUS")); private final CodeControl controller; private Map previewSheetVars = new HashMap<>(); @@ -576,8 +576,8 @@ public PlayerCharacter(Collection loadedCampaigns) setXPTable(SettingsHandler.getGameAsProperty().get().getDefaultXPTableName()); ChannelUtilities.setControlledChannel(id, CControl.CHARACTERTYPE, - SettingsHandler.getGameAsProperty().get() - .getDefaultCharacterType()); + SettingsHandler.getGameAsProperty().get() + .getDefaultCharacterType()); setPreviewSheet(SettingsHandler.getGameAsProperty().get().getDefaultPreviewSheet()); setName(Constants.EMPTY_STRING); @@ -593,8 +593,8 @@ public PlayerCharacter(Collection loadedCampaigns) * well as setting the proficiency, this zeros out the Weight and cost of * the equipment. * - * @param equip the Weapon to get the proficiency from - * @param eqm the weapon to set the proficiency in + * @param equip the Weapon to get the proficiency from + * @param eqm the weapon to set the proficiency in */ private static void setProf(final Equipment equip, final Equipment eqm) { @@ -643,7 +643,8 @@ private void deityWatchSetup(LoadContext context) ChannelUtilities.watchChannel(this, CControl.DEITYINPUT, FacetLibrary.getFacet(DeityWeaponProfFacet.class)); ChannelUtilities.watchChannel(this, CControl.DEITYINPUT, FacetLibrary.getFacet(KitChoiceFacet.class)); ChannelUtilities.watchChannel(this, CControl.DEITYINPUT, FacetLibrary.getFacet(RemoveFacet.class)); - ChannelUtilities.watchChannel(this, CControl.DEITYINPUT, FacetLibrary.getFacet(BonusActiviationFacet.class), 1000); + ChannelUtilities.watchChannel(this, CControl.DEITYINPUT, FacetLibrary.getFacet(BonusActiviationFacet.class), + 1000); ChannelUtilities.watchChannel(this, CControl.DEITYINPUT, FacetLibrary.getFacet(CalcBonusFacet.class), 5000); ChannelUtilities.watchChannel(this, CControl.DEITYINPUT, moveResultFacet, 2000); } @@ -666,7 +667,7 @@ public void setWeight(int value) /** * Sets player character information * - * @param attr which attribute to set + * @param attr which attribute to set * @param value the value to set it to */ public void setPCAttribute(PCStringKey attr, String value) @@ -839,8 +840,7 @@ public void setCalcEquipmentList(final boolean useTempBonuses) eq.put(ObjectKey.CURRENT_COST, BigDecimal.ZERO); eq.put(ObjectKey.WEIGHT, BigDecimal.ZERO); eq.setLocation(anEquip.getLocation()); - } - else + } else { // replace the orig item with the bonus item eq.setLocation(anEquip.getLocation()); @@ -852,8 +852,7 @@ public void setCalcEquipmentList(final boolean useTempBonuses) eq.setIsEquipped(true, this); eq.setNumberEquipped(1); - } - else + } else { eq.put(ObjectKey.CURRENT_COST, BigDecimal.ZERO); eq.put(ObjectKey.WEIGHT, BigDecimal.ZERO); @@ -931,6 +930,7 @@ public List getClassList() /** * Gets the Set of PCClass objects for this Character. + * * @return a set of PCClass objects */ public Set getClassSet() @@ -1058,6 +1058,7 @@ private Set getEquipmentSet() /** * Get the character's "equipped" equipment. + * * @return a set of the "equipped" equipment */ public Set getEquippedEquipmentSet() @@ -1083,12 +1084,11 @@ public List getEquipmentListInOutputOrder() * ascending order of the equipment's outputIndex field. If multiple items * of equipment have the same outputIndex they will be ordered by name. Note * hidden items (outputIndex = -1) are not included in this list. - * + *

* Deals with merge as well. See the Constants package for acceptable values * of merge . * * @param merge controls how much merging is done. - * * @return An ArrayList of the equipment objects in output order. */ public List getEquipmentListInOutputOrder(final int merge) @@ -1112,30 +1112,28 @@ public List getEquipmentMasterList() /** * Search for a piece of equipment in the specified list by name. - * + *

* TODO - This does not belong in PlayerCharacter. Move to Equipment if * needed. - * + *

* TODO - This probably won't work with i18n. Should always search by key. * - * @param aString - * The name of the equipment. - * @param aList - * The Collection of equipment to search in. - * + * @param aString The name of the equipment. + * @param aList The Collection of equipment to search in. * @return The Equipment object or null */ private static Equipment getEquipmentNamed(final String aString, final Collection aList) { return aList.stream() - .filter(eq -> aString.equalsIgnoreCase(eq.getName())) - .findFirst() - .orElse(null); + .filter(eq -> aString.equalsIgnoreCase(eq.getName())) + .findFirst() + .orElse(null); } /** * Search among the PCs equipment for a named piece of equipment. + * * @param name The name of the piece of equipment. * @return null or the equipment named. */ @@ -1147,8 +1145,7 @@ public Equipment getEquipmentNamed(final String name) /** * Set the characters eye colour. * - * @param aString - * the colour of their eyes + * @param aString the colour of their eyes */ public void setEyeColor(final String aString) { @@ -1242,6 +1239,7 @@ private double getBonusFeatPool() } return bonus; } + /** * Sets the filename of the character. * @@ -1279,8 +1277,7 @@ public Collection getFollowerList() * The gender will only be changed if the character does not have a template * that locks the character's gender. * - * @param g - * A gender to try and set. + * @param g A gender to try and set. */ public void setGender(final Gender g) { @@ -1302,8 +1299,7 @@ public void setGender(final Gender g) *

* TODO - This is pretty dangerous. * - * @param newIsImporting - * true to mark the character as being imported. + * @param newIsImporting true to mark the character as being imported. */ public void setImporting(final boolean newIsImporting) { @@ -1330,8 +1326,7 @@ public Set getLanguageSet() * be driven off types but now it's driven from a list of companion mods * but the java doc has not been updated. * - * @param compList - * A list of companionMods to get level for + * @param compList A list of companionMods to get level for * @return The effective level for this companion type */ public int getEffectiveCompanionLevel(final CompanionList compList) @@ -1370,8 +1365,7 @@ public int getEffectiveCompanionLevel(final CompanionList compList) * on the masters level and info contained in the companionModList Array * such as HitDie, SR, BONUS, SA, etc. * - * @param aM - * The master to be set. + * @param aM The master to be set. */ public void setMaster(final Follower aM) { @@ -1406,7 +1400,7 @@ public void setMaster(final Follower aM) boolean found = false; for (CompanionMod cMod : Globals.getContext().getReferenceContext().getManufacturerId(aM.getType()) - .getAllObjects()) + .getAllObjects()) { if ((cMod.getLevelApplied(mClass) > 0) && !found) { @@ -1422,7 +1416,7 @@ public void setMaster(final Follower aM) Collection oldCompanionMods = companionModFacet.removeAll(id); for (CompanionMod cMod : Globals.getContext().getReferenceContext().getManufacturerId(aM.getType()) - .getAllObjects()) + .getAllObjects()) { // Check all the masters classes for (PCClass mClass : mPC.getClassSet()) @@ -1597,8 +1591,7 @@ public void setMaster(final Follower aM) * the given companion list. This method does not adjust for any followers * already selected by the character. * - * @param cList - * A list of potential follower races + * @param cList A list of potential follower races * @return The max number of followers -1 for any number */ public int getMaxFollowers(CompanionList cList) @@ -1677,8 +1670,7 @@ public PlayerCharacter getMasterPC() /** * Sets the character's name. * - * @param aString - * A name to set. + * @param aString A name to set. */ public final void setName(final String aString) { @@ -1710,7 +1702,7 @@ public List getNamedTempBonusList() * Takes all the Temporary Bonuses and Merges them into just the unique * named bonuses. * - * @return List of Strings + * @return List of Strings */ public List getNamedTempBonusDescList() { @@ -1719,6 +1711,7 @@ public List getNamedTempBonusDescList() /** * Set the value of the feat pool. + * * @param pool value to set the feat pool to */ public void setPoolAmount(final int pool) @@ -1728,6 +1721,7 @@ public void setPoolAmount(final int pool) /** * Get the value of the feat pool. + * * @return the feat pool amount */ public int getPoolAmount() @@ -1738,8 +1732,7 @@ public int getPoolAmount() /** * Selector Sets the path to the portrait of the character. * - * @param newPortraitPath - * the path to the portrait file + * @param newPortraitPath the path to the portrait file */ public void setPortraitPath(final String newPortraitPath) { @@ -1748,6 +1741,7 @@ public void setPortraitPath(final String newPortraitPath) /** * Set a new outline for the portrait thumbnail. + * * @param rect The thumbnail outline. */ public void setPortraitThumbnailRect(Rectangle rect) @@ -1943,8 +1937,7 @@ public ArrayList getSpecialAbilityTimesList() numTimes[j]++; } } - } - else + } else { sortList.add(ability); numTimes[i] = 1; @@ -1968,8 +1961,7 @@ public ArrayList getSpecialAbilityTimesList() /** * Set the name of the spellbook to auto add new known spells to. * - * @param aString - * The new spellbook name. + * @param aString The new spellbook name. */ public void setSpellBookNameToAutoAddKnown(final String aString) { @@ -1989,8 +1981,7 @@ public String getSpellBookNameToAutoAddKnown() /** * Retrieve a spell book object given the name of the spell book. * - * @param name - * The name of the spell book to be retrieved. + * @param name The name of the spell book to be retrieved. * @return The spellbook (or null if not present). */ public SpellBook getSpellBookByName(final String name) @@ -2080,6 +2071,7 @@ public void unsetTempBonusFilter(final String aBonusStr) /** * Get a set of the templates applies to this pc. + * * @return the set of Templates. */ public Collection getTemplateSet() @@ -2089,12 +2081,12 @@ public Collection getTemplateSet() /** * Evaluates the variable string passed in and returns its value. - * + *

* This should probably be refactored to return a String instead. * * @param variableString the variable to evaluate - * @param isMax if multiple values are stored, whether to return the largest value - * found or the first. + * @param isMax if multiple values are stored, whether to return the largest value + * found or the first. * @return the value of the variable. */ public Float getVariable(final String variableString, final boolean isMax) @@ -2108,9 +2100,9 @@ public Float getVariable(final String variableString, final boolean isMax) { if (Logging.isDebugMode()) { - final String sb = "This is a deliberate warning message, not an error - " - + "Avoiding infinite loop in getVariable: repeated lookup " + "of \"" + lastVariable + "\" at " - + value; + String sb = "This is a deliberate warning message, not an error - " + + "Avoiding infinite loop in getVariable: repeated lookup " + "of \"" + lastVariable + + "\" at " + value; Logging.debugPrint(sb); } lastVariable = null; @@ -2127,8 +2119,7 @@ public Float getVariable(final String variableString, final boolean isMax) value = val; found = true; } - } - catch (IllegalArgumentException e) + } catch (IllegalArgumentException e) { //This variable is not in the data - must be builtin? } @@ -2162,15 +2153,14 @@ public int getPointBuyPoints() /** * Sets the total Experience Points for the Player Character to the given value. - * + *

* Note this sets earned Experience Points as a side effect (calculated based on the * level-adjusted Experience Points the Player Character may have). If the given xp * value is less than the level-adjusted Experience Points possessed by the Player * Character, then an error will be logged, and the earned Experience Points will be * set to 0. * - * @param xp - * The total Experience Points for the Player Character + * @param xp The total Experience Points for the Player Character */ public void setXP(int xp) { @@ -2248,8 +2238,7 @@ public void addEquipSet(final EquipSet set) /** * Add an item of equipment to the character. * - * @param eq - * The equipment to be added. + * @param eq The equipment to be added. */ public void addEquipment(final Equipment eq) { @@ -2260,6 +2249,7 @@ public void addEquipment(final Equipment eq) /** * Cache the output index of an automatic equipment item. + * * @param item The equipment item. */ public void cacheOutputIndex(Equipment item) @@ -2279,12 +2269,9 @@ public void cacheOutputIndex(Equipment item) * Mostly concerned with ensuring that the spellbook objects remain in sync * with the number of equipment spellbooks. * - * @param eq - * The Equipment being updated. - * @param oldQty - * The original number of items. - * @param newQty - * The new number of items. + * @param eq The Equipment being updated. + * @param oldQty The original number of items. + * @param newQty The new number of items. */ public void updateEquipmentQty(final Equipment eq, double oldQty, double newQty) { @@ -2362,6 +2349,7 @@ public TempBonusInfo addTempBonus(final BonusObj aBonus, Object source, Object t /** * Add a piece of equipment to the temporary bonus list. + * * @param aEq The piece of equipment to add. */ public void addTempBonusItemList(final Equipment aEq) @@ -2373,7 +2361,7 @@ public void addTempBonusItemList(final Equipment aEq) /** * Compute the total bonus from a List of BonusObjs. * - * @param aList The list of objects + * @param aList The list of objects * @param source The source of the bonus objects. * @return The aggregate bonus */ @@ -2391,6 +2379,7 @@ public double calcBonusFromList(final List aList, CDOMObject source) /** * Checks that the parameter passed in is in the list of objects for which this PC qualifies. + * * @param obj the object to test for qualification. * @return true if the PC is qualified to have this object. */ @@ -2401,6 +2390,7 @@ public boolean checkQualifyList(CDOMObject obj) /** * Check whether this PC has this WeaponProf. + * * @param wp The WeaponProf to check. * @return True if the PC has the WeaponProf */ @@ -2411,6 +2401,7 @@ public boolean hasWeaponProf(final WeaponProf wp) /** * Remove an EqSet from the PC's Equipped Equipment. + * * @param eSet - The EquipSet to remove. * @return true if the object was removed. */ @@ -2423,6 +2414,7 @@ public boolean delEquipSet(final EquipSet eSet) /** * Remove a Follower from this PC. + * * @param aFollower The follower to remove. */ public void delFollower(final Follower aFollower) @@ -2433,6 +2425,7 @@ public void delFollower(final Follower aFollower) /** * Check whether the PC has this variable. + * * @param variableString The variable to check for. * @return True if the PC has the variable. */ @@ -2441,8 +2434,7 @@ public boolean hasVariable(final String variableString) try { return variableFacet.contains(id, VariableKey.valueOf(variableString)); - } - catch (IllegalArgumentException e) + } catch (IllegalArgumentException e) { //Built in variable return false; @@ -2450,7 +2442,6 @@ public boolean hasVariable(final String variableString) } /** - * * @param eq */ public void removeEquipment(final Equipment eq) @@ -2466,7 +2457,6 @@ public void removeEquipment(final Equipment eq) } /** - * * @param eq */ private void removeLocalEquipment(final Equipment eq) @@ -2503,16 +2493,11 @@ public String getAttackString(AttackType at, final int bonus) * attacks generated. The second increases both the size and number of * attacks * - * @param at - * The type of attack. Takes an AttackType (an enumeration) - * - * @param TOHITBonus - * A bonus that will be added to the TOHIT numbers. This bonus - * affects only the numbers produced, not the number of attacks - * - * @param BABBonus - * This bonus will be added to BAB before the number of attacks - * has been determined. + * @param at The type of attack. Takes an AttackType (an enumeration) + * @param TOHITBonus A bonus that will be added to the TOHIT numbers. This bonus + * affects only the numbers produced, not the number of attacks + * @param BABBonus This bonus will be added to BAB before the number of attacks + * has been determined. * @return The attack string for this character */ @@ -2618,8 +2603,7 @@ public String getAttackString(AttackType at, final int TOHITBonus, int BABBonus) { attackCycle = defaultAttackCycle; attackTotal = subTotal; - } - else + } else { workingBAB -= raceBAB; subTotal -= raceBAB; @@ -2712,8 +2696,7 @@ public boolean getUseHigherKnownSlots() * Set whether higher level known spell slots can be used for lower level * spells, or if known spells are restricted to their own level only. * - * @param useHigher - * Can higher level known spell slots be used? + * @param useHigher Can higher level known spell slots be used? */ public void setUseHigherKnownSlots(boolean useHigher) { @@ -2736,8 +2719,7 @@ public boolean getUseHigherPreppedSlots() * Set whether higher level prepared spell slots can be used for lower level * spells, or if prepared spells are restricted to their own level only. * - * @param useHigher - * Can higher level prepared spell slots be used? + * @param useHigher Can higher level prepared spell slots be used? */ public void setUseHigherPreppedSlots(boolean useHigher) { @@ -2748,9 +2730,7 @@ public void setUseHigherPreppedSlots(boolean useHigher) * Returns the "Base" check value for the check at the index * specified. * - * @param check - * The index of the check to get - * + * @param check The index of the check to get * @return The base check value. */ public int getBaseCheck(final PCCheck check) @@ -2801,9 +2781,7 @@ public int getBaseCheck(final PCCheck check) *

* This total includes all check bonuses the character has. * - * @param check - * The check to get. - * + * @param check The check to get. * @return A check value. */ public int getTotalCheck(PCCheck check) @@ -2834,11 +2812,9 @@ public double getBonusDueToType(final String mainType, final String subType, fin * Retrieves an unsorted list of the character's equipment matching the * supplied type and status criteria. * - * @param typeName - * The type of equipment to be selected - * @param status - * The required status: 1 (equipped) 2 (not equipped) 3 (don't - * care) + * @param typeName The type of equipment to be selected + * @param status The required status: 1 (equipped) 2 (not equipped) 3 (don't + * care) * @return An ArrayList of the matching equipment objects. */ public List getEquipmentOfType(final String typeName, final int status) @@ -2850,14 +2826,11 @@ public List getEquipmentOfType(final String typeName, final int statu * Retrieves an unsorted list of the character's equipment matching the * supplied type, sub type and status criteria. * - * @param typeName - * The type of equipment to be selected - * @param subtypeName - * The subtype of equipment to be selected (empty string for no - * subtype) - * @param status - * The required status: 1 (equipped) 2 (not equipped) 3 (don't - * care) + * @param typeName The type of equipment to be selected + * @param subtypeName The subtype of equipment to be selected (empty string for no + * subtype) + * @param status The required status: 1 (equipped) 2 (not equipped) 3 (don't + * care) * @return An ArrayList of the matching equipment objects. */ public List getEquipmentOfType(final String typeName, final String subtypeName, final int status) @@ -2887,11 +2860,9 @@ public List getEquipmentOfType(final String typeName, final String su * equipment have the same outputIndex they will be ordered by name. Note * hidden items (outputIndex = -1) are not included in this list. * - * @param typeName - * The type of equipment to be selected - * @param status - * The required status: 1 (equipped) 2 (not equipped) 3 (don't - * care) + * @param typeName The type of equipment to be selected + * @param status The required status: 1 (equipped) 2 (not equipped) 3 (don't + * care) * @return An ArrayList of the matching equipment objects in output order. */ public List getEquipmentOfTypeInOutputOrder(final String typeName, final int status) @@ -2900,12 +2871,9 @@ public List getEquipmentOfTypeInOutputOrder(final String typeName, fi } /** - * @param typeName - * The type of equipment to be selected - * @param status - * The required status - * @param merge - * What type of merge for like equipment + * @param typeName The type of equipment to be selected + * @param status The required status + * @param merge What type of merge for like equipment * @return An ArrayList of equipment objects */ public List getEquipmentOfTypeInOutputOrder(final String typeName, final int status, final int merge) @@ -2914,18 +2882,14 @@ public List getEquipmentOfTypeInOutputOrder(final String typeName, fi } /** - * @param typeName - * The type of equipment to be selected - * @param subtypeName - * The subtype of equipment to be selected - * @param status - * The required status - * @param merge - * What sort of merging should occur + * @param typeName The type of equipment to be selected + * @param subtypeName The subtype of equipment to be selected + * @param status The required status + * @param merge What sort of merging should occur * @return An ArrayList of equipment objects */ public List getEquipmentOfTypeInOutputOrder(final String typeName, final String subtypeName, - final int status, final int merge) + final int status, final int merge) { return sortEquipmentList(getEquipmentOfType(typeName, subtypeName, status), merge); } @@ -2936,7 +2900,6 @@ public List getEquipmentOfTypeInOutputOrder(final String typeName, fi * equipment depends on the passed in int * * @param merge The type of merge to perform - * * @return the sorted list of weapons. */ public List getExpandedWeapons(final int merge) @@ -3054,8 +3017,7 @@ else if (equip.isMelee() && equip.isRanged()) if (replacedPrimary) { primaryWeaponFacet.addAfter(id, eqm, eqr); - } - else if (replacedSecondary) + } else if (replacedSecondary) { secondaryWeaponFacet.addAfter(id, eqm, eqr); } @@ -3196,7 +3158,7 @@ public int getMaxCharacterDomains() * @param source * @param aPC * @return the number of Character Domains possible and check the level of - * the source class if the result is 0. + * the source class if the result is 0. */ public int getMaxCharacterDomains(final PCClass source, final PlayerCharacter aPC) { @@ -3212,11 +3174,9 @@ public int getMaxCharacterDomains(final PCClass source, final PlayerCharacter aP * Calculate the maximum number of ranks the character is allowed to have in * the specified skill. * - * @param aSkill - * The skill being checked. - * @param aClass - * The name of the current class in which points are being spent - - * only used to check cross-class skill cost. + * @param aSkill The skill being checked. + * @param aClass The name of the current class in which points are being spent - + * only used to check cross-class skill cost. * @return max rank */ public Float getMaxRank(Skill aSkill, final PCClass aClass) @@ -3250,26 +3210,22 @@ public Float getMaxRank(Skill aSkill, final PCClass aClass) levelForSkillPurposes = (getTotalLevels()); maxRanks = SkillUtilities.maxCrossClassSkillForLevel(levelForSkillPurposes, this); - } - else + } else { maxRanks = SkillUtilities.maxClassSkillForLevel(levelForSkillPurposes, this); } - } - else if (!this.isClassSkill(aSkill) && (this.getSkillCostForClass(aSkill, aClass) == SkillCost.CLASS)) + } else if (!this.isClassSkill(aSkill) && (this.getSkillCostForClass(aSkill, aClass) == SkillCost.CLASS)) { // Cross class skill - but as cost is 1 only return a whole number maxRanks = new BigDecimal( - SkillUtilities.maxCrossClassSkillForLevel( - levelForSkillPurposes, this).intValue()); // This was (int) (i/2.0) previously - } - else if (!this.isClassSkill(aSkill)) + SkillUtilities.maxCrossClassSkillForLevel( + levelForSkillPurposes, this).intValue()); // This was (int) (i/2.0) previously + } else if (!this.isClassSkill(aSkill)) { // Cross class skill maxRanks = SkillUtilities.maxCrossClassSkillForLevel(levelForSkillPurposes, this); - } - else + } else { // Class skill maxRanks = SkillUtilities.maxClassSkillForLevel(levelForSkillPurposes, this); @@ -3297,12 +3253,10 @@ public boolean isProficientWith(final Equipment eq) if (eq.isShield()) { return shieldProfFacet.isProficientWithShield(id, eq); - } - else if (eq.isArmor()) + } else if (eq.isArmor()) { return armorProfFacet.isProficientWithArmor(id, eq); - } - else if (eq.isWeapon()) + } else if (eq.isWeapon()) { return weaponProfFacet.isProficientWithWeapon(id, eq); } @@ -3322,8 +3276,7 @@ public final boolean setRace(final Race newRace) if (newRace == null) { success = raceInputFacet.set(id, RaceUtilities.getUnselectedRace()); - } - else + } else { success = raceInputFacet.set(id, newRace); } @@ -3375,8 +3328,7 @@ public double getSizeAdjustmentBonusTo(String aType, String aName) /** * Set the skill display filter * - * @param filter - * The new filter + * @param filter The new filter */ public void setSkillFilter(final SkillFilter filter) { @@ -3395,7 +3347,7 @@ public SkillFilter getSkillFilter() if (filter == null) { filter = SkillFilter.getByValue(PCGenSettings.OPTIONS_CONTEXT.initInt(PCGenSettings.OPTION_SKILL_FILTER, - SkillFilter.Usable.getValue())); + SkillFilter.Usable.getValue())); if (filter == SkillFilter.SkillsTab) { filter = SkillFilter.Usable; @@ -3408,8 +3360,7 @@ public SkillFilter getSkillFilter() /** * Set the order in which skills should be sorted for output. * - * @param i - * The new output order + * @param i The new output order */ public void setSkillsOutputOrder(final SkillsOutputOrder i) { @@ -3445,11 +3396,9 @@ public boolean isSpellCaster(final int minLevel) * and see if they are a spell caster and of the total of all of their * spellcasting levels is at least the desired caster level. * - * @param minLevel - * The desired caster level - * @param sumOfLevels - * True if all of the character caster levels should be added - * together before the comparison. + * @param minLevel The desired caster level + * @param sumOfLevels True if all of the character caster levels should be added + * together before the comparison. * @return boolean */ public int isSpellCaster(final int minLevel, final boolean sumOfLevels) @@ -3462,13 +3411,10 @@ public int isSpellCaster(final int minLevel, final boolean sumOfLevels) * and see if they are a spell caster of the desired type and of the desired * caster level. * - * @param spellType - * The type of spellcaster (i.e. "Arcane" or "Divine") - * @param minLevel - * The desired caster level - * @param sumLevels - * True if all of the character caster levels should be added - * together before the comparison. + * @param spellType The type of spellcaster (i.e. "Arcane" or "Divine") + * @param minLevel The desired caster level + * @param sumLevels True if all of the character caster levels should be added + * together before the comparison. * @return boolean */ public int isSpellCaster(final String spellType, final int minLevel, final boolean sumLevels) @@ -3482,7 +3428,7 @@ public int isSpellCaster(final String spellType, final int minLevel, final boole { int classLevels = (int) getTotalBonusTo("CASTERLEVEL", pcClass.getKeyName()); if ((classLevels == 0) && (canCastSpellTypeLevel(pcClass.getSpellType(), 0) - || canCastSpellTypeLevel(pcClass.getSpellType(), 1))) + || canCastSpellTypeLevel(pcClass.getSpellType(), 1))) { // missing CASTERLEVEL hack classLevels = getLevel(pcClass); @@ -3491,8 +3437,7 @@ public int isSpellCaster(final String spellType, final int minLevel, final boole if (sumLevels) { runningTotal += classLevels; - } - else + } else { if (classLevels >= minLevel) { @@ -3527,10 +3472,8 @@ public void getSpellList() * Parses a spells range (short, medium or long) into an Integer based on * the spell and spell casters level * - * @param aSpell - * The spell being output. - * @param si - * The info about conditions applied to the spell + * @param aSpell The spell being output. + * @param si The info about conditions applied to the spell * @return spell range */ public String getSpellRange(final CharacterSpell aSpell, final SpellInfo si) @@ -3543,12 +3486,10 @@ public String getSpellRange(final CharacterSpell aSpell, final SpellInfo si) if (aRange.equalsIgnoreCase("CLOSE") && (aString == null)) { aString = "((CASTERLEVEL/2).TRUNC*5)+25"; //$NON-NLS-1$ - } - else if (aRange.equalsIgnoreCase("MEDIUM") && (aString == null)) + } else if (aRange.equalsIgnoreCase("MEDIUM") && (aString == null)) { aString = "(CASTERLEVEL*10)+100"; //$NON-NLS-1$ - } - else if (aRange.equalsIgnoreCase("LONG") && (aString == null)) + } else if (aRange.equalsIgnoreCase("LONG") && (aString == null)) { aString = "(CASTERLEVEL*40)+400"; //$NON-NLS-1$ } @@ -3577,9 +3518,8 @@ else if (aRange.equalsIgnoreCase("LONG") && (aString == null)) } aRange += (" (" + Globals.getGameModeUnitSet().displayDistanceInUnitSet(rangeInFeet) - + Globals.getGameModeUnitSet().getDistanceUnit() + ')'); - } - else + + Globals.getGameModeUnitSet().getDistanceUnit() + ')'); + } else { aRange = parseSpellString(aSpell, aRange); } @@ -3641,10 +3581,8 @@ public double getTemplateBonusTo(String aType, String aName) * companions, Equipment, Feats, Templates, Domains, Races, etc This value * is taken from an already populated HashMap for speed * - * @param bonusType - * Type of bonus ("COMBAT" or "SKILL") - * @param bonusName - * Name of bonus ("AC" or "Hide"); + * @param bonusType Type of bonus ("COMBAT" or "SKILL") + * @param bonusName Name of bonus ("AC" or "Hide"); * @return total bonus to */ public double getTotalBonusTo(final String bonusType, final String bonusName) @@ -3661,12 +3599,9 @@ public int getTotalLevels() * Get the value of the desired stat at the point just before the character * was raised to the next level. * - * @param stat - * The Stat to check. - * @param level - * The level we want to see the stat at. - * @param includePost - * Should stat mods that occurred after levelling be included? + * @param stat The Stat to check. + * @param level The level we want to see the stat at. + * @param includePost Should stat mods that occurred after levelling be included? * @return The stat as it was at the level */ public int getTotalStatAtLevel(final PCStat stat, final int level, final boolean includePost) @@ -3712,10 +3647,8 @@ public boolean getUseTempMods() * Evaluates a variable for this character e.g: * getVariableValue("3+CHA","CLASS:Cleric") for Turn Undead * - * @param aString - * The variable to be evaluated - * @param src - * The source within which the variable is evaluated + * @param aString The variable to be evaluated + * @param src The source within which the variable is evaluated * @return The value of the variable */ public Float getVariableValue(final String aString, final String src) @@ -3733,13 +3666,10 @@ public Float getVariableValue(final String varName, final String src, final Play * Evaluates a variable for this character e.g: * getVariableValue("3+CHA","CLASS:Cleric") for Turn Undead * - * @param aSpell - * This is specifically to compute bonuses to CASTERLEVEL for a - * specific spell. - * @param aString - * The variable to be evaluated - * @param src - * The source within which the variable is evaluated + * @param aSpell This is specifically to compute bonuses to CASTERLEVEL for a + * specific spell. + * @param aString The variable to be evaluated + * @param src The source within which the variable is evaluated * @return The value of the variable */ private Float getVariableValue(final CharacterSpell aSpell, String aString, String src) @@ -3756,7 +3686,7 @@ public VariableProcessor getVariableProcessor() } public int getTotalCasterLevelWithSpellBonus(CharacterSpell acs, final Spell aSpell, final String spellType, - final String classOrRace, final int casterLev) + final String classOrRace, final int casterLev) { if (aSpell != null && acs.getFixedCasterLevel() != null) { @@ -3975,7 +3905,7 @@ public int getTotalCasterLevelWithSpellBonus(CharacterSpell acs, final Spell aSp } private static int tallyCasterlevelBonuses(final int casterLev, boolean replaceCasterLevel, - final List bonuses) + final List bonuses) { // now go through all bonuses, checking types to see what should add // together @@ -3995,8 +3925,7 @@ private static int tallyCasterlevelBonuses(final int casterLev, boolean replaceC { zType = zType.substring(0, zType.length() - 8); zReplace = true; - } - else + } else { if (zType.endsWith(".STACK")) { @@ -4021,8 +3950,7 @@ private static int tallyCasterlevelBonuses(final int casterLev, boolean replaceC { kType = kType.substring(0, kType.length() - 8); kReplace = true; - } - else + } else { if (kType.endsWith(".STACK")) { @@ -4055,8 +3983,7 @@ private static int tallyCasterlevelBonuses(final int casterLev, boolean replaceC if (zBonus.getBonus() > kBonus.getBonus()) { kBonus.setBonus(0); - } - else + } else { zBonus.setBonus(0); } @@ -4111,8 +4038,7 @@ public List addEqType(final List aList, final String aType /** * Adds a Kit to the applied list of kits for the character. * - * @param aKit - * The Kit to add. + * @param aKit The Kit to add. */ public void addKit(final Kit aKit) { @@ -4121,28 +4047,21 @@ public void addKit(final Kit aKit) } /** - * @param acs - * is the CharacterSpell object containing the spell which is to - * be modified - * @param aFeatList - * is the list of feats to be added to the SpellInfo object - * @param classKey - * is the name of the class whose list of character spells will - * be modified - * @param bookName - * is the name of the book for the SpellInfo object - * @param spellLevel - * is the original (unadjusted) level of the spell not including - * feat adjustments - * @param adjSpellLevel - * is the adjustedLevel (including feat adjustments) of this - * spell, it may be higher if the user chooses a higher level. - * + * @param acs is the CharacterSpell object containing the spell which is to + * be modified + * @param aFeatList is the list of feats to be added to the SpellInfo object + * @param classKey is the name of the class whose list of character spells will + * be modified + * @param bookName is the name of the book for the SpellInfo object + * @param spellLevel is the original (unadjusted) level of the spell not including + * feat adjustments + * @param adjSpellLevel is the adjustedLevel (including feat adjustments) of this + * spell, it may be higher if the user chooses a higher level. * @return an empty string on successful completion, otherwise the return - * value indicates the reason the add function failed. + * value indicates the reason the add function failed. */ public String addSpell(CharacterSpell acs, final List aFeatList, final String classKey, - final String bookName, final int adjSpellLevel, final int spellLevel) + final String bookName, final int adjSpellLevel, final int spellLevel) { if (acs == null) { @@ -4178,11 +4097,11 @@ public String addSpell(CharacterSpell acs, final List aFeatList, final if (aClass == null && spellBook.getType() == SpellBook.TYPE_SPELL_BOOK) { aClass = Globals.getContext().getReferenceContext().silentlyGetConstructedCDOMObject(PCClass.class, - classKey); + classKey); if ((aClass == null) && (classKey.lastIndexOf('(') >= 0)) { aClass = Globals.getContext().getReferenceContext().silentlyGetConstructedCDOMObject(PCClass.class, - classKey.substring(0, classKey.lastIndexOf('(')).trim()); + classKey.substring(0, classKey.lastIndexOf('(')).trim()); } } @@ -4218,7 +4137,7 @@ public String addSpell(CharacterSpell acs, final List aFeatList, final // But if a spell is both prohibited and in a speciality // which can be the case for some spells, then allow it. if (spellBook.getType() != SpellBook.TYPE_SPELL_BOOK && !acs.isSpecialtySpell(this) - && SpellCountCalc.isProhibited(aSpell, aClass, this)) + && SpellCountCalc.isProhibited(aSpell, aClass, this)) { return acs.getSpell().getDisplayName() + " is prohibited."; } @@ -4255,10 +4174,10 @@ public String addSpell(CharacterSpell acs, final List aFeatList, final // Explicitly should *not* set the dirty flag to true. spellLevelTemp = spellLevel; /* - * TODO Need to understand more about this context of formula - * resolution (in context of a spell??) in order to understand how - * to put this method into the Formula interface - */ + * TODO Need to understand more about this context of formula + * resolution (in context of a spell??) in order to understand how + * to put this method into the Formula interface + */ numPages = getVariableValue(acs, spellBook.getPageFormula().toString(), "").intValue(); // Check number of pages remaining in the book if (numPages + spellBook.getNumPagesUsed() > spellBook.getNumPages()) @@ -4267,9 +4186,8 @@ public String addSpell(CharacterSpell acs, final List aFeatList, final } spellBook.setNumPagesUsed(numPages + spellBook.getNumPagesUsed()); spellBook.setNumSpells(spellBook.getNumSpells() + 1); - } - else if (!aClass.getSafe(ObjectKey.MEMORIZE_SPELLS) - && !availableSpells(adjSpellLevel, aClass, bookName, true, acs.isSpecialtySpell(this))) + } else if (!aClass.getSafe(ObjectKey.MEMORIZE_SPELLS) + && !availableSpells(adjSpellLevel, aClass, bookName, true, acs.isSpecialtySpell(this))) { String ret; int maxAllowed; @@ -4278,11 +4196,10 @@ else if (!aClass.getSafe(ObjectKey.MEMORIZE_SPELLS) { ret = "Your remaining slot(s) must be filled with your speciality."; maxAllowed = known; - } - else + } else { ret = "You can only learn " + (known + specialKnown) + " spells for level " + adjSpellLevel - + " \nand there are no higher-level slots available."; + + " \nand there are no higher-level slots available."; maxAllowed = known + specialKnown; } int memTot = SpellCountCalc.memorizedSpellForLevelBook(this, aClass, adjSpellLevel, bookName); @@ -4292,9 +4209,8 @@ else if (!aClass.getSafe(ObjectKey.MEMORIZE_SPELLS) ret += "\n" + spellDifference + " spells from lower levels are using slots for this level."; } return ret; - } - else if (aClass.getSafe(ObjectKey.MEMORIZE_SPELLS) && !isDefault - && !availableSpells(adjSpellLevel, aClass, bookName, false, acs.isSpecialtySpell(this))) + } else if (aClass.getSafe(ObjectKey.MEMORIZE_SPELLS) && !isDefault + && !availableSpells(adjSpellLevel, aClass, bookName, false, acs.isSpecialtySpell(this))) { String ret; int maxAllowed; @@ -4302,16 +4218,14 @@ else if (aClass.getSafe(ObjectKey.MEMORIZE_SPELLS) && !isDefault { ret = "Your remaining slot(s) must be filled with your speciality or domain."; maxAllowed = this.getSpellSupport(aClass).getCastForLevel(adjSpellLevel, bookName, false, true, this); - } - else if (acs.isSpecialtySpell(this) && availableSpells(adjSpellLevel, aClass, bookName, false, false)) + } else if (acs.isSpecialtySpell(this) && availableSpells(adjSpellLevel, aClass, bookName, false, false)) { ret = "Your remaining slot(s) must be filled with spells not from your speciality or domain."; maxAllowed = this.getSpellSupport(aClass).getCastForLevel(adjSpellLevel, bookName, false, true, this); - } - else + } else { ret = "You can only prepare " + cast + " spells for level " + adjSpellLevel - + " \nand there are no higher-level slots available."; + + " \nand there are no higher-level slots available."; maxAllowed = cast; int memTot = SpellCountCalc.memorizedSpellForLevelBook(this, aClass, adjSpellLevel, bookName); int spellDifference = maxAllowed - memTot; @@ -4349,8 +4263,7 @@ else if (acs.isSpecialtySpell(this) && availableSpells(adjSpellLevel, aClass, bo { final CharacterSpell tcs = acsList.get(0); si = tcs.getSpellInfoFor(bookName, adjSpellLevel, aFeatList); - } - else + } else { si = acs.getSpellInfoFor(bookName, adjSpellLevel, aFeatList); } @@ -4367,14 +4280,12 @@ else if (acs.isSpecialtySpell(this) && availableSpells(adjSpellLevel, aClass, bo + "You cannot place spells in multiple times."; } si.setTimes(si.getTimes() + 1); - } - else + } else { if (isEmpty && !containsCharacterSpell(aClass, acs)) { addCharacterSpell(aClass, acs); - } - else if (isEmpty) + } else if (isEmpty) { // Make sure that we are working on the same spell object, not just the same spell for (CharacterSpell characterSpell : getCharacterSpells(aClass)) @@ -4472,7 +4383,7 @@ public boolean addTemplate(final PCTemplate inTemplate) final int formerGained = pi.getSkillPointsGained(this); pi.setSkillPointsGained(this, newSkillPointsGained); pi.setSkillPointsRemaining( - pi.getSkillPointsRemaining() + newSkillPointsGained - formerGained); + pi.getSkillPointsRemaining() + newSkillPointsGained - formerGained); setSkillPool(pcClass, pcClass.getSkillPool(this) + newSkillPointsGained - formerGained); } } @@ -4514,7 +4425,7 @@ public void adjustMoveRates() } public List aggregateSpellList(final String school, final String subschool, final String descriptor, - final int minLevel, final int maxLevel) + final int minLevel, final int maxLevel) { final List retList = new ArrayList<>(); @@ -4526,11 +4437,11 @@ public List aggregateSpellList(final String school, final String subschoo { final Spell aSpell = cs.getSpell(); SpellSchool ss = Globals.getContext().getReferenceContext() - .silentlyGetConstructedCDOMObject(SpellSchool.class, school); + .silentlyGetConstructedCDOMObject(SpellSchool.class, school); if ((school.isEmpty()) || (ss != null) && aSpell.containsInList(ListKey.SPELL_SCHOOL, ss) - || (subschool.isEmpty()) || aSpell.containsInList(ListKey.SPELL_SUBSCHOOL, subschool) - || (descriptor.isEmpty()) || aSpell.containsInList(ListKey.SPELL_DESCRIPTOR, descriptor)) + || (subschool.isEmpty()) || aSpell.containsInList(ListKey.SPELL_SUBSCHOOL, subschool) + || (descriptor.isEmpty()) || aSpell.containsInList(ListKey.SPELL_DESCRIPTOR, descriptor)) { retList.add(aSpell); } @@ -4648,7 +4559,7 @@ public int calcSR(final boolean includeEquipment) for (Equipment eq : getEquippedEquipmentSet()) { SR = Math.max(SR, - eq.getSafe(ObjectKey.SR).getReduction().resolve(this, eq.getQualifiedKey()).intValue()); + eq.getSafe(ObjectKey.SR).getReduction().resolve(this, eq.getQualifiedKey()).intValue()); for (EquipmentModifier eqMod : eq.getEqModifierList(true)) { @@ -4680,12 +4591,10 @@ public int calcSR(final boolean includeEquipment) * Method will go through the list of classes that the PC has and see if * they can cast spells of desired type at desired spell level. * - * @param spellType - * Spell type to check for - * @param spellLevel - * Desired spell level + * @param spellType Spell type to check for + * @param spellLevel Desired spell level * @return boolean

author David Wilson - * + * */ private boolean canCastSpellTypeLevel(final String spellType, final int spellLevel) { @@ -4694,7 +4603,7 @@ private boolean canCastSpellTypeLevel(final String spellType, final int spellLev FactKey fk = FactKey.valueOf("SpellType"); String classSpellType = aClass.getResolved(fk); if (classSpellType != null - && ("Any".equalsIgnoreCase(spellType) || classSpellType.equalsIgnoreCase(spellType))) + && ("Any".equalsIgnoreCase(spellType) || classSpellType.equalsIgnoreCase(spellType))) { // Get the number of known spells for the level int knownForLevel = this.getSpellSupport(aClass).getKnownForLevel(spellLevel, this); @@ -4715,7 +4624,7 @@ private boolean canCastSpellTypeLevel(final String spellType, final int spellLev // a CastList then they use something funky // like Power Points (psionic) if (!aClass.getSafe(ObjectKey.MEMORIZE_SPELLS) && !this.getSpellSupport(aClass).hasKnownList() - && this.getSpellSupport(aClass).canCastSpells(this)) + && this.getSpellSupport(aClass).canCastSpells(this)) { return true; } @@ -4729,10 +4638,8 @@ private boolean canCastSpellTypeLevel(final String spellType, final int spellLev * Method will go through the list of classes that the PC has and see if * they can cast spells of desired type at desired spell level. * - * @param spellType - * Spell type to check for - * @param spellLevel - * Desired spell level + * @param spellType Spell type to check for + * @param spellLevel Desired spell level * @return The number of spells castable **/ public int countSpellCastTypeLevel(final String spellType, final int spellLevel) @@ -4744,7 +4651,7 @@ public int countSpellCastTypeLevel(final String spellType, final int spellLevel) FactKey fk = FactKey.valueOf("SpellType"); String classSpellType = aClass.getResolved(fk); if (classSpellType != null - && ("Any".equalsIgnoreCase(spellType) || classSpellType.equalsIgnoreCase(spellType))) + && ("Any".equalsIgnoreCase(spellType) || classSpellType.equalsIgnoreCase(spellType))) { int numCastLevel = this.getSpellSupport(aClass).getCastForLevel(spellLevel, this); @@ -4763,7 +4670,7 @@ public int countSpellCastTypeLevel(final String spellType, final int spellLevel) // a CastList then they use something funky // like Power Points (psionic) if (!aClass.getSafe(ObjectKey.MEMORIZE_SPELLS) && !this.getSpellSupport(aClass).hasKnownList() - && this.getSpellSupport(aClass).canCastSpells(this)) + && this.getSpellSupport(aClass).canCastSpells(this)) { return Integer.MAX_VALUE; } @@ -4777,8 +4684,8 @@ public int countSpellCastTypeLevel(final String spellType, final int spellLevel) * Check whether a deity can be selected by this character * * @return {@code true} means the deity can be a selected by a - * character with the given properties; {@code false} means - * the character cannot. + * character with the given properties; {@code false} means + * the character cannot. */ public boolean canSelectDeity(final Deity aDeity) { @@ -4815,10 +4722,10 @@ public String delSpell(SpellInfo si, final PCClass aClass, final String bookName // there is some weird spell that keeps getting loaded by // accident (or is saved in the .pcg file) if (isDefault - && this.getSpellSupport(aClass).isAutoKnownSpell(acs.getSpell(), si.getActualLevel(), false, this)) + && this.getSpellSupport(aClass).isAutoKnownSpell(acs.getSpell(), si.getActualLevel(), false, this)) { Logging.errorPrint( - "Notice: removing " + acs.getSpell().getDisplayName() + " even though it is an auto known spell"); + "Notice: removing " + acs.getSpell().getDisplayName() + " even though it is an auto known spell"); } SpellBook spellBook = getSpellBookByName(bookName); @@ -4874,8 +4781,7 @@ public String delSpell(SpellInfo si, final PCClass aClass, final String bookName * author: Thomas Behr 09-03-02 * * @param check - * @param tokenString - * tokenString to parse + * @param tokenString tokenString to parse * @return the calculated save bonus */ public int calculateSaveBonus(final PCCheck check, final String tokenString) @@ -4896,19 +4802,16 @@ public int calculateSaveBonus(final PCCheck check, final String tokenString) if ("TOTAL".equals(tokens[i])) { save += getTotalCheck(check); - } - else if ("BASE".equals(tokens[i])) + } else if ("BASE".equals(tokens[i])) { save += getBaseCheck(check); - } - else if ("MISC".equals(tokens[i])) + } else if ("MISC".equals(tokens[i])) { String saveVar = ControlUtilities.getControlToken(Globals.getContext(), CControl.MISCSAVE); if (saveVar == null) { save += (int) getTotalBonusTo("SAVE", saveType); - } - else + } else { save += ((Number) getLocal(check, saveVar)).intValue(); } @@ -4920,8 +4823,7 @@ else if ("MISC".equals(tokens[i])) if (saveVar == null) { save += (int) getBonusDueToType("SAVE", saveType, "EPIC"); - } - else + } else { save += ((Number) getLocal(check, saveVar)).intValue(); } @@ -4933,8 +4835,7 @@ else if ("MISC".equals(tokens[i])) if (saveVar == null) { save += (int) getEquipmentBonusTo("SAVE", saveType); - } - else + } else { save += ((Number) getLocal(check, saveVar)).intValue(); } @@ -4946,8 +4847,7 @@ else if ("MISC".equals(tokens[i])) if (saveVar == null) { save += calculateSaveBonusRace(check); - } - else + } else { save += ((Number) getLocal(check, saveVar)).intValue(); } @@ -4958,9 +4858,8 @@ else if ("MISC".equals(tokens[i])) if (ControlUtilities.hasControlToken(Globals.getContext(), CControl.BASESAVE)) { Logging.errorPrint( - "FEATS is not a supported SAVE modification " + "when BASESAVE Code Control is used"); - } - else + "FEATS is not a supported SAVE modification " + "when BASESAVE Code Control is used"); + } else { save += (int) getFeatBonusTo("SAVE", saveType); } @@ -4972,8 +4871,7 @@ else if ("MISC".equals(tokens[i])) if (saveVar == null) { save += (int) checkBonusFacet.getCheckBonusTo(id, "SAVE", saveType); - } - else + } else { save += ((Number) getLocal(check, saveVar)).intValue(); } @@ -4988,8 +4886,7 @@ else if ("MISC".equals(tokens[i])) if (saveVar == null) { save -= (int) getBonusDueToType("SAVE", saveType, "EPIC"); - } - else + } else { save -= ((Number) getLocal(check, saveVar)).intValue(); } @@ -5001,8 +4898,7 @@ else if ("MISC".equals(tokens[i])) if (saveVar == null) { save -= (int) getEquipmentBonusTo("SAVE", saveType); - } - else + } else { save -= ((Number) getLocal(check, saveVar)).intValue(); } @@ -5014,8 +4910,7 @@ else if ("MISC".equals(tokens[i])) if (saveVar == null) { save -= calculateSaveBonusRace(check); - } - else + } else { save -= ((Number) getLocal(check, saveVar)).intValue(); } @@ -5026,9 +4921,8 @@ else if ("MISC".equals(tokens[i])) if (ControlUtilities.hasControlToken(Globals.getContext(), CControl.BASESAVE)) { Logging.errorPrint( - "NOFEATS is not a supported SAVE modification " + "when BASESAVE Code Control is used"); - } - else + "NOFEATS is not a supported SAVE modification " + "when BASESAVE Code Control is used"); + } else { save -= (int) getFeatBonusTo("SAVE", saveType); } @@ -5040,8 +4934,7 @@ else if ("MISC".equals(tokens[i])) if (saveVar == null) { save -= (int) checkBonusFacet.getCheckBonusTo(id, "SAVE", saveType); - } - else + } else { save -= ((Number) getLocal(check, saveVar)).intValue(); } @@ -5060,7 +4953,7 @@ else if ("MISC".equals(tokens[i])) public boolean delSpellBook(final String aName) { if ((!aName.isEmpty()) && !aName.equals(Globals.getDefaultSpellBook()) - && spellBookFacet.containsBookNamed(id, aName)) + && spellBookFacet.containsBookNamed(id, aName)) { processSpellBookRemoval(aName); return true; @@ -5106,25 +4999,22 @@ private void determinePrimaryOffWeapon() final boolean isEquipped = eq.isEquipped(); if ((eq.getLocation() == EquipmentLocation.EQUIPPED_PRIMARY) - || ((eq.getLocation() == EquipmentLocation.EQUIPPED_BOTH) && primaryWeaponFacet.isEmpty(id)) - || (eq.getLocation() == EquipmentLocation.EQUIPPED_TWO_HANDS)) + || ((eq.getLocation() == EquipmentLocation.EQUIPPED_BOTH) && primaryWeaponFacet.isEmpty(id)) + || (eq.getLocation() == EquipmentLocation.EQUIPPED_TWO_HANDS)) { if (isEquipped) { primaryWeaponFacet.add(id, eq); - } - else + } else { unequippedPrimary.add(eq); } - } - else if ((eq.getLocation() == EquipmentLocation.EQUIPPED_BOTH) && !primaryWeaponFacet.isEmpty(id)) + } else if ((eq.getLocation() == EquipmentLocation.EQUIPPED_BOTH) && !primaryWeaponFacet.isEmpty(id)) { if (isEquipped) { secondaryWeaponFacet.add(id, eq); - } - else + } else { unequippedSecondary.add(eq); } @@ -5135,8 +5025,7 @@ else if ((eq.getLocation() == EquipmentLocation.EQUIPPED_BOTH) && !primaryWeapon if (isEquipped) { secondaryWeaponFacet.add(id, eq); - } - else + } else { unequippedSecondary.add(eq); } @@ -5149,8 +5038,7 @@ else if ((eq.getLocation() == EquipmentLocation.EQUIPPED_BOTH) && !primaryWeapon if (isEquipped) { secondaryWeaponFacet.add(id, eq); - } - else + } else { unequippedSecondary.add(eq); } @@ -5259,10 +5147,8 @@ private int getClassHitPoints(PCClass pcClass, int iConMod) * with this method, also this method does not print warning messages see: * incrementClassLevel(int, PCClass, boolean, boolean); * - * @param mod - * the number of levels to add/remove - * @param aClass - * the class to adjust + * @param mod the number of levels to add/remove + * @param aClass the class to adjust */ public void incrementClassLevel(final int mod, final PCClass aClass) { @@ -5314,8 +5200,7 @@ public void makeIntoExClass(final PCClass fromClass) toClass = cl.clone(); //Still required :( bClassNew = true; toLevel = 0; - } - else + } else { bClassNew = false; toLevel = getLevel(toClass); @@ -5345,8 +5230,7 @@ public void makeIntoExClass(final PCClass fromClass) if (bClassNew) { classFacet.replaceClass(id, fromClass, toClass); - } - else + } else { classFacet.removeClass(id, fromClass); } @@ -5382,11 +5266,10 @@ public void makeIntoExClass(final PCClass fromClass) } setSkillPool(toClass, fromClass.getSkillPool(this)); - } - catch (NumberFormatException nfe) + } catch (NumberFormatException nfe) { ShowMessageDelegate.showMessageDialog(nfe.getMessage(), Constants.APPLICATION_NAME, - MessageType.INFORMATION); + MessageType.INFORMATION); } } @@ -5435,7 +5318,7 @@ public int modToACFromEquipment() /** * Calculate the ACCHECK bonus from equipped items. Extracted from * modToFromEquipment. - * + *

* TODO Penalty for load could/should be GameMode specific? * * @return PC's ACCHECK bonus from equipment @@ -5478,7 +5361,13 @@ public int processOldMaxDex() { final int statBonus = (int) getStatBonusTo("MISC", "MAXDEX"); final Load load = getHouseRuledLoadType(); - int bonus = (load == Load.MEDIUM) ? 2 : (load == Load.HEAVY) ? 1 : (load == Load.OVERLOAD) ? 0 : statBonus; + int bonus = switch (load) + { + case MEDIUM -> 3; + case HEAVY -> 1; + case OVERLOAD -> 0; + default -> statBonus; + }; // If this is still true after all the equipment has been // examined, then we should use the Maximum - Maximum Dex modifier. @@ -5507,8 +5396,7 @@ public int processOldMaxDex() if (bonus < 0) { bonus = 0; - } - else if (bonus > Constants.MAX_MAXDEX) + } else if (bonus > Constants.MAX_MAXDEX) { bonus = Constants.MAX_MAXDEX; } @@ -5519,7 +5407,7 @@ else if (bonus > Constants.MAX_MAXDEX) * Takes a String and a Class name and computes spell based variable such as * Class level. * - * @param aSpell The spell object + * @param aSpell The spell object * @param aString the variable to evaluate * @return String */ @@ -5546,8 +5434,7 @@ public String parseSpellString(final CharacterSpell aSpell, String aString) if (aString.charAt(i) == '(') { level++; - } - else if (aString.charAt(i) == ')') + } else if (aString.charAt(i) == ')') { level--; if (level == 0) @@ -5570,8 +5457,8 @@ else if (aString.charAt(i) == ')') final Float fVal = getVariableValue(aSpell, inCalc, aSpellClass); if (!CoreUtility.doublesEqual(fVal, 0.0f) || (inCalc.contains("MIN")) - || (inCalc.contains("MAX")) || inCalc.toUpperCase().contains("MIN(") - || inCalc.toUpperCase().contains("MAX(")) + || (inCalc.contains("MAX")) || inCalc.toUpperCase().contains("MIN(") + || inCalc.toUpperCase().contains("MAX(")) { found = true; replacement = String.valueOf(fVal.intValue()); @@ -5580,8 +5467,7 @@ else if (aString.charAt(i) == ')') if (found) { aString = aString.substring(0, start) + replacement + aString.substring(end + 1); - } - else + } else { aString = aString.substring(0, start) + '[' + inCalc + ']' + aString.substring(end + 1); } @@ -5894,7 +5780,7 @@ public int getNumAttacks() * @return double */ private double getPObjectWithCostBonusTo(final Collection aList, final String aType, - final String aName) + final String aName) { double iBonus = 0; @@ -5944,7 +5830,8 @@ public String getClassLevelString(String aClassKey, final boolean doReplace) { int totalLevels = 0; String[] classTypes = aClassKey.substring(5).split("\\."); - CLASSFOR: for (PCClass cl : getClassSet()) + CLASSFOR: + for (PCClass cl : getClassSet()) { for (String type : classTypes) { @@ -5961,8 +5848,7 @@ public String getClassLevelString(String aClassKey, final boolean doReplace) } } return Integer.toString(totalLevels); - } - else + } else { final PCClass aClass = getClassKeyed(aClassKey); @@ -6034,7 +5920,7 @@ public List getCDOMObjectList() if (isFeatureEnabled(CControl.DOMAINFEATURE)) { list.add((Deity) ChannelUtilities.readControlledChannel(getCharID(), - CControl.DEITYINPUT)); + CControl.DEITYINPUT)); } // Domain @@ -6092,24 +5978,19 @@ public List getCDOMObjectList() /** * availableSpells sk4p 13 Dec 2002 - * + *

* For learning or preparing a spell: Are there slots available at this * level or higher Fixes BUG [569517] * - * @param level - * the level being checked for availability - * @param aClass - * the class under consideration - * @param bookName - * the name of the spellbook - * @param knownLearned - * "true" if this is learning a spell, "false" if prepping - * @param isSpecialtySpell - * "true" if this is a speciality for the given class + * @param level the level being checked for availability + * @param aClass the class under consideration + * @param bookName the name of the spellbook + * @param knownLearned "true" if this is learning a spell, "false" if prepping + * @param isSpecialtySpell "true" if this is a speciality for the given class * @return true or false, a new spell can be added */ public boolean availableSpells(final int level, final PCClass aClass, final String bookName, - final boolean knownLearned, final boolean isSpecialtySpell) + final boolean knownLearned, final boolean isSpecialtySpell) { boolean available = false; FactKey fk = FactKey.valueOf("SpellType"); @@ -6140,8 +6021,7 @@ public boolean availableSpells(final int level, final PCClass aClass, final Stri { knownNon = this.getSpellSupport(aClass).getKnownForLevel(i, this); knownSpec = this.getSpellSupport(aClass).getSpecialtyKnownForLevel(i, this); - } - else + } else { // Get the number of castable slots knownTot = this.getSpellSupport(aClass).getCastForLevel(i, bookName, true, true, this); @@ -6216,8 +6096,7 @@ public boolean availableSpells(final int level, final PCClass aClass, final Stri { knownNon = this.getSpellSupport(aClass).getKnownForLevel(i, this); knownSpec = this.getSpellSupport(aClass).getSpecialtyKnownForLevel(i, this); - } - else + } else { // Get the number of castable slots knownTot = this.getSpellSupport(aClass).getCastForLevel(i, bookName, true, true, this); @@ -6321,8 +6200,7 @@ public boolean availableSpells(final int level, final PCClass aClass, final Stri { available = true; } - } - else if (isSpecialtySpell && (excNon + excSpec > 0)) + } else if (isSpecialtySpell && (excNon + excSpec > 0)) { available = true; } @@ -6355,14 +6233,14 @@ else if (isSpecialtySpell && (excNon + excSpec > 0)) /** * Compute total bonus from a List of BonusObjs Use cost of bonus to adjust * total bonus up or down This method takes a list of bonus objects. - * + *

* For each object in the list, it gets the creating object and queries it * for its "COST". It then multiplies the value of the bonus by this cost * and adds it to the cumulative total so far. If subSearch is true, the * choices made in the object that the bonus originated in are searched, the * effective bonus is multiplied by the number of times this bonus appears * in the list. - * + *

* Note: This COST seems to be used for several different things in the code * base, in feats for instance, it is used to modify the feat pool by * amounts other than 1 when selecting a given feat. Here it is used as a @@ -6370,8 +6248,7 @@ else if (isSpecialtySpell && (excNon + excSpec > 0)) * of 0.5 counts for half its normal value. The COST is limited to a max of * 1, so it can only make bonuses less effective. * - * @param aList - * a list of bonus objects + * @param aList a list of bonus objects * @return the calculated cumulative bonus */ private double calcBonusWithCostFromList(final List aList) @@ -6419,8 +6296,7 @@ public int countSpellsInBook(final String aString) if (aTok.hasMoreTokens()) { levelNum = Integer.parseInt(aTok.nextToken()); - } - else + } else { levelNum = -1; } @@ -6449,8 +6325,7 @@ public SizeAdjustment getSizeAdjustment() if (sizeControl != null) { return (SizeAdjustment) getGlobal(sizeControl); - } - else + } else { return sizeFacet.get(id); } @@ -6495,17 +6370,17 @@ public boolean includeSkill(final Skill skill, final SkillFilter filter) } return switch (filter) - { - case Ranks -> (SkillRankControl.getTotalRank(this, skill) > 0); - case NonDefault -> ( - SkillRankControl.getTotalRank(this, skill) > 0 - || SkillModifier.modifier(skill, this) != SkillModifier.getStatMod(skill, this) - + getSizeAdjustmentBonusTo("SKILL", skill.getKeyName())); - case Usable -> qualifySkill(skill) && ( - SkillRankControl.getTotalRank(this, skill) > 0 - || skill.getSafe(ObjectKey.USE_UNTRAINED)); - default -> qualifySkill(skill); - }; + { + case Ranks -> (SkillRankControl.getTotalRank(this, skill) > 0); + case NonDefault -> ( + SkillRankControl.getTotalRank(this, skill) > 0 + || SkillModifier.modifier(skill, this) != SkillModifier.getStatMod(skill, this) + + getSizeAdjustmentBonusTo("SKILL", skill.getKeyName())); + case Usable -> qualifySkill(skill) && ( + SkillRankControl.getTotalRank(this, skill) > 0 + || skill.getSafe(ObjectKey.USE_UNTRAINED)); + default -> qualifySkill(skill); + }; } private boolean qualifySkill(final Skill skill) @@ -6520,13 +6395,9 @@ private boolean qualifySkill(final Skill skill) * with this method, see: incrementClassLevel(int, PCClass, boolean, * boolean); * - * - * @param numberOfLevels - * number of levels to add - * @param globalClass - * the class to add the levels to - * @param bSilent - * whether or not to display warning messages + * @param numberOfLevels number of levels to add + * @param globalClass the class to add the levels to + * @param bSilent whether or not to display warning messages */ public void incrementClassLevel(final int numberOfLevels, final PCClass globalClass, final boolean bSilent) { @@ -6538,25 +6409,21 @@ public void incrementClassLevel(final int numberOfLevels, final PCClass globalCl * It is assumed that this method is not used as part of loading a * previously saved character. * - * @param numberOfLevels - * The number of levels to add or remove. If a positive number is - * passed in then that many levels will be added. If the number - * of levels passed in is negative then that many levels will be - * removed from the specified class. - * @param globalClass - * The global class from the data store. The class as stored in - * the character will be compared to this one using the - * getClassNamed() method - * @param bSilent - * If true do not display any warning messages about adding or - * removing too many levels - * @param bypassPrereqs - * Whether we should bypass the checks as to whether or not the - * PC qualifies to take this class. If true, the checks will be - * bypassed + * @param numberOfLevels The number of levels to add or remove. If a positive number is + * passed in then that many levels will be added. If the number + * of levels passed in is negative then that many levels will be + * removed from the specified class. + * @param globalClass The global class from the data store. The class as stored in + * the character will be compared to this one using the + * getClassNamed() method + * @param bSilent If true do not display any warning messages about adding or + * removing too many levels + * @param bypassPrereqs Whether we should bypass the checks as to whether or not the + * PC qualifies to take this class. If true, the checks will be + * bypassed */ public void incrementClassLevel(final int numberOfLevels, final PCClass globalClass, final boolean bSilent, - final boolean bypassPrereqs) + final boolean bypassPrereqs) { // If not importing, load the spell list if (!importing) @@ -6574,12 +6441,12 @@ public void incrementClassLevel(final int numberOfLevels, final PCClass globalCl Race race = getRace(); if (globalClass.isMonster() && !SettingsHandler.isIgnoreMonsterHDCap() && !race.isAdvancementUnlimited() - && ((totalHitDice() + numberOfLevels) > race.maxHitDiceAdvancement()) && !bSilent) + && ((totalHitDice() + numberOfLevels) > race.maxHitDiceAdvancement()) && !bSilent) { ShowMessageDelegate.showMessageDialog( - "Cannot increase Monster Hit Dice for this character beyond " + race.maxHitDiceAdvancement() - + ". This character's current number of Monster Hit Dice is " + totalHitDice(), - Constants.APPLICATION_NAME, MessageType.INFORMATION); + "Cannot increase Monster Hit Dice for this character beyond " + race.maxHitDiceAdvancement() + + ". This character's current number of Monster Hit Dice is " + totalHitDice(), + Constants.APPLICATION_NAME, MessageType.INFORMATION); return; } @@ -6601,7 +6468,7 @@ public void incrementClassLevel(final int numberOfLevels, final PCClass globalCl if (pcClassClone == null) { Logging.errorPrint("PlayerCharacter::incrementClassLevel => " + "Clone of class " - + globalClass.getKeyName() + " failed!"); + + globalClass.getKeyName() + " failed!"); return; } @@ -6614,8 +6481,7 @@ public void incrementClassLevel(final int numberOfLevels, final PCClass globalCl // Add the class to the character classes as level 0 classFacet.addClass(id, pcClassClone); - } - else + } else { // mod is < 0 and character does not have class. Return. return; @@ -6636,8 +6502,7 @@ public void incrementClassLevel(final int numberOfLevels, final PCClass globalCl return; } } - } - else if (numberOfLevels < 0) + } else if (numberOfLevels < 0) { for (int i = 0; i < -numberOfLevels; ++i) { @@ -6683,8 +6548,7 @@ private PCLevelInfo removeLevelInfo(final String classKeyName) * drop lowest 5: 4d6 reroll 1's and 2's drop lowest 6: 3d6 +5 7: 5d6 Drop * lowest and middle as per FREQ #458917 * - * @param method - * the method to be used for rolling. + * @param method the method to be used for rolling. */ public final void rollStats(final int method) { @@ -6693,11 +6557,12 @@ public final void rollStats(final int method) { aMethod = Constants.CHARACTER_STAT_METHOD_PURCHASE; } - rollStats(aMethod, statFacet.getSet(id), SettingsHandler.getGameAsProperty().get().getCurrentRollingMethod(), false); + rollStats(aMethod, statFacet.getSet(id), SettingsHandler.getGameAsProperty().get().getCurrentRollingMethod(), + false); } public void rollStats(final int method, final Collection aStatList, final RollMethod rollMethod, - boolean aSortedFlag) + boolean aSortedFlag) { int[] rolls = new int[aStatList.size()]; @@ -6709,7 +6574,8 @@ public void rollStats(final int method, final Collection aStatList, fina SettingsHandler.getGameAsProperty().get().getPurchaseModeBaseStatScore(this); case Constants.CHARACTER_STAT_METHOD_ALL_THE_SAME -> rolls[i] = SettingsHandler.getGameAsProperty().get().getAllStatsValue(); - case Constants.CHARACTER_STAT_METHOD_ROLLED -> { + case Constants.CHARACTER_STAT_METHOD_ROLLED -> + { final String diceExpression = rollMethod.getMethodRoll(); rolls[i] = RollingMethods.roll(diceExpression); } @@ -6763,10 +6629,8 @@ public void rollStats(final int method, final Collection aStatList, fina * of equipment have the same outputIndex they will be ordered by name. Note * hidden items (outputIndex = -1) are not included in list. * - * @param unsortedEquip - * An ArrayList of the equipment to be sorted. - * @param merge - * How to merge. + * @param unsortedEquip An ArrayList of the equipment to be sorted. + * @param merge How to merge. * @return An ArrayList of the equipment objects in output order. */ private static List sortEquipmentList(final Collection unsortedEquip, final int merge) @@ -6911,12 +6775,9 @@ public Set getLanguageBonusSelectionList() * to all included bonuses. If not excluding either, it is quicker to use * getTotalBonusTo. * - * @param stat - * The stat to calculate the bonus for. - * @param useTemp - * Should temp bonuses be included? - * @param useEquip - * Should equipment bonuses be included? + * @param stat The stat to calculate the bonus for. + * @param useTemp Should temp bonuses be included? + * @param useEquip Should equipment bonuses be included? * @return The bonus to the stat. */ public int getPartialStatFor(PCStat stat, boolean useTemp, boolean useEquip) @@ -6931,16 +6792,11 @@ public int getPartialStatFor(PCStat stat, boolean useTemp, boolean useEquip) * stacking rules are applied to all included bonuses. If not excluding * either, it is quicker to use getTotalStatAtLevel. * - * @param stat - * The stat to calculate the value of. - * @param level - * The level we want to see the stat at. - * @param usePost - * Should stat mods that occurred after levelling be included? - * @param useTemp - * Should temp bonuses be included? - * @param useEquip - * Should equipment bonuses be included? + * @param stat The stat to calculate the value of. + * @param level The level we want to see the stat at. + * @param usePost Should stat mods that occurred after levelling be included? + * @param useTemp Should temp bonuses be included? + * @param useEquip Should equipment bonuses be included? * @return The stat as it was at the level */ public int getPartialStatAtLevel(PCStat stat, int level, boolean usePost, boolean useTemp, boolean useEquip) @@ -6975,8 +6831,7 @@ public PlayerCharacter clone() try { aClone.assocSupt = assocSupt.clone(); - } - catch (CloneNotSupportedException e) + } catch (CloneNotSupportedException e) { Logging.errorPrint("PlayerCharacter.clone failed", e); } @@ -7001,8 +6856,7 @@ public PlayerCharacter clone() if (followerMaster != null) { aClone.masterFacet.set(id, followerMaster.clone()); - } - else + } else { aClone.masterFacet.remove(id); } @@ -7138,6 +6992,7 @@ private String getSingleLocation(Equipment eqI) /** * Identify the equipping location for a natural weapon. + * * @param eqI The natural weapon * @return The location name, or null if not a natural weapon. */ @@ -7178,8 +7033,8 @@ private boolean canEquipItem(EquipSet eSet, String locName, Equipment eqI, Equip // If Carried/Equipped/Not Carried slot // allow as many as they would like if (locName.startsWith(Constants.EQUIP_LOCATION_CARRIED) - || locName.startsWith(Constants.EQUIP_LOCATION_EQUIPPED) - || locName.startsWith(Constants.EQUIP_LOCATION_NOTCARRIED)) + || locName.startsWith(Constants.EQUIP_LOCATION_EQUIPPED) + || locName.startsWith(Constants.EQUIP_LOCATION_NOTCARRIED)) { return true; } @@ -7256,7 +7111,7 @@ private boolean canEquipItem(EquipSet eSet, String locName, Equipment eqI, Equip // if Double Weapon or Both Hands, then no // other weapon slots can be occupied if ((locName.equals(Constants.EQUIP_LOCATION_BOTH) || locName.equals(Constants.EQUIP_LOCATION_DOUBLE)) - && (es.getName().equals(Constants.EQUIP_LOCATION_PRIMARY) + && (es.getName().equals(Constants.EQUIP_LOCATION_PRIMARY) || es.getName().equals(Constants.EQUIP_LOCATION_SECONDARY) || es.getName().equals(Constants.EQUIP_LOCATION_BOTH) || es.getName().equals(Constants.EQUIP_LOCATION_DOUBLE))) @@ -7266,8 +7121,8 @@ private boolean canEquipItem(EquipSet eSet, String locName, Equipment eqI, Equip // inverse of above case if ((locName.equals(Constants.EQUIP_LOCATION_PRIMARY) - || locName.equals(Constants.EQUIP_LOCATION_SECONDARY)) - && (es.getName().equals(Constants.EQUIP_LOCATION_BOTH) + || locName.equals(Constants.EQUIP_LOCATION_SECONDARY)) + && (es.getName().equals(Constants.EQUIP_LOCATION_BOTH) || es.getName().equals(Constants.EQUIP_LOCATION_DOUBLE))) { return false; @@ -7390,14 +7245,13 @@ public int getNewChildId(String pid) } public EquipSet addEquipToTarget(final EquipSet eSet, final Equipment eqTarget, String locName, final Equipment eqI, - Float newQty) + Float newQty) { float tempQty = 1.0f; if (newQty != null) { tempQty = newQty; - } - else + } else { newQty = tempQty; } @@ -7443,8 +7297,7 @@ public EquipSet addEquipToTarget(final EquipSet eSet, final Equipment eqTarget, locName = eqTarget.getName(); addAll = true; mergeItem = true; - } - else + } else { return null; } @@ -7527,6 +7380,7 @@ else if (locName.equalsIgnoreCase("Equipped")) /** * Move the equipset to a new unique path under its existing parent. + * * @param es The equipment set item to be moved. */ public void moveEquipSetToNewPath(EquipSet es) @@ -7561,8 +7415,7 @@ public String getSafeStringFor(PCStringKey key) * Note: This is kind of a hack used by the Kit code to allow a kit * to specify what the level abilities are. * - * @param yesNo - * Yes if level increases should process ADD: level abilities. + * @param yesNo Yes if level increases should process ADD: level abilities. */ public void setDoLevelAbilities(boolean yesNo) { @@ -7622,8 +7475,7 @@ public void adjustAbilities(final Category aCategory, final BigDecimal if (userMods != null) { userMods = userMods.add(arg); - } - else + } else { userMods = arg; } @@ -7661,8 +7513,7 @@ public BigDecimal getTotalAbilityPool(final AbilityCategory aCategory) if (AbilityCategory.FEAT.equals(aCategory)) { bonus = getBonusFeatPool(); - } - else + } else { bonus = getTotalBonusTo("ABILITYPOOL", aCategory.getKeyName()); } @@ -7736,16 +7587,14 @@ private BigDecimal getAbilityPoolSpent(final AbilityCategory aCategory) if (ChooseActivation.hasNewChooseToken(ability)) { spent += Math.ceil(subfeatCount * cost); - } - else + } else { int select = ability.getSafe(FormulaKey.SELECT).resolve(this, "").intValue(); double relativeCost = cost / select; if (aCategory.allowFractionalPool()) { spent += relativeCost; - } - else + } else { spent += (int) Math.ceil(relativeCost); } @@ -7870,7 +7719,7 @@ private void processAbilityList(CDOMObject cdo, CDOMReference ref) if (ab == null) { Logging.log(Logging.LST_ERROR, "Missing object referenced in the ability list for '" + cdo - + "' list is " + ref + ". Source " + cdo.getSourceURI()); + + "' list is " + ref + ". Source " + cdo.getSourceURI()); continue; } for (AssociatedPrereqObject apo : assoc) @@ -7888,8 +7737,7 @@ private void processAbilityList(CDOMObject cdo, CDOMReference ref) new CNAbilitySelection(CNAbilityFactory.getCNAbility(cat, nature, ab), ""); cas.addAllPrerequisites(apo.getPrerequisiteList()); applyAbility(cas, cdo); - } - else + } else { for (final String choice : choices) { @@ -7899,8 +7747,7 @@ private void processAbilityList(CDOMObject cdo, CDOMReference ref) applyAbility(cas, cdo); } } - } - else + } else { CNAbilitySelection cas = new CNAbilitySelection(CNAbilityFactory.getCNAbility(cat, nature, ab)); cas.addAllPrerequisites(apo.getPrerequisiteList()); @@ -7918,8 +7765,7 @@ public void applyAbility(CNAbilitySelection cas, Object source) if (cas.hasPrerequisites()) { conditionalFacet.add(id, cas, source); - } - else + } else { directAbilityFacet.add(id, cas, source); } @@ -8149,7 +7995,7 @@ public boolean hasNonStatStat(PCStat stat) // Check for a non stat, but only if it hasn't been reset to a stat if (!nonStatToStatFacet.contains(id, stat)) { - return nonStatStatFacet.contains(id, stat); + return nonStatStatFacet.contains(id, stat); } return false; } @@ -8169,7 +8015,7 @@ public String getDescription(PObject pobj) return getDescription(Collections.singletonList(pobj)); } - public String getDescription(List objList) + public String getDescription(List objList) { if (objList.isEmpty()) { @@ -8177,11 +8023,18 @@ public String getDescription(List objList) } PObject cdo; Object b = objList.get(0); - if (b instanceof CNAbility) + if (b instanceof PObject) + { + cdo = (PObject) b; + String dString = getInfoToken(".INFO.DESC", cdo); + if (!dString.equals(".INFO.DESC")) + { + return dString; + } + } else if (b instanceof CNAbility) { cdo = ((CNAbility) b).getAbility(); - } - else + } else { Logging.errorPrint("Unable to resolve Description with object of type: " + b.getClass().getName()); return Constants.EMPTY_STRING; @@ -8210,20 +8063,28 @@ public String getDescription(List objList) return sb.toString(); } - public String getInfoToken(String token, PObject po) { + public String getInfoToken(String token, PObject po) + { // looking for a token in the form of RACE.INFO.TAG where // RACE indicate which token map to check for a INFO label of TAG to return int i = token.indexOf(".INFO."); String ts = token; if (i>-1) - ts = token.substring(i+6); + { + ts = token.substring(i + 6); + } else + { return token; + } Set> keys = po.getMapKeys(); - for (MapKey key : keys) { + for (MapKey key : keys) + { Map key2 = po.getMapFor(key); - for(Object k : key2.keySet()) { - if (k.toString().equalsIgnoreCase(ts)) { + for(Object k : key2.keySet()) + { + if (k.toString().equalsIgnoreCase(ts)) + { MessageFormat m = (MessageFormat) key2.get(k); return m.toPattern(); } @@ -8238,9 +8099,8 @@ public String getInfoToken(String token, PObject po) { * domains may cast the spell. * * @param sp The spell to get the info for. - * * @return Map containing the class levels and domains that may cast the - * spell + * spell */ public HashMapToList, Integer> getSpellLevelInfo(Spell sp) { @@ -8257,7 +8117,8 @@ public HashMapToList, Integer> getSpellLevelInfo(Spell sp) /** * Retrieve the character's existing version of this spell, if any. - * @param po The source of the spell list for this spell (normally a PCClass) + * + * @param po The source of the spell list for this spell (normally a PCClass) * @param spell The spell to be retrieved * @param owner The source of the spell (either the PCClass or the Domian) * @return The character's existing instance of the spell, or null if none. @@ -8281,6 +8142,7 @@ public CharacterSpell getCharacterSpellForSpell(PObject po, Spell spell, PObject /** * Get a list of CharacterSpells from the character spell list. + * * @param spellSource * @param aSpell * @param book @@ -8288,7 +8150,7 @@ public CharacterSpell getCharacterSpellForSpell(PObject po, Spell spell, PObject * @return list of CharacterSpells from the character spell list */ public final List getCharacterSpells(CDOMObject spellSource, final Spell aSpell, final String book, - final int level) + final int level) { List csList = new ArrayList<>(getCharacterSpells(spellSource)); // Add in the spells granted by objects @@ -8317,6 +8179,7 @@ public final List getCharacterSpells(CDOMObject spellSource, fin /** * Returns DC for a spell and SpellInfo. + * * @param sp the spell * @param cs TODO * @param si the spell info @@ -8467,6 +8330,7 @@ public int getDC(final Spell sp, PCClass aClass, int spellLevel, int metaDC, CDO /** * Returns concentration bonus for a spell and SpellInfo. + * * @param sp the spell * @param cs TODO * @param si the spell info @@ -8495,7 +8359,7 @@ public int getConcentration(final Spell sp, CharacterSpell cs, final SpellInfo s } public int getConcentration(final Spell sp, final CharacterSpell aSpell, PCClass aClass, int spellLevel, - int metaConcentration, CDOMObject ow) + int metaConcentration, CDOMObject ow) { String bonDomain = ""; if (ow instanceof Domain) @@ -8539,8 +8403,9 @@ public int getConcentration(final Spell sp, final CharacterSpell aSpell, PCClass // must be done after spellLevel is set above int concentration = - getVariableValue(aSpell, SettingsHandler.getGameAsProperty().get().getSpellBaseConcentration(), classKey).intValue() - + metaConcentration; + getVariableValue(aSpell, SettingsHandler.getGameAsProperty().get().getSpellBaseConcentration(), + classKey).intValue() + + metaConcentration; concentration += (int) getTotalBonusTo("CONCENTRATION", "ALLSPELLS"); if (useStatFromSpell) @@ -8713,8 +8578,7 @@ public void setApplied(BonusObj bonusObj, boolean bool) if (bool) { appliedBonusFacet.add(id, bonusObj); - } - else + } else { appliedBonusFacet.remove(id, bonusObj); } @@ -8727,8 +8591,7 @@ public void setSubstitutionLevel(PCClass pcc, PCClassLevel originalClassLevel) PCClassLevel clvl = originalClassLevel.clone(); clvl.put(StringKey.QUALIFIED_KEY, pcc.getQualifiedKey()); classFacet.setClassLevel(id, pcc, clvl); - } - catch (CloneNotSupportedException e) + } catch (CloneNotSupportedException e) { Logging.errorPrint(e.getLocalizedMessage(), e); } @@ -9103,7 +8966,7 @@ public Collection getBonusContainerList() if (gameMode.isPurchaseStatMode()) { PointBuyMethod pbm = gameMode.getContext().getReferenceContext() - .silentlyGetConstructedCDOMObject(PointBuyMethod.class, gameMode.getPurchaseModeMethodName()); + .silentlyGetConstructedCDOMObject(PointBuyMethod.class, gameMode.getPurchaseModeMethodName()); list.add(pbm); } return list; @@ -9136,8 +8999,7 @@ public void reInheritClassLevels(PCClass pcc) { classFacet.setClassLevel(id, pcc, pcl); } - } - catch (CloneNotSupportedException e) + } catch (CloneNotSupportedException e) { Logging.errorPrint(e.getLocalizedMessage(), e); } @@ -9161,7 +9023,8 @@ public void checkSkillModChange() if (currClass == null) { Logging - .errorPrint("No PCClass found for '" + classKeyName + "' in character's class list: " + newClasses); + .errorPrint( + "No PCClass found for '" + classKeyName + "' in character's class list: " + newClasses); return; } PCClassLevel classLevel = getActiveClassLevel(currClass, lvlInfo.getClassLevel()); @@ -9170,7 +9033,7 @@ public void checkSkillModChange() } public void checkSkillModChangeForLevel(PCClass pcClass, PCLevelInfo pi, PCClassLevel classLevel, - int characterLevel) + int characterLevel) { int newSkillPointsGained = pcClass.getSkillPointsForLevel(this, classLevel, characterLevel); if (pi.getClassKeyName().equals(pcClass.getKeyName())) @@ -9188,6 +9051,7 @@ public void checkSkillModChangeForLevel(PCClass pcClass, PCLevelInfo pi, PCClass /** * Add a chronicle entry. + * * @param chronicleEntry The entry to be added. */ public void addChronicleEntry(ChronicleEntry chronicleEntry) @@ -9197,6 +9061,7 @@ public void addChronicleEntry(ChronicleEntry chronicleEntry) /** * Remove a chronicle entry. + * * @param chronicleEntry The entry to be removed. */ public void removeChronicleEntry(ChronicleEntry chronicleEntry) @@ -9274,7 +9139,7 @@ public void addDefaultSpellList(PCClass pcc) } double getSizeBonusTo(SizeAdjustment sizeAdjustment, final String bonusType, final List typeList, - double defaultValue) + double defaultValue) { for (String type : typeList) { @@ -9300,7 +9165,7 @@ public void addDefaultSpellList(PCClass pcc) * Adds to the provided list any spells that have been granted to the character's class by abilities * through the use of SPELLKNOWN:CLASS tags. * - * @param aClass The character class owning the spell list. + * @param aClass The character class owning the spell list. * @param cSpells The list of spells to be updated. */ public void addBonusKnownSpellsToList(CDOMObject aClass, List cSpells) @@ -9395,8 +9260,7 @@ public int recalcSkillPointMod(PCClass pcClass, final int characterLevel) if (lockedMonsterSkillPoints > 0) { spMod = lockedMonsterSkillPoints; - } - else if (characterLevel == 1) + } else if (characterLevel == 1) { int monSkillPts = (int) getTotalBonusTo("MONSKILLPTS", "NUMBER"); if (monSkillPts != 0) @@ -9422,9 +9286,9 @@ else if (characterLevel == 1) int otherSp = spMod - classSp - raceSp; // should mostly be stat related skillpoints final int classSpMin = (int) getTotalBonusTo("MINCLASSSKILLPOINTS", "NUMBER"); // if a MINCLASSSKILLPOINTS.NUMBER is defined and spMod was lower due to INT penalty - if (classSpMin>0 && (classSp+otherSp 0 && (classSp + otherSp < classSpMin)) { - spMod = Math.max(classSpMin,classSp+otherSp)+raceSp; + spMod = Math.max(classSpMin, classSp + otherSp) + raceSp; } if (characterLevel == 1) @@ -9440,8 +9304,7 @@ else if (characterLevel == 1) // Only generate a random age if the user hasn't set one! bioSetFacet.get(id).randomize("AGE", this); } - } - else + } else { spMod *= Globals.getSkillMultiplierForLevel(characterLevel); } @@ -9563,6 +9426,7 @@ public void setSkillRankValue(Skill sk, PCClass pcc, double value) /** * Retrieve the classes that have ranks in this skill. NB: For granted ranks * this may include null. + * * @param sk The skill to be checked. * @return The collection of classes with ranks - may include null as a PCClass. */ @@ -9673,17 +9537,16 @@ public void calculateKnownSpellsForClassLevel(PCClass pcc) cs = new CharacterSpell(pcc, spell); cs.addInfo(spellLevel, 1, Globals.getDefaultSpellBook()); addCharacterSpell(pcc, cs); - } - else + } else { if (cs.getSpellInfoFor(Globals.getDefaultSpellBook(), spellLevel) == null) { cs.addInfo(spellLevel, 1, Globals.getDefaultSpellBook()); - } - else + } else { // already know this one - Logging.log(Logging.DEBUG, "We already know about the level: " + spellLevel + " of this character spell, it's in the global default spellbook already."); + Logging.debugPrint("We already know about the level: " + spellLevel + + " of this character spell, it's in the global default spellbook already."); } } } @@ -9840,15 +9703,14 @@ public void addSavedAbility(CNAbilitySelection cnas, Object owner, Object locati addAbility(cnas, owner, location); } - public void addAbility(CNAbilitySelection cnas, @SuppressWarnings("UnusedParameters") - final Object owner, Object location) + public void addAbility(CNAbilitySelection cnas, @SuppressWarnings("UnusedParameters") final Object owner, + Object location) { //TODO Need to handle owner if (cnas.hasPrerequisites()) { conditionalFacet.add(id, cnas, location); - } - else + } else { directAbilityFacet.add(id, cnas, location); } @@ -9859,14 +9721,13 @@ public void addAbility(CNAbilitySelection cnas, @SuppressWarnings("UnusedParamet } public void removeAbility(CNAbilitySelection cnas, @SuppressWarnings("UnusedParameters") Object owner, - Object location) + Object location) { //TODO Need to handle owner if (cnas.hasPrerequisites()) { conditionalFacet.remove(id, cnas, location); - } - else + } else { directAbilityFacet.remove(id, cnas, location); } @@ -9892,12 +9753,10 @@ public List getConsolidatedAssociationList(CDOMObject cdo) list.addAll(getAssociationList(cna)); } return list; - } - else if (cdo instanceof ChooseDriver) + } else if (cdo instanceof ChooseDriver) { return getAssociationList((ChooseDriver) cdo); - } - else + } else { //Can't really do a message here because this is heavily done by BonusManager // Logging @@ -9931,17 +9790,16 @@ public String getControl(String string) * Returns the value of a CodeControl, or the default value if the code control has * not been set in the data. * - * @param control - * The CodeControl for which the value should be returned + * @param control The CodeControl for which the value should be returned * @return The value of a CodeControl, or the default value if the code control has - * not been set in the data + * not been set in the data */ public String getControl(CControl control) { ObjectKey ok = ObjectKey.getKeyFor(String.class, - "*" + Objects.requireNonNull(control.getName())); + "*" + Objects.requireNonNull(control.getName())); return Optional.ofNullable(controller.get(ok)) - .orElse(control.getDefaultValue()); + .orElse(control.getDefaultValue()); } public boolean hasControl(String string) @@ -9952,24 +9810,22 @@ public boolean hasControl(String string) /** * Returns true if a feature code control is enabled. * - * @param feature - * The feature code control for which the value should be returned + * @param feature The feature code control for which the value should be returned * @return true if a feature code control is enabled; false otherwise */ public boolean isFeatureEnabled(String feature) { Boolean object = controller.get(ObjectKey.getKeyFor(Boolean.class, - "*" + Objects.requireNonNull(feature))); + "*" + Objects.requireNonNull(feature))); return (object != null) && object; } /** * Directly solves the given NEPFormula in context to this PlayerCharacter. * - * @param formula - * The NEPFormula to be solved + * @param formula The NEPFormula to be solved * @return The result of solving the given NEPFormula in context to this - * PlayerCharacter + * PlayerCharacter */ public T solve(NEPFormula formula) { @@ -9977,7 +9833,6 @@ public T solve(NEPFormula formula) } /** - * * @return the racial size */ public int racialSizeInt() diff --git a/code/src/java/pcgen/core/term/PCCasterLevelTotalTermEvaluator.java b/code/src/java/pcgen/core/term/PCCasterLevelTotalTermEvaluator.java index 4579b747598..eb66c8fd3e0 100644 --- a/code/src/java/pcgen/core/term/PCCasterLevelTotalTermEvaluator.java +++ b/code/src/java/pcgen/core/term/PCCasterLevelTotalTermEvaluator.java @@ -21,7 +21,6 @@ package pcgen.core.term; import pcgen.cdom.base.Constants; -import pcgen.core.Equipment; import pcgen.core.PCClass; import pcgen.core.PlayerCharacter; import pcgen.core.character.CharacterSpell; diff --git a/code/src/java/pcgen/gui2/PCGenFrame.java b/code/src/java/pcgen/gui2/PCGenFrame.java index 2160dee417e..f9fc90baa7c 100644 --- a/code/src/java/pcgen/gui2/PCGenFrame.java +++ b/code/src/java/pcgen/gui2/PCGenFrame.java @@ -85,7 +85,7 @@ import pcgen.gui2.util.ShowMessageGuiObserver; import pcgen.gui3.GuiAssertions; import pcgen.gui3.GuiUtility; -import pcgen.gui3.JFXPanelFromResource; +import pcgen.gui3.PanelFromResource; import pcgen.gui3.component.PCGenToolBar; import pcgen.gui3.dialog.AboutDialog; import pcgen.gui3.dialog.RememberingChoiceDialog; @@ -243,12 +243,11 @@ public void run() if (!alternateStartup) { //Do a default startup + if (TipOfTheDayHandler.shouldShowTipOfTheDay()) + { + Platform.runLater(PCGenFrame::showTipsOfTheDay); + } SwingUtilities.invokeLater(() -> { - if (TipOfTheDayHandler.shouldShowTipOfTheDay()) - { - showTipsOfTheDay(); - } - if (!SourceSelectionDialog.skipSourceSelection()) { showSourceSelectionDialog(); @@ -1296,7 +1295,7 @@ private void updateTitle() */ static void showTipsOfTheDay() { - var totd = new JFXPanelFromResource<>( + var totd = new PanelFromResource<>( TipOfTheDayController.class, "TipOfTheDay.fxml" ); @@ -1560,6 +1559,7 @@ public void run() updateTitle(); } + // TODO there are no examples with licenses. Either remove this or add a test. private void showLicenses() { PropertyContext context = PCGenSettings.OPTIONS_CONTEXT; @@ -1576,11 +1576,8 @@ private void showLicenses() { showLicenseDialog(LanguageBundle.getString("in_specialLicenses"), licenses); //$NON-NLS-1$ } - for (String license : loader.getOtherLicenses()) - { - showLicenseDialog(LanguageBundle.getString("in_specialLicenses"), license); //$NON-NLS-1$ - } - + loader.getOtherLicenses() + .forEach(license -> showLicenseDialog(LanguageBundle.getString("in_specialLicenses"), license)); //$NON-NLS-1$ } } if (loader.hasMatureCampaign() && context.initBoolean(PCGenSettings.OPTION_SHOW_MATURE_ON_LOAD, true)) diff --git a/code/src/java/pcgen/gui2/converter/ConvertPanel.java b/code/src/java/pcgen/gui2/converter/ConvertPanel.java index b14104856c4..2659a206326 100644 --- a/code/src/java/pcgen/gui2/converter/ConvertPanel.java +++ b/code/src/java/pcgen/gui2/converter/ConvertPanel.java @@ -1,16 +1,16 @@ /* * Copyright (c) 2009 Tom Parker - * + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA @@ -44,6 +44,7 @@ import pcgen.gui2.converter.panel.ConvertSubPanel; import pcgen.gui2.tools.CursorControlUtilities; import pcgen.gui2.tools.Utility; +import pcgen.util.GracefulExit; public class ConvertPanel extends JPanel { @@ -114,7 +115,7 @@ public void progressNotAllowed(ProgressEvent pe) finishButton = new JButton("Finish"); finishButton.addActionListener(arg0 -> { PCGenDataConvert.savePrefs(); - System.exit(0); + GracefulExit.exit(0); }); finishButton.setVisible(false); buttonBox.add(finishButton); @@ -174,7 +175,7 @@ public void checkExit() if (response == JOptionPane.OK_OPTION) { PCGenDataConvert.savePrefs(); - System.exit(0); + GracefulExit.exit(0); } } @@ -208,8 +209,8 @@ private void runNextPanel() /** * The Class {@code PreviousButtonListener} ... - * - * + * + * */ public class PreviousButtonListener implements ActionListener { diff --git a/code/src/java/pcgen/gui2/converter/LSTConverter.java b/code/src/java/pcgen/gui2/converter/LSTConverter.java index 01d11fa9a5b..c3acc78295b 100644 --- a/code/src/java/pcgen/gui2/converter/LSTConverter.java +++ b/code/src/java/pcgen/gui2/converter/LSTConverter.java @@ -1,16 +1,16 @@ /* * Copyright (c) 2009 Tom Parker - * + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA @@ -30,6 +30,7 @@ import java.util.HashSet; import java.util.List; import java.util.Observable; +import java.util.Optional; import java.util.Set; import pcgen.base.util.DoubleKeyMapToList; @@ -117,7 +118,7 @@ public int getNumFilesInCampaign(Campaign campaign) } /** - * Initialise the list of campaigns. This will load the ability + * Initialise the list of campaigns. This will load the ability * categories in advance of the conversion. * @param campaigns The campaigns or sources to be converted. */ @@ -182,7 +183,7 @@ private void startItem(final Campaign campaign) continue; } File in = new File(uri); - // Use canonical name to stop reruns for the same file referred to using .. + // Use canonical name to stop reruns for the same file referred to using .. URI canonicalUri; try { @@ -223,19 +224,20 @@ private void startItem(final Campaign campaign) try { changeLogWriter.append("\nProcessing ").append(String.valueOf(in)).append("\n"); - String result = load(uri, loader); - if (result != null) - { - try (Writer out = new BufferedWriter(new OutputStreamWriter( - new FileOutputStream(outFile), - StandardCharsets.UTF_8 - ))) - { - out.write(result); - } - } + load(uri, loader) + .ifPresent((String result) -> { + try (Writer out = new BufferedWriter(new OutputStreamWriter( + new FileOutputStream(outFile), + StandardCharsets.UTF_8))) + { + out.write(result); + } catch (IOException e) + { + Logging.errorPrint(e.getLocalizedMessage(), e); + } + }); } - catch (PersistenceLayerException | IOException | InterruptedException e) + catch (PersistenceLayerException | IOException e) { Logging.errorPrint(e.getLocalizedMessage(), e); } @@ -299,49 +301,57 @@ private File findSubRoot(File root, File in) return findSubRoot(root, parent); } - private String load(URI uri, Loader loader) throws InterruptedException, PersistenceLayerException + private Optional load(URI uri, Loader loader) throws PersistenceLayerException { - String dataBuffer; context.setSourceURI(uri); context.setExtractURI(uri); try { - dataBuffer = LstFileLoader.readFromURI(uri); - } - catch (PersistenceLayerException ple) - { - String message = LanguageBundle.getFormattedString("Errors.LstFileLoader.LoadError", //$NON-NLS-1$ - uri, ple.getMessage()); - Logging.errorPrint(message); - return null; - } + return LstFileLoader.readFromURI(uri) + .map((String dataBuffer) -> { + StringBuilder resultBuffer = new StringBuilder(dataBuffer.length()); - StringBuilder resultBuffer = new StringBuilder(dataBuffer.length()); - final String aString = dataBuffer; - - String[] fileLines = aString.split(LstFileLoader.LINE_SEPARATOR_REGEXP); - for (int line = 0; line < fileLines.length; line++) - { - String lineString = fileLines[line]; - if ((lineString.isEmpty()) || (lineString.charAt(0) == LstFileLoader.LINE_COMMENT_CHAR) - || lineString.startsWith("SOURCE")) - { - resultBuffer.append(lineString); - } - else - { - List newObj = loader.process(resultBuffer, line, lineString, decider); - if (newObj != null) - { - for (CDOMObject cdo : newObj) + String[] fileLines = dataBuffer.split(LstFileLoader.LINE_SEPARATOR_REGEXP); + for (int line = 0; line < fileLines.length; line++) { - injected.addToListFor(loader, uri, cdo); + String lineString = fileLines[line]; + if ((lineString.isEmpty()) || (lineString.charAt(0) == LstFileLoader.LINE_COMMENT_CHAR) + || lineString.startsWith("SOURCE")) + { + resultBuffer.append(lineString); + } + else + { + try + { + List newObj = loader.process(resultBuffer, line, lineString, decider); + if (newObj != null) + { + for (CDOMObject cdo : newObj) + { + injected.addToListFor(loader, uri, cdo); + } + } + } + catch (PersistenceLayerException | InterruptedException e) + { + String message = LanguageBundle.getFormattedString("Errors.LstFileLoader.LoadError", //$NON-NLS-1$ + uri, e.getMessage()); + Logging.errorPrint(message, e); + return null; + } + } + resultBuffer.append("\n"); } - } - } - resultBuffer.append("\n"); + return resultBuffer.toString(); + }); + } + catch (PersistenceLayerException ple) + { + Logging.errorPrint(LanguageBundle.getFormattedString("Errors.LstFileLoader.LoadError", //$NON-NLS-1$ + uri, ple.getMessage())); + return Optional.empty(); } - return resultBuffer.toString(); } public Collection getInjectedLoaders() diff --git a/code/src/java/pcgen/gui2/converter/Loader.java b/code/src/java/pcgen/gui2/converter/Loader.java index 694ecd0a2fe..8608d5685de 100644 --- a/code/src/java/pcgen/gui2/converter/Loader.java +++ b/code/src/java/pcgen/gui2/converter/Loader.java @@ -1,16 +1,16 @@ /* * Copyright (c) 2009 Tom Parker - * + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA @@ -26,9 +26,8 @@ public interface Loader { - public List process(StringBuilder sb, int line, String lineString, ConversionDecider decider) + List process(StringBuilder sb, int line, String lineString, ConversionDecider decider) throws PersistenceLayerException, InterruptedException; - public List getFiles(Campaign campaign); - + List getFiles(Campaign campaign); } diff --git a/code/src/java/pcgen/gui2/converter/ObjectInjector.java b/code/src/java/pcgen/gui2/converter/ObjectInjector.java index c0f19541e91..71b93a9818e 100644 --- a/code/src/java/pcgen/gui2/converter/ObjectInjector.java +++ b/code/src/java/pcgen/gui2/converter/ObjectInjector.java @@ -171,7 +171,7 @@ private boolean processWrite(Campaign campaign, TripleKeyMapToList - * + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA @@ -88,7 +88,8 @@ public boolean performAnalysis(CDOMObject pc) SettingsHandler.setGame(gameMode.getName()); } // Globals.emptyLists(); - Globals.sortPObjectListByName(Globals.getCampaignList()); + // TODO the campaign list is an unmodifiable list, so it is not possible to sort it. + // Globals.sortPObjectListByName(Globals.getCampaignList()); return saveGameMode(pc); } diff --git a/code/src/java/pcgen/gui2/tabs/SkillInfoTab.java b/code/src/java/pcgen/gui2/tabs/SkillInfoTab.java index 562881a19d9..5b6e0ac98a5 100644 --- a/code/src/java/pcgen/gui2/tabs/SkillInfoTab.java +++ b/code/src/java/pcgen/gui2/tabs/SkillInfoTab.java @@ -1,20 +1,20 @@ /* * Copyright 2008 Connor Petty - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * */ package pcgen.gui2.tabs; @@ -79,7 +79,6 @@ import pcgen.gui2.util.event.ListDataAdapter; import pcgen.gui2.util.table.TableCellUtilities; import pcgen.gui2.util.table.TableCellUtilities.SpinnerEditor; -import pcgen.gui2.util.table.TableCellUtilities.SpinnerRenderer; import pcgen.gui3.JFXPanelFromResource; import pcgen.gui3.SimpleHtmlPanelController; import pcgen.system.LanguageBundle; diff --git a/code/src/java/pcgen/gui3/JFXPanelFromResource.java b/code/src/java/pcgen/gui3/JFXPanelFromResource.java index a1e34ae8108..0d471d77b89 100644 --- a/code/src/java/pcgen/gui3/JFXPanelFromResource.java +++ b/code/src/java/pcgen/gui3/JFXPanelFromResource.java @@ -112,5 +112,4 @@ public void showAndBlock(String title) }); lock.join(); } - } diff --git a/code/src/java/pcgen/gui3/PanelFromResource.java b/code/src/java/pcgen/gui3/PanelFromResource.java new file mode 100644 index 00000000000..be8ca562757 --- /dev/null +++ b/code/src/java/pcgen/gui3/PanelFromResource.java @@ -0,0 +1,89 @@ +package pcgen.gui3; + +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.stage.Stage; +import pcgen.system.LanguageBundle; + +import java.text.MessageFormat; +import java.util.logging.Level; +import java.util.logging.Logger; + +import java.io.IOException; +import java.net.URL; +import java.util.Objects; + +/** + * A utility class for loading and displaying JavaFX scenes from FXML resources. + * This class provides functionality to load FXML files, assign their controllers, + * and display the loaded scenes in a new JavaFX stage. + * + * @param The type of the controller associated with the FXML resource. + */ +public class PanelFromResource implements Controllable +{ + private static final Logger LOG = Logger.getLogger(PanelFromResource.class.getName()); + + private final FXMLLoader fxmlLoader = new FXMLLoader(); + + /** + * Constructs a new PanelFromResource instance. + * + * @param klass The class relative to which the FXML resource is located. + * @param resourceName The name of the FXML resource file to load. + * @throws NullPointerException if the resource cannot be found. + */ + public PanelFromResource(Class klass, String resourceName) + { + URL resource = klass.getResource(resourceName); + Objects.requireNonNull(resource, + () -> MessageFormat.format("Resource {0} not found relative to class {1}", resourceName, klass)); + LOG.log(Level.FINE, () -> MessageFormat.format( + "Loading a scene for resource name {0} (a class {1}). The final location is {2}", resourceName, klass, + resource)); + fxmlLoader.setLocation(resource); + fxmlLoader.setResources(LanguageBundle.getBundle()); + } + + /** + * Retrieves the controller associated with the loaded FXML resource. + * + * @return The controller instance. + * @throws IllegalStateException if this method is called outside the JavaFX application thread. + */ + @Override + public T getController() + { + GuiAssertions.assertIsJavaFXThread(); + return fxmlLoader.getController(); + } + + /** + * Displays the loaded FXML resource as a new JavaFX stage. + * + * @param title The title of the stage to be displayed. + * @throws IllegalStateException if this method is called outside the JavaFX application thread. + */ + public void showAsStage(String title) + { + GuiAssertions.assertIsJavaFXThread(); + + try + { + // Load the scene from the FXML resource. + Scene scene = fxmlLoader.load(); + + // Create and configure a new stage. + Stage stage = new Stage(); + stage.setTitle(title); + stage.setScene(scene); + stage.sizeToScene(); + stage.show(); + } catch (IOException e) + { + LOG.log(Level.SEVERE, + MessageFormat.format("Failed to load stream fxml from location {0})", fxmlLoader.getLocation()), + e); + } + } +} diff --git a/code/src/java/pcgen/io/ExportHandler.java b/code/src/java/pcgen/io/ExportHandler.java index 671de1eb7dc..a2f4b83dd5b 100644 --- a/code/src/java/pcgen/io/ExportHandler.java +++ b/code/src/java/pcgen/io/ExportHandler.java @@ -32,7 +32,14 @@ import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.ParseException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import pcgen.cdom.base.CDOMObject; @@ -75,13 +82,13 @@ import pcgen.util.enumeration.View; /** - * This class deals with exporting a PC to various types of output sheets - * including XML, HTML, PDF and Text. - * - * Very basically it takes a PC (or PCs) and replaces tokens in a character - * sheet template with the appropriate values from the PC (PCs). Much of the - * code in here deals with replacing tokens and dealing with the FOR and IIF - * constructs that can be found in the character sheet templates. + * This class deals with exporting a PC to various types of output sheets + * including XML, HTML, PDF, and Text. + *

+ * Very basically, it takes a PC (or PCs) and replaces tokens in a character + * sheet template with the appropriate values from the PC (PCs). Much of the + * code in here deals with replacing tokens and dealing with the FOR and IIF + * constructs that can be found in the character sheet templates. * */ public abstract class ExportHandler @@ -104,9 +111,9 @@ public static ExportHandler createExportHandler(File templateFile) /** A map of output tokens to export */ private static final Map TOKEN_MAP = new HashMap<>(); - /** + /** * A variable to hold the state of whether or not the output token map to - * be exported is populated or not. + * be exported is populated or not. */ private static boolean tokenMapPopulated; @@ -125,7 +132,7 @@ public static ExportHandler createExportHandler(File templateFile) private final File templateFile; /** - * These maps hold the loop variables and parameters of FOR constructs that + * These maps hold the loop variables and parameters of FOR constructs that * will be replaced by their actual values when evaluated. */ protected final Map loopVariables = new HashMap<>(); @@ -138,9 +145,9 @@ public static ExportHandler createExportHandler(File templateFile) private boolean skipMath; /** - * A state variable to indicate whether we should write out what we are currently + * A state variable to indicate whether we should write out what we are currently * processing, would be set to false for example if we were filtering some output - * + * * defaults to true. */ private boolean canWrite = true; @@ -150,7 +157,7 @@ public static ExportHandler createExportHandler(File templateFile) /** - * Constructor. Populates the token map (a list of possible output tokens) and + * Constructor. Populates the token map (a list of possible output tokens) and * sets the character sheet template we are using. * * @param templateFile the template to use while exporting. @@ -163,9 +170,9 @@ protected ExportHandler(File templateFile) /** * Replace the token, but deliberately skip the math - * + * * @param aPC The PC being exported - * @param aString the string which will have its tokens replaced + * @param aString the string which will have its tokens replaced * @param output the object that represents the sheet we are exporting */ public void replaceTokenSkipMath(PlayerCharacter aPC, String aString, BufferedWriter output) @@ -179,7 +186,7 @@ public void replaceTokenSkipMath(PlayerCharacter aPC, String aString, BufferedWr /** * Exports the contents of the given PlayerCharacter to a Writer * according to the handler's template - * + * *
author: Thomas Behr 12-04-02 * * @param aPC the PlayerCharacter to write @@ -191,7 +198,7 @@ public void replaceTokenSkipMath(PlayerCharacter aPC, String aString, BufferedWr /** * Exports a PlayerCharacter-Party to a Writer * according to the handler's template - * + * *
author: Thomas Behr 13-11-02 * * @param PCs the Collection of PlayerCharacter instances which compromises the Party to write @@ -212,10 +219,10 @@ public File getTemplateFile() } /** - * Get variable value from the variable string passed in, this might be - * an old style variable string (COUNT[EQ and STRLEN) or a new style + * Get variable value from the variable string passed in, this might be + * an old style variable string (COUNT[EQ and STRLEN) or a new style * (JEP formula) - * + * * @param varString Variable string that we want to calculate value from * @param aPC The PC that holds the data that we need to get the info from * @return The result @@ -241,7 +248,7 @@ private int getVarValue(String varString, PlayerCharacter aPC) /** * Helper method for getting the variable value out of a variable string - * + * * @param vString The variable String * @param aPC The PC to get the token from * @return the altered variable string @@ -253,7 +260,7 @@ private String processCountEquipmentTokens(String vString, PlayerCharacter aPC) { char chC = vString.charAt(countIndex + 8); - // If the character after COUNT[EQ is . or [1-9] + // If the character after COUNT[EQ is . or [1-9] if ((chC == '.') || ((chC >= '0') && (chC <= '9'))) { final int i = vString.indexOf(']', countIndex + 8); @@ -285,9 +292,9 @@ private String processCountEquipmentTokens(String vString, PlayerCharacter aPC) } /** - * Helper method for getting the variable value out of a variable string, + * Helper method for getting the variable value out of a variable string, * deals with STRLEN tokens - * + * * @param vString The variable string to get the values out of * @param aPC The PC to get the token value out of * @return The altered variable string @@ -327,7 +334,7 @@ private String processStringLengthTokens(String vString, PlayerCharacter aPC) /** * Add to the token map, called mainly by the plugin loader - * + * * @param newToken the token to add */ public static void addToTokenMap(Token newToken) @@ -401,7 +408,7 @@ private static String replaceVariables(String expr, Map variable /** * Helper method to evaluate an expression, used by OIF and IIF tokens - * + * * @param expr Expression to evaluate * @param aPC PC containing values to help evaluate the expression * @return true if the expression was evaluated successfully, else false @@ -426,7 +433,7 @@ private boolean evaluateExpression(final String expr, final PlayerCharacter aPC) return (evaluateExpression(part1, aPC) || evaluateExpression(part2, aPC)); } - /* + /* * Deal with objects held in the loopVariables and loopParameters * sets, e.g. replace the key place holder with the actual value */ @@ -515,16 +522,16 @@ private boolean evaluateExpression(final String expr, final PlayerCharacter aPC) } - // Deal with JEP formula + // Deal with JEP formula final Float res = aPC.getVariableProcessor().getJepOnlyVariableValue(null, expr1, "", 0); if (res != null) { return res.equals(JEP_TRUE); } - /* + /* * Deal with anything else - * + * * Before returning a default false, let's see if this is a valid token, like this: * * |IIF(WEAPON%weap.CATEGORY:Ranged)| @@ -532,10 +539,10 @@ private boolean evaluateExpression(final String expr, final PlayerCharacter aPC) * |ELSE| * something 2 * |ENDIF| - * + * * It can theoretically be used with any valid token, doing an equal compare * (integer or string equalities are valid) - * + * * Can now contain a token on the right side as well, so two tokens can be * compared to each other. Comparison is case-insensitive. */ @@ -617,9 +624,9 @@ else if (tokenCount != 2) /** * Deal with SPELLCASTER. - * + * * Could look like one of the following: - * + * * Arcane * Chaos * Divine @@ -706,14 +713,14 @@ private static boolean processSpellcasterExpression(String expr1, PlayerCharacte /** * Helper method to evaluate a IIF token - * + * * @param node The IIFNode to evaluate * @param output The output to write to (character sheet template) * @param aPC The PC we are outputting */ private void evaluateIIF(final IIFNode node, final BufferedWriter output, final PlayerCharacter aPC) { - // Comma is a delimiter for a higher-level parser, so + // Comma is a delimiter for a higher-level parser, so // we'll use a semicolon and replace it with a comma for // expressions like: // |IIF(VAR.IF(var("COUNT[SKILLTYPE=Strength]")>0;1;0):1)| @@ -732,7 +739,7 @@ private void evaluateIIF(final IIFNode node, final BufferedWriter output, final /** * Helper method to evaluate the results of a IIF child node - * + * * @param children The list of children for the IIF node * @param output The output to write to (filling in the character sheet template) * @param aPC THe PC to output @@ -743,7 +750,7 @@ private void evaluateIIFChildren(final List children, final BufferedWriter ou { if (aChild instanceof final FORNode nextFor) { - // If the child is a FORNode then put it in the loopVariables map as + // If the child is a FORNode then put it in the loopVariables map as // a key with a corresponding value of 0 loopVariables.put(nextFor.var(), 0); existsOnly = nextFor.exists(); @@ -777,7 +784,7 @@ private void evaluateIIFChildren(final List children, final BufferedWriter ou existsOnly = nextFor.exists(); loopVariables.remove(nextFor.var()); } - // If child is an IIFNode, then evaluate that + // If child is an IIFNode, then evaluate that else if (aChild instanceof IIFNode) { evaluateIIF((IIFNode) aChild, output, aPC); @@ -792,7 +799,7 @@ else if (aChild instanceof IIFNode) replaceLine(lineString, output, aPC); // Each time we replace a line that is part of an IIF statement - // we output a newline if we are allowed to write and the + // we output a newline if we are allowed to write and the // whitespace is not controlled by the OS author if (canWrite && !manualWhitespace) { @@ -804,7 +811,7 @@ else if (aChild instanceof IIFNode) /** * Loop through a set of output as required by a FOR loop. - * + * * @param node The node being processed * @param start The starting value of the loop * @param end The ending value of the loop @@ -826,7 +833,7 @@ protected void loopFOR(final FORNode node, final int start, final int end, final /** * Process an iteration of a FOR loop. - * + * * @param node The node being processed * @param output The writer output is to be sent to. * @param aPC The character being processed. @@ -884,7 +891,7 @@ else if (node.children().get(y) instanceof IIFNode) noMoreItems = false; replaceLine(lineString, output, aPC); - // If the output sheet author has no control + // If the output sheet author has no control // over the whitespace then print a newline. if (canWrite && !manualWhitespace) { @@ -1154,7 +1161,7 @@ private String mathMode(String aString, PlayerCharacter aPC) /** * Helper method to process the math for Knowledge (xx) types of tokens - * + * * @param str String to process * @param aPC PC we are exporting * @return Processed string @@ -1192,7 +1199,7 @@ private String processBracketedTokens(String str, PlayerCharacter aPC) /** * Helper class to output normal text - * + * * @param nonToken * @param output */ @@ -1218,7 +1225,7 @@ private void outputNonToken(String nonToken, java.io.Writer output) /** * Helper method to parse |FOR tokens (pre-processing for a template) - * + * * @param forLine * @param tokens * @return A FORNode of the parsed tokens @@ -1277,11 +1284,11 @@ else if (line.startsWith("|ENDFOR|")) } /** - * Retrieve the parameters of a comma separated command such as a - * FOR token. Commas inside brackets are ignored, thus allowing JEP + * Retrieve the parameters of a comma separated command such as a + * FOR token. Commas inside brackets are ignored, thus allowing JEP * functions with multiple parameters to be included in FOR loops. - * - * @param forToken The token to be broken up. + * + * @param forToken The token to be broken up. * @return The token parameters. */ public static List getParameters(String forToken) @@ -1319,9 +1326,9 @@ else if (inFormula) } /** - * Helper method to parse the IIF tokens, includes dealing with a + * Helper method to parse the IIF tokens, includes dealing with a * |FOR child, |IIF child, ELSE, END IF and plain text - * + * * @param expr * @param tokens * @return IIFNode representing the parsed tokens @@ -1330,8 +1337,8 @@ protected IIFNode parseIIFs(String expr, StringTokenizer tokens) { final IIFNode node = new IIFNode(expr); - // Flag to indicate whether we are adding the - // true case (e.g. The IF) or the false case + // Flag to indicate whether we are adding the + // true case (e.g. The IF) or the false case // (e.g. The ELSE) boolean trueCase = true; @@ -1344,7 +1351,7 @@ protected IIFNode parseIIFs(String expr, StringTokenizer tokens) { StringTokenizer newFor = new StringTokenizer(line, ","); newFor.nextToken(); - // It's the first type of |FOR, e.g. With a variable name, + // It's the first type of |FOR, e.g. With a variable name, // see PCGen docs for |FOR token if (newFor.nextToken().startsWith("%")) { @@ -1409,7 +1416,7 @@ else if (line.startsWith("|ENDIF|")) } /** - * Populate the token map (if not already done so), e.g. Add all + * Populate the token map (if not already done so), e.g. Add all * of the types of Output Tokens to the map */ private static void populateTokenMap() @@ -1434,10 +1441,10 @@ private static void populateTokenMap() } /** - * This method performs some work on a given character sheet template line, - * namely replacing tokens, dealing with Malformed lines and simply outputting + * This method performs some work on a given character sheet template line, + * namely replacing tokens, dealing with Malformed lines and simply outputting * plain text. - * + * * @param aLine The line to do the work on * @param output The output buffer that is effectively the character sheet template * @param aPC The PC that we are outputting @@ -1497,7 +1504,7 @@ private void replaceLine(String aLine, BufferedWriter output, PlayerCharacter aP { outputNonToken(tok, output); } - // Reverse the inPipe state, causing the next token to + // Reverse the inPipe state, causing the next token to // take the other decision path if (aTok.hasMoreTokens()) { @@ -1509,7 +1516,7 @@ private void replaceLine(String aLine, BufferedWriter output, PlayerCharacter aP /** * Replace the token with the value it represents - * + * * @param aString The string containing the token to be replaced * @param output The object that will capture the output * @param aPC The PC currently being exported @@ -1569,7 +1576,7 @@ public int replaceToken(String aString, BufferedWriter output, PlayerCharacter a StringTokenizer tok = new StringTokenizer(tokenString, ".,", false); String firstToken = tok.nextToken(); - // Get the remaining token/test string + // Get the remaining token/test string // TODO Understand this String testString = tokenString; if (testString.indexOf(',') > -1) @@ -1652,13 +1659,13 @@ else if (TOKEN_MAP.get(firstToken) != null) /** * Helper method to determine if a line of text needs replacing or not - * + * * @param aString * @return true If it is plain text (e.g. Does not need replacing) */ private boolean isPlainText(String aString) { - // If we 'cannot write' and the string is non-empty, non-filter token then + // If we 'cannot write' and the string is non-empty, non-filter token then // there is nothing to replace so return 0 if (!canWrite && (!aString.isEmpty()) && (aString.charAt(0) != '%')) { @@ -1669,7 +1676,7 @@ private boolean isPlainText(String aString) /** * Helper method to determine if a token is a filter token or not - * + * * @param aString token to evaluate * @return true if it is a filter token */ @@ -1681,7 +1688,7 @@ private boolean isFilterToken(String aString) /** * Helper method, determines if a token is a valid SUB token - * + * * @param tokenString token to evaluate * @return true if it is a valid SUB token */ @@ -1692,9 +1699,9 @@ private boolean isValidSubToken(String tokenString) /** * Helper method to detect if a token is a DFOR or FOR token - * + * * @param tokenString token to check - * @return true if it is a DFOR or FOR token + * @return true if it is a DFOR or FOR token */ private boolean isForOrDForToken(String tokenString) { @@ -1703,9 +1710,9 @@ private boolean isForOrDForToken(String tokenString) /** * Helper method to determine if a string contains a mathematical token - * + * * @param testString String to test - * @return true if it + * @return true if it */ private boolean containsMathematicalToken(String testString) { @@ -1716,7 +1723,7 @@ private boolean containsMathematicalToken(String testString) /** * Helper method, deals with replacing the SUB token - * + * * @param tokenString the SUB token * @return The altered SUB token */ @@ -1746,9 +1753,9 @@ private String replaceSubToken(String tokenString) } /** - * Helper method that deals with Processing the FOR./DFOR. tokens as a + * Helper method that deals with Processing the FOR./DFOR. tokens as a * DFOR loop - * + * * @param tokenString the token to loop over * @param output The writer we write to * @param aPC The PC we are exporting @@ -1768,12 +1775,12 @@ private void processLoopToken(String tokenString, BufferedWriter output, PlayerC } /** - * Helper method for replaceToken, deals with the filter tokens e.g. %DOMAIN, basically + * Helper method for replaceToken, deals with the filter tokens e.g. %DOMAIN, basically * returns 0 if we should not be writing something out, e.g. It's filtered out - * + * * @param aString * @param aPC - * @return 0 If we should not be writing something out + * @return 0 If we should not be writing something out */ private int dealWithFilteredTokens(String aString, PlayerCharacter aPC) { @@ -2418,7 +2425,7 @@ else if (bio.trim().isEmpty()) canWrite = (aPC.getLevel(aClass) >= i); } - // Filter out SPELLLISTCLASS + // Filter out SPELLLISTCLASS if (bString.startsWith("SPELLLISTCLASS")) { // New token syntax |%SPELLLISTCLASS.x| instead of |%SPELLLISTCLASSx| @@ -2451,7 +2458,7 @@ else if (bio.trim().isEmpty()) /** * Helper method to get the equipment merging strategy - * + * * @param aString * @return merging strategy constant */ @@ -2473,13 +2480,13 @@ private int getEquipmentMergingStrategy(String aString) /** * Helper method to deal with DFOR token, e.g. - * + * * DFOR.0,(COUNT[SKILLS]+1)/2,1,COUNT[SKILLS],(COUNT[SKILLS]+1)/2,\SKILL%\ * \SKILL%.TOTAL\\SKILL%.RANK\ * \SKILL%.ABILITY\\SKILL%.MOD\,,,0 - * + * * Produces a 2 column row table of all skills. - * + * * @param aString String to process * @param output Output we are writing to * @param aPC PC we are exporting @@ -2705,13 +2712,13 @@ private void replaceTokenForDfor(String aString, BufferedWriter output, PlayerCh /** * Helper method to parse OIF token, e.g. - * + * * OIF(expr,truepart,falsepart) * OIF(HASFEAT:Armor Prof (Light), Yes, No) - * - * If the character has the Light Armor proficiency, then returns "Yes". + * + * If the character has the Light Armor proficiency, then returns "Yes". * Otherwise it returns "No". - * + * * @param aString String to parse * @param output output to write to * @param aPC PC we are exporting @@ -2813,7 +2820,7 @@ private void replaceTokenOIF(String aString, java.io.Writer output, PlayerCharac /** * Helper method to deal with the SpellListBook token - * + * * @param aString * @param aPC * @return 0 @@ -2853,7 +2860,7 @@ private int replaceTokenSpellListBook(String aString, PlayerCharacter aPC) /** * Helper method for replacing token variables - * + * * @param aString String to process * @param aPC PC we are exporting */ @@ -2934,7 +2941,7 @@ else if ("NEQ".equals(bString)) /** * Exports a PlayerCharacter-Party to a Writer * according to the handler's template - * + * *
author: Thomas Behr 13-11-02 * * @param PCs the PlayerCharacter[] which compromises the Party to write @@ -2968,7 +2975,7 @@ private void write(PlayerCharacter[] PCs, BufferedWriter out) // If not inside a TAG and there is no | character on this line if (!betweenPipes && lastPipeIndex == -1) { - // If output sheet author controls new lines + // If output sheet author controls new lines // then replace tabs with empty space. if (manualWhitespace) { @@ -2982,17 +2989,17 @@ private void write(PlayerCharacter[] PCs, BufferedWriter out) } } - // Else if we are Inside a tag but we are not at the finish of - // the tag e.g. + // Else if we are Inside a tag but we are not at the finish of + // the tag e.g. // // | // x // | - // - // Or we are at the start of a tag that wraps onto the next line e.g. - // + // + // Or we are at the start of a tag that wraps onto the next line e.g. + // // |x - // + // // Collect this text (without the pipe) // to be passed for replacement later. else if (lastPipeIndex == (betweenPipes ? -1 : 0)) @@ -3033,10 +3040,10 @@ else if (lastPipeIndex == (betweenPipes ? -1 : 0)) /** * Helper method to process a line that begins with a | (and may end with a |) - * + * * @param PCs List of PCs to output * @param aLine Line to parse - * @param buf + * @param buf * @param out character sheet we are building up * @param between Whether we are processing a line between pipes * @return true if we processed successfully @@ -3058,7 +3065,7 @@ private boolean processPipedLine(PlayerCharacter[] PCs, String aLine, StringBuil { String tok = aTok.nextToken(); - // If we're not between pipes then just write to the output + // If we're not between pipes then just write to the output // removing tab characters if asked to do so if (!betweenPipes) { @@ -3096,7 +3103,7 @@ else if (!noPipes && !aTok.hasMoreTokens()) Matcher mat = Pattern.compile("^(\\d+)").matcher(aString); int charNum = mat.matches() ? Integer.parseInt(mat.group()) : -1; - // This seems bizarre since we haven't stripped the + // This seems bizarre since we haven't stripped the // integer from the front of this string which means // that it will not be recognised as a token and will // just be written to the output verbatim @@ -3123,7 +3130,7 @@ else if (aString.startsWith("EXPORT")) /** * Deal with the FOR. token, but at a party level - * + * * @param PCs The PCs to export * @param out The Output we are writing to * @param tokenString The token string to process @@ -3162,7 +3169,7 @@ private void doPartyForToken(PlayerCharacter[] PCs, BufferedWriter out, String t // Note: This was changed from == to && since I can't see how // == could possibly be correct behaviour. If we were not // just printing characters that exist the loop would - // terminate after printing one character. + // terminate after printing one character. boolean breakloop = (forParser.existsOnly() && (currPC == null)); ++x; @@ -3227,7 +3234,7 @@ public void setManualWhitespace(boolean manualWhitespace) /** * Get the token string - * + * * @param aPC the PC being exported * @param aString The token string to convert * @return token string diff --git a/code/src/java/pcgen/io/ExportUtilities.java b/code/src/java/pcgen/io/ExportUtilities.java index bcfc7c344e2..987c3296b53 100644 --- a/code/src/java/pcgen/io/ExportUtilities.java +++ b/code/src/java/pcgen/io/ExportUtilities.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.stream.Collectors; +import freemarker.template.Configuration; import pcgen.cdom.base.Constants; import pcgen.core.SettingsHandler; import pcgen.facade.core.CharacterFacade; @@ -49,7 +50,7 @@ /** * ExportUtilities is a collection of useful tools for exporting characters. - * + * */ public final class ExportUtilities { @@ -57,14 +58,14 @@ public final class ExportUtilities public static final String HTML_EXPORT_DIR_PROP = "htmlExportDir"; /** - * This class should not be constructed. + * This class should not be constructed. */ private ExportUtilities() { } /** - * Retrieve the extension that should be used for the output file. This is base don the template name. + * Retrieve the extension that should be used for the output file. This is base don the template name. * @param templateFilename The filename of the export template. * @param isPdf Is this an export to a PDF file? * @return The output filename extension. @@ -112,12 +113,12 @@ public static boolean isPdfTemplate(String templateFilename) } /** - * Returns an ObjectWrapper of sufficiently high version for pcgen + * Returns an ObjectWrapper out of the latest version of Freemarker for pcgen */ public static ObjectWrapper getObjectWrapper() { DefaultObjectWrapperBuilder defaultObjectWrapperBuilder = new DefaultObjectWrapperBuilder( - new Version("2.3.28")); + new Version(Configuration.getVersion().toString())); return defaultObjectWrapperBuilder.build(); } diff --git a/code/src/java/pcgen/io/exporttoken/AbilityToken.java b/code/src/java/pcgen/io/exporttoken/AbilityToken.java index 3ab47534f0a..60bb4110b9c 100644 --- a/code/src/java/pcgen/io/exporttoken/AbilityToken.java +++ b/code/src/java/pcgen/io/exporttoken/AbilityToken.java @@ -56,7 +56,7 @@ /** * {@code AbilityToken} handles the output of ability information. - * + * * The format is ABILITY.u.v.w.x.y.z where: *

    *
  • u is the AbilityCategory (FEAT, FIGHTER etc, or ALL) - Mandatory
  • @@ -68,7 +68,7 @@ *
  • z is what is to be output DESC, TYPE, SOURCE, default is name, or * TYPE=<type> - type filter
  • *
- * + * */ public class AbilityToken extends Token { @@ -96,7 +96,7 @@ public class AbilityToken extends Token /** * Get the TOKENNAME - * + * * @return TOKENNAME */ @Override @@ -123,7 +123,7 @@ public String getToken(String tokenSource, PlayerCharacter pc, ExportHandler eh) /** * Produce the ABILITY token output for a specific ability category. - * + * * @param tokenSource * The token being processed. * @param pc @@ -144,8 +144,8 @@ protected String getTokenForCategory(String tokenSource, PlayerCharacter pc, Exp boolean cacheAbilityProcessingData = (cachedPC != pc || !aCategory.equals(lastCategory) || cachedPcSerial != pc.getSerial() || !tokenString.equals(lastToken)); - // As this method can effectively be called by an OS FOR token, there - // is a performance saving in caching some of the one-off processing data + // As this method can effectively be called by an OS FOR token, there + // is a performance saving in caching some of the one-off processing data if (cacheAbilityProcessingData) { // Overridden by subclasses to return the right list. @@ -174,10 +174,10 @@ protected String getTokenForCategory(String tokenSource, PlayerCharacter pc, Exp */ int abilityIndex = -1; - /* + /* * Grab the next token which will either be be: - * visibility (v), type (w) or index (x), stop processing - * once you hit the index token + * visibility (v), type (w) or index (x), stop processing + * once you hit the index token */ while (aTok.hasMoreTokens()) { @@ -188,7 +188,7 @@ protected String getTokenForCategory(String tokenSource, PlayerCharacter pc, Exp abilityIndex = Integer.parseInt(bString); break; } - // The optional visibility (v) or type (w) has been provided, so deal with those + // The optional visibility (v) or type (w) has been provided, so deal with those catch (NumberFormatException exc) { switch (bString) @@ -210,10 +210,10 @@ protected String getTokenForCategory(String tokenSource, PlayerCharacter pc, Exp } } - /* + /* * Grab the next token which will either be be: - * TYPE (y) or property (z), stop processing - * once you hit the last token + * TYPE (y) or property (z), stop processing + * once you hit the last token */ while (aTok.hasMoreTokens()) { @@ -221,10 +221,10 @@ protected String getTokenForCategory(String tokenSource, PlayerCharacter pc, Exp int typeInd = typeStr.indexOf("TYPE="); int extypeInd = typeStr.indexOf("EXCLUDETYPE="); - // If it's TYPE and it actually has a value attached then process it + // If it's TYPE and it actually has a value attached then process it if (typeInd != -1 && extypeInd == -1 && typeStr.length() > 5) { - // It's a type to be excluded from the filter list + // It's a type to be excluded from the filter list if (typeStr.startsWith("!")) { Logging.deprecationPrint( @@ -241,7 +241,7 @@ protected String getTokenForCategory(String tokenSource, PlayerCharacter pc, Exp } } - // If it's EXCLUDETYPE and it actually has a value attached then process it + // If it's EXCLUDETYPE and it actually has a value attached then process it if (extypeInd != -1 && typeStr.length() > 12) { // exclude TYPEs from comma-separated list @@ -253,7 +253,7 @@ protected String getTokenForCategory(String tokenSource, PlayerCharacter pc, Exp } int keyInd = typeStr.indexOf("KEY="); - // If it's KEY and it actually has a value attached then process it + // If it's KEY and it actually has a value attached then process it if (keyInd != -1 && typeStr.length() > 4) { key = typeStr.substring(keyInd + 4); @@ -287,7 +287,7 @@ protected String getTokenForCategory(String tokenSource, PlayerCharacter pc, Exp /** * Build up the list of abilities of interest based on the type and visibility selection. - * + * * @param types * The list of types which it must match at least one of. * @param negate @@ -314,7 +314,7 @@ static MapToList buildAbilityList(List types, List bList = new ArrayList<>(); // For each ability figure out whether it should be displayed depending - // on its visibility filtering and its ability type filtering + // on its visibility filtering and its ability type filtering for (Ability aAbility : aList) { matchTypeDef = abilityMatchesType(abilityType, aAbility, types, negate); @@ -342,7 +342,7 @@ static MapToList buildAbilityList(List types, List buildAbilityList(String key, View view, List bList = new ArrayList<>(); // For each ability figure out whether it should be displayed depending - // on its visibility filtering and its ability type filtering + // on its visibility filtering and its ability type filtering for (Ability aAbility : aList) { matchKeyDef = aAbility.getKeyName().equalsIgnoreCase(key); @@ -388,9 +388,9 @@ static MapToList buildAbilityList(String key, View view, } /** - * Helper method, returns true if the ability has one of the ability types that + * Helper method, returns true if the ability has one of the ability types that * we are matching on. - * + * * @param abilityType The ability Type to test * @param aAbility The ability * @param types The list of types we're trying to match on @@ -443,7 +443,7 @@ static boolean abilityMatchesType(String abilityType, Ability aAbility, List abilitie /** * Gets the aspect string. - * + * * @param pc * The character being exported. * @param abilities * The ability - * + * * @return the aspect string */ private String getAspectString(PlayerCharacter pc, List abilities) @@ -652,14 +652,14 @@ private String getAspectString(PlayerCharacter pc, List abilities) /** * Gets the aspect string for an aspect identified by position or name. - * + * * @param pc * The character being exported. * @param abilities * The ability being queried. * @param key * The key (number or name) of the aspect to retrieve - * + * * @return the aspect string */ private String getAspectString(PlayerCharacter pc, List abilities, String key) @@ -700,12 +700,12 @@ private String getAspectString(PlayerCharacter pc, List abilities, St /** * Gets the boolean (Y/N) string for the presence of the named aspect. - * + * * @param ability * The ability being queried. * @param key * The key (name only) of the aspect to check - * + * * @return Y if the aspect is present, N if not. */ private String getHasAspectString(PlayerCharacter pc, Ability ability, AspectName key) @@ -735,7 +735,9 @@ private String getHasInfoString(PlayerCharacter pc, Ability ability, String key) String v = pc.getInfoToken(tokenString, ability); if (!v.equals(tokenString)) + { return "Y"; + } return "N"; } @@ -755,7 +757,9 @@ private String getInfoString(PlayerCharacter pc, Ability ability, String key) String v = pc.getInfoToken(tokenString, ability); if (!v.equals(tokenString)) + { return v; + } return ""; } @@ -763,7 +767,7 @@ private String getInfoString(PlayerCharacter pc, Ability ability, String key) * Returns the correct list of abilities for the character. This method is * overridden in subclasses if they need to change the list of abilities * looked at. - * + * * @param pc * The character who's abilities we are retrieving. * @param aCategory diff --git a/code/src/java/pcgen/io/exporttoken/Token.java b/code/src/java/pcgen/io/exporttoken/Token.java index 1e01dd4bff2..7d4edb5a4d6 100644 --- a/code/src/java/pcgen/io/exporttoken/Token.java +++ b/code/src/java/pcgen/io/exporttoken/Token.java @@ -20,11 +20,8 @@ */ package pcgen.io.exporttoken; -import java.text.MessageFormat; -import java.util.*; +import java.util.StringTokenizer; -import pcgen.cdom.enumeration.MapKey; -import pcgen.core.PObject; import pcgen.core.PlayerCharacter; import pcgen.io.ExportHandler; diff --git a/code/src/java/pcgen/persistence/GameModeFileLoader.java b/code/src/java/pcgen/persistence/GameModeFileLoader.java index 9bd9cb3c14d..fc9ef48ee97 100644 --- a/code/src/java/pcgen/persistence/GameModeFileLoader.java +++ b/code/src/java/pcgen/persistence/GameModeFileLoader.java @@ -20,6 +20,8 @@ import java.io.File; import java.io.FilenameFilter; import java.net.URI; +import java.util.Optional; +import java.util.logging.Level; import pcgen.cdom.base.Constants; import pcgen.cdom.content.TabInfo; @@ -57,7 +59,6 @@ public class GameModeFileLoader extends PCGenTask { - private static final FilenameFilter GAME_MODE_FILE_FILTER = (aFile, aString) -> { try { @@ -132,57 +133,58 @@ private void loadGameModes(String[] gameFiles) { File specGameModeDir = new File(gameModeDir, gameFile); File miscInfoFile = new File(specGameModeDir, "miscinfo.lst"); - final GameMode gm = GameModeFileLoader.loadGameModeMiscInfo(gameFile, miscInfoFile.toURI()); - if (gm != null) - { - String gmName = gm.getName(); - //SettingsHandler.setGame(gmName); - LoadContext context = gm.getModeContext(); - loadGameModeInfoFile(gm, new File(specGameModeDir, "level.lst").toURI(), "level"); - loadGameModeInfoFile(gm, new File(specGameModeDir, "rules.lst").toURI(), "rules"); - // Load equipmentslot.lst - GameModeFileLoader.loadGameModeLstFile(context, eqSlotLoader, gmName, gameFile, "equipmentslots.lst"); + GameModeFileLoader.loadGameModeMiscInfo(gameFile, miscInfoFile.toURI()) + .ifPresentOrElse((GameMode gameMode) -> { + String gmName = gameMode.getName(); + //SettingsHandler.setGame(gmName); + LoadContext context = gameMode.getModeContext(); + loadGameModeInfoFile(gameMode, new File(specGameModeDir, "level.lst").toURI(), "level"); + loadGameModeInfoFile(gameMode, new File(specGameModeDir, "rules.lst").toURI(), "rules"); - // Load paperInfo.lst - GameModeFileLoader.loadGameModeLstFile(context, paperLoader, gmName, gameFile, "paperInfo.lst"); + // Load equipmentslot.lst + GameModeFileLoader.loadGameModeLstFile(context, eqSlotLoader, gmName, gameFile, "equipmentslots.lst"); - // Load bio files - GameModeFileLoader.loadGameModeLstFile(context, traitLoader, gmName, gameFile, - "bio" + File.separator + "traits.lst"); - GameModeFileLoader.loadGameModeLstFile(context, locationLoader, gmName, gameFile, - "bio" + File.separator + "locations.lst"); + // Load paperInfo.lst + GameModeFileLoader.loadGameModeLstFile(context, paperLoader, gmName, gameFile, "paperInfo.lst"); - // Load load.lst and check for completeness - GameModeFileLoader.loadGameModeLstFile(context, loadInfoLoader, gmName, gameFile, "load.lst"); + // Load bio files + GameModeFileLoader.loadGameModeLstFile(context, traitLoader, gmName, gameFile, + "bio" + File.separator + "traits.lst"); + GameModeFileLoader.loadGameModeLstFile(context, locationLoader, gmName, gameFile, + "bio" + File.separator + "locations.lst"); - // Load sizeAdjustment.lst - GameModeFileLoader.loadGameModeLstFile(context, sizeLoader, gmName, gameFile, "sizeAdjustment.lst", - false); + // Load load.lst and check for completeness + GameModeFileLoader.loadGameModeLstFile(context, loadInfoLoader, gmName, gameFile, "load.lst"); - // Load statsandchecks.lst - GameModeFileLoader.loadGameModeLstFile(context, statCheckLoader, gmName, gameFile, "statsandchecks.lst", - false); + // Load sizeAdjustment.lst + GameModeFileLoader.loadGameModeLstFile(context, sizeLoader, gmName, gameFile, "sizeAdjustment.lst", + false); - // Load equipIcons.lst - GameModeFileLoader.loadGameModeLstFile(context, equipIconLoader, gmName, gameFile, "equipIcons.lst"); + // Load statsandchecks.lst + GameModeFileLoader.loadGameModeLstFile(context, statCheckLoader, gmName, gameFile, "statsandchecks.lst", + false); - GameModeFileLoader.loadGameModeLstFile(context, codeControlLoader, gmName, gameFile, "codeControl.lst"); + // Load equipIcons.lst + GameModeFileLoader.loadGameModeLstFile(context, equipIconLoader, gmName, gameFile, "equipIcons.lst"); - // Load pointbuymethods.lst - loadPointBuyFile(context, gameFile, gmName); - for (final PointBuyCost pbc : context.getReferenceContext() - .getConstructedCDOMObjects(PointBuyCost.class)) - { - gm.addPointBuyStatCost(pbc); - } + GameModeFileLoader.loadGameModeLstFile(context, codeControlLoader, gmName, gameFile, "codeControl.lst"); - // Load migration.lst - GameModeFileLoader.loadGameModeLstFile(context, migrationLoader, gmName, gameFile, "migration.lst"); + // Load pointbuymethods.lst + loadPointBuyFile(context, gameFile, gmName); + for (final PointBuyCost pbc : context.getReferenceContext() + .getConstructedCDOMObjects(PointBuyCost.class)) + { + gameMode.addPointBuyStatCost(pbc); + } - GameModeFileLoader.loadGameModeLstFile(context, bioLoader, gmName, gameFile, - "bio" + File.separator + "biosettings.lst"); - } + // Load migration.lst + GameModeFileLoader.loadGameModeLstFile(context, migrationLoader, gmName, gameFile, "migration.lst"); + + GameModeFileLoader.loadGameModeLstFile(context, bioLoader, gmName, gameFile, + "bio" + File.separator + "biosettings.lst"); + }, () -> Logging.log(Level.WARNING, "There is no data for game mode: " + gameFile + + ", and the URI: " + miscInfoFile.toURI())); progress++; setProgress(progress); @@ -259,10 +261,40 @@ private static boolean loadGameModeLstFile(LoadContext context, LstLineFileLoade private void loadGameModeInfoFile(GameMode gameMode, URI uri, String aType) { - String data; try { - data = LstFileLoader.readFromURI(uri); + LstFileLoader.readFromURI(uri) + .ifPresentOrElse((String data) -> { + String[] fileLines = data.split(LstFileLoader.LINE_SEPARATOR_REGEXP); + String xpTable = ""; + for (int i = 0; i < fileLines.length; i++) + { + String aLine = fileLines[i]; + + // Ignore commented-out and empty lines + if (aLine.isEmpty() || (aLine.charAt(0) == '#')) + { + continue; + } + + if (aType.equals("level")) + { + xpTable = LevelLoader.parseLine(gameMode, aLine, i + 1, uri, xpTable); + } else if (aType.equals("rules")) + { + try + { + ruleCheckLoader.parseLine(gameMode.getModeContext(), aLine, uri); + } catch (final PersistenceLayerException e) + { + Logging.errorPrint( + LanguageBundle.getFormattedString( + "Errors.LstSystemLoader.loadGameModeInfoFile", //$NON-NLS-1$ + uri, e.getMessage())); + } + } + } + }, () -> Logging.log(Level.WARNING, "There was no data in the URI: " + uri)); } catch (final PersistenceLayerException ple) { @@ -270,48 +302,52 @@ private void loadGameModeInfoFile(GameMode gameMode, URI uri, String aType) LanguageBundle.getFormattedString( "Errors.LstSystemLoader.loadGameModeInfoFile", //$NON-NLS-1$ uri, ple.getMessage())); - return; - } - - String[] fileLines = data.split(LstFileLoader.LINE_SEPARATOR_REGEXP); - String xpTable = ""; - for (int i = 0; i < fileLines.length; i++) - { - String aLine = fileLines[i]; - - // Ignore commented-out and empty lines - if (aLine.isEmpty() || (aLine.charAt(0) == '#')) - { - continue; - } - - if (aType.equals("level")) - { - xpTable = LevelLoader.parseLine(gameMode, aLine, i + 1, uri, xpTable); - } - else if (aType.equals("rules")) - { - try - { - ruleCheckLoader.parseLine(gameMode.getModeContext(), aLine, uri); - } - catch (final PersistenceLayerException e) - { - Logging.errorPrint( - LanguageBundle.getFormattedString( - "Errors.LstSystemLoader.loadGameModeInfoFile", //$NON-NLS-1$ - uri, e.getMessage())); - } - } } } - private static GameMode loadGameModeMiscInfo(String aName, URI uri) + private static Optional loadGameModeMiscInfo(String aName, URI uri) { - String data; try { - data = LstFileLoader.readFromURI(uri); + return LstFileLoader.readFromURI(uri) + .map((String data) -> { + String[] fileLines = data.split(LstFileLoader.LINE_SEPARATOR_REGEXP); + + GameMode gameMode = new GameMode(aName); + SystemCollections.addToGameModeList(gameMode); + gameMode.getModeContext().getReferenceContext().importObject(AbilityCategory.FEAT); + + for (int i = 0; i < fileLines.length; i++) + { + String aLine = fileLines[i]; + + // Ignore commented-out and empty lines + if (aLine.isEmpty() || (aLine.charAt(0) == '#')) + { + continue; + } + + GameModeLoader.parseMiscGameInfoLine(gameMode, aLine, uri, i + 1); + } + + // Record how the FEAT category was configured + AbilityCategory feat = new AbilityCategory(); + feat.copyFields(AbilityCategory.FEAT); + gameMode.setFeatTemplate(feat); + + int[] dieSizes = gameMode.getDieSizes(); + if (dieSizes == null || dieSizes.length == 0) + { + final int[] defaultDieSizes = {1, 2, 3, 4, 6, 8, 10, 12, 20, 100, 1000}; + gameMode.setDieSizes(defaultDieSizes); + Logging.log(Logging.LST_ERROR, "GameMode (" + gameMode.getName() + + ") : MiscInfo.lst did not contain any valid DIESIZES. " + "Using the system default DIESIZES."); + } + addDefaultUnitSet(gameMode); + addDefaultTabInfo(gameMode); + gameMode.applyPreferences(); + return gameMode; + }); } catch (final PersistenceLayerException ple) { @@ -319,45 +355,8 @@ private static GameMode loadGameModeMiscInfo(String aName, URI uri) LanguageBundle.getFormattedString( "Errors.LstSystemLoader.loadGameModeInfoFile", //$NON-NLS-1$ uri, ple.getMessage())); - return null; - } - - String[] fileLines = data.split(LstFileLoader.LINE_SEPARATOR_REGEXP); - - GameMode gameMode = new GameMode(aName); - SystemCollections.addToGameModeList(gameMode); - gameMode.getModeContext().getReferenceContext().importObject(AbilityCategory.FEAT); - - for (int i = 0; i < fileLines.length; i++) - { - String aLine = fileLines[i]; - - // Ignore commented-out and empty lines - if (aLine.isEmpty() || (aLine.charAt(0) == '#')) - { - continue; - } - - GameModeLoader.parseMiscGameInfoLine(gameMode, aLine, uri, i + 1); - } - - // Record how the FEAT category was configured - AbilityCategory feat = new AbilityCategory(); - feat.copyFields(AbilityCategory.FEAT); - gameMode.setFeatTemplate(feat); - - int[] dieSizes = gameMode.getDieSizes(); - if (dieSizes == null || dieSizes.length == 0) - { - final int[] defaultDieSizes = {1, 2, 3, 4, 6, 8, 10, 12, 20, 100, 1000}; - gameMode.setDieSizes(defaultDieSizes); - Logging.log(Logging.LST_ERROR, "GameMode (" + gameMode.getName() - + ") : MiscInfo.lst did not contain any valid DIESIZES. " + "Using the system default DIESIZES."); + return Optional.empty(); } - addDefaultUnitSet(gameMode); - addDefaultTabInfo(gameMode); - gameMode.applyPreferences(); - return gameMode; } /** diff --git a/code/src/java/pcgen/persistence/SourceFileLoader.java b/code/src/java/pcgen/persistence/SourceFileLoader.java index 5e9ac82e483..5068a3f7428 100644 --- a/code/src/java/pcgen/persistence/SourceFileLoader.java +++ b/code/src/java/pcgen/persistence/SourceFileLoader.java @@ -28,9 +28,11 @@ import java.util.List; import java.util.Observable; import java.util.Observer; +import java.util.Optional; import java.util.Set; import java.util.logging.Handler; import java.util.logging.LogRecord; +import java.util.stream.Stream; import pcgen.base.formatmanager.FormatUtilities; import pcgen.base.formula.base.LegalScope; @@ -250,21 +252,22 @@ public String getLicenses() /** * @return a list of licenses read from the campaign license files */ - public Iterable getOtherLicenses() + public Stream getOtherLicenses() { - Collection licenses = new ArrayList<>(); - for (CampaignSourceEntry licenseFile : licenseFiles) - { - try - { - String dataBuffer = LstFileLoader.readFromURI(licenseFile.getURI()); - licenses.add(dataBuffer); - } catch (PersistenceLayerException e) - { - Logging.errorPrint("Could not read license at " + licenseFile, e); - } - } - return licenses; + return licenseFiles.stream() + .map(CampaignSourceEntry::getURI) + .map((URI uri) -> { + try + { + return LstFileLoader.readFromURI(uri); + } catch (PersistenceLayerException e) + { + Logging.errorPrint("Could not read license at " + uri, e); + return Optional.empty(); + } + }) + .filter(Optional::isPresent) + .map(Optional::get); } public String getMatureInfo() diff --git a/code/src/java/pcgen/persistence/lst/LstFileLoader.java b/code/src/java/pcgen/persistence/lst/LstFileLoader.java index 0396d470f96..72ce81158cf 100644 --- a/code/src/java/pcgen/persistence/lst/LstFileLoader.java +++ b/code/src/java/pcgen/persistence/lst/LstFileLoader.java @@ -19,6 +19,15 @@ */ package pcgen.persistence.lst; +import pcgen.cdom.base.Constants; +import pcgen.core.SettingsHandler; +import pcgen.core.utils.CoreUtility; +import pcgen.core.utils.MessageType; +import pcgen.core.utils.ShowMessageDelegate; +import pcgen.persistence.PersistenceLayerException; +import pcgen.system.LanguageBundle; +import pcgen.util.Logging; + import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; @@ -27,14 +36,8 @@ import java.nio.charset.MalformedInputException; import java.nio.file.Files; import java.nio.file.Path; -import org.jetbrains.annotations.Nullable; -import pcgen.cdom.base.Constants; -import pcgen.core.SettingsHandler; -import pcgen.core.utils.CoreUtility; -import pcgen.core.utils.MessageType; -import pcgen.core.utils.ShowMessageDelegate; -import pcgen.persistence.PersistenceLayerException; -import pcgen.util.Logging; +import java.text.MessageFormat; +import java.util.Optional; /** * This class is a base class for LST file loaders. @@ -49,7 +52,7 @@ * *

* Instances of LstFileLoader or its subclasses are not thread-safe, - * so any thread should only acccess a single loader (or group of loaders) + * so any thread should only access a single loader (or group of loaders) * at a time. */ public final class LstFileLoader @@ -59,32 +62,35 @@ private LstFileLoader() //Utility class } - /** The String that represents the start of a line comment. */ + /** + * The String that represents the start of a line comment. + */ public static final char LINE_COMMENT_CHAR = '#'; - /** The String that separates individual objects */ + /** + * The String that separates individual objects + */ public static final String LINE_SEPARATOR_REGEXP = "(\r\n?|\n)"; //$NON-NLS-1$ - /** BOM prefix, used to warn the user that BOM-strings are not supported */ + /** + * BOM prefix, used to warn the user that BOM-strings are not supported + */ private static final String BOM = "\uFEFF"; /** * This method reads the given URI and returns its content as a string. If an error occurs, we don't throw an - * exception, but log the error in the logger. It is possible to read file content from the remote link, but + * exception but log the error in the logger. It is possible to read file content from the remote link, but * a corresponding option must be enabled in settings. * - * @param uri URI of the remote content - * @return String file content - * @throws PersistenceLayerException is thrown when a null URI is provided + * @param uri URI of the remote content + * @return String file content + * @throws PersistenceLayerException is thrown when a null URI is provided */ - @Nullable - public static String readFromURI(URI uri) throws PersistenceLayerException + public static Optional readFromURI(URI uri) throws PersistenceLayerException { - if (uri == null) - { - // We have a problem! - throw new PersistenceLayerException("LstFileLoader.readFromURI() received a null URI parameter!"); - } + uri = Optional.ofNullable(uri) + .orElseThrow(() -> new PersistenceLayerException( + "LstFileLoader.readFromURI() received a null URI parameter!")); try { @@ -94,13 +100,12 @@ public static String readFromURI(URI uri) throws PersistenceLayerException String result = Files.readString(path); if (result.startsWith(BOM)) { - Logging.log(Logging.WARNING, - "The file %s uses UTF-8-BOM encoding. LST files must be UTF-8".formatted(uri)); + Logging.log(Logging.WARNING, MessageFormat.format( + "The file {0} uses UTF-8-BOM encoding. LST files must be UTF-8", uri)); result = result.substring(1); } - return result; - } - else if (SettingsHandler.isLoadURLs()) // load from remote URIs + return Optional.of(result); + } else if (SettingsHandler.isLoadURLs()) // load from remote URIs { try (HttpClient client = HttpClient.newHttpClient()) { @@ -108,30 +113,27 @@ else if (SettingsHandler.isLoadURLs()) // load from remote URIs .uri(uri) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); - return response.body(); + return Optional.of(response.body()); } - } - else + } else { - // Just to protect people from using web - // sources without their knowledge, - // we added a preference. - ShowMessageDelegate.showMessageDialog("Preferences are currently set to NOT allow\nloading of " - + "sources from web links.\n" + uri + " is a web link", Constants.APPLICATION_NAME, - MessageType.ERROR); + // Just to protect people from using web sources without their knowledge, we added a preference. + ShowMessageDelegate.showMessageDialog(LanguageBundle.getFormattedString("in_err_remote_lst_warn", uri), + Constants.APPLICATION_NAME, MessageType.ERROR); } } catch (MalformedInputException ie) { - Logging.errorPrint("ERROR: " + uri + "\nThe file doesn't use UTF-8 encoding. LST files must be UTF-8", ie); - } - catch (IOException | InterruptedException e) + Logging.errorPrint(MessageFormat.format( + "ERROR: file {0}\nThe file does not use UTF-8 encoding. LST files must be UTF-8", + uri), ie); + } catch (IOException | InterruptedException e) { // Don't throw an exception here because a simple // file not found will prevent ANY other files from // being loaded/processed -- NOT what we want - Logging.errorPrint("ERROR: " + uri + '\n' + "Exception type: " + e.getClass().getName() + "\n" + "Message: " - + e.getMessage(), e); + Logging.errorPrint(MessageFormat.format("ERROR: {0}\nException type: {1}\nMessage: {2}", + uri, e.getClass().getName(), e.getMessage()), e); } - return null; + return Optional.empty(); } } diff --git a/code/src/java/pcgen/persistence/lst/LstLineFileLoader.java b/code/src/java/pcgen/persistence/lst/LstLineFileLoader.java index 94565c1e4b5..80e2c5efeb1 100644 --- a/code/src/java/pcgen/persistence/lst/LstLineFileLoader.java +++ b/code/src/java/pcgen/persistence/lst/LstLineFileLoader.java @@ -42,8 +42,8 @@ */ public abstract class LstLineFileLoader extends Observable { - /** - * Stores what game mode the objects loaded by this loader should be + /** + * Stores what game mode the objects loaded by this loader should be * associated with. */ // TODO - Should be a constant. @@ -59,7 +59,8 @@ public abstract class LstLineFileLoader extends Observable */ public void loadLstFile(LoadContext context, URI uri) throws PersistenceLayerException { - String dataBuffer = LstFileLoader.readFromURI(uri); + String dataBuffer = LstFileLoader.readFromURI(uri) + .orElseThrow(() -> new PersistenceLayerException("Failed to read from URI: " + uri)); if (context != null) { context.setSourceURI(uri); diff --git a/code/src/java/pcgen/persistence/lst/LstObjectFileLoader.java b/code/src/java/pcgen/persistence/lst/LstObjectFileLoader.java index 0d7e6eaffea..9d566760dbf 100644 --- a/code/src/java/pcgen/persistence/lst/LstObjectFileLoader.java +++ b/code/src/java/pcgen/persistence/lst/LstObjectFileLoader.java @@ -68,7 +68,7 @@ public abstract class LstObjectFileLoader extends Observab /** * This method loads the given list of LST files. * @param fileList containing the list of files to read - * @throws PersistenceLayerException + * @throws PersistenceLayerException */ public void loadLstFiles(LoadContext context, List fileList) throws PersistenceLayerException { @@ -129,22 +129,22 @@ public abstract T parseLine(LoadContext context, T target, String lstLine, Sourc * This method is called by the loading framework to signify that the * loading of this object is complete and the object should be added to the * system. - * + * *

This method will check that the loaded object should be included via * a call to {@code includeObject} and if not add it to the list of * excluded objects. - * + * *

Once the object has been verified the method will call * {@code finishObject} to give each object a chance to complete * processing. - * + * *

The object is then added to the system if it doesn't already exist. * If the object exists, the object sources are compared by date and if the * System setting allowing over-rides is set it will use the object from the * newer source. * @param context TODO * @param pObj The object that has just completed loading. - * + * * @see pcgen.persistence.lst.LstObjectFileLoader#includeObject(SourceEntry, CDOMObject) */ public void completeObject(LoadContext context, SourceEntry source, final T pObj) @@ -260,7 +260,7 @@ protected boolean includeObject(SourceEntry source, CDOMObject cdo) * duplicate loads * @param context TODO * @param key The CDOMObject containing the key to retrieve (for which there may be a duplicate) - * + * * @return CDOMObject from Globals */ protected T getMatchingObject(LoadContext context, CDOMObject key) @@ -278,10 +278,10 @@ protected void loadLstFile(LoadContext context, CampaignSourceEntry sourceEntry) setChanged(); URI uri = sourceEntry.getURI(); notifyObservers(uri); - String dataBuffer; + String aString; try { - dataBuffer = LstFileLoader.readFromURI(uri); + aString = LstFileLoader.readFromURI(uri).get(); } catch (PersistenceLayerException ple) { @@ -291,7 +291,6 @@ protected void loadLstFile(LoadContext context, CampaignSourceEntry sourceEntry) setChanged(); return; } - String aString = Objects.requireNonNull(dataBuffer); if (context != null) { context.setSourceURI(uri); @@ -426,8 +425,8 @@ private void performForget(LoadContext context, T objToForget) * This method will perform a single .COPY operation based on the LST * file content. * @param context - * @param me - * @throws PersistenceLayerException + * @param me + * @throws PersistenceLayerException */ private void performCopy(LoadContext context, ModEntry me) throws PersistenceLayerException { @@ -461,7 +460,7 @@ private void performCopy(LoadContext context, ModEntry me) throws PersistenceLay /** * Create a copy of an object with a new name. If the base object cannot be found, an error will be reported unless * the copy has been excluded by include/exclude rules for the source. - * + * * @param context The current load context in whihc the new object is to be created. * @param baseName The name of the object to be copied. * @param copyName The name of the new object. @@ -588,7 +587,7 @@ private void performMod(LoadContext context, List entryList) /** * This method will process the lines containing a .COPY directive - * @throws PersistenceLayerException + * @throws PersistenceLayerException */ private void processCopies(LoadContext context) throws PersistenceLayerException { @@ -656,14 +655,14 @@ public static final class ModEntry * @param aLstLine LST syntax modification * [must not be null] * @param aLineNumber - * + * * @throws IllegalArgumentException if aSource or aLstLine is null. */ private ModEntry(final CampaignSourceEntry aSource, final String aLstLine, final int aLineNumber) { super(); - // These are programming errors so the msgs don't need to be + // These are programming errors so the msgs don't need to be // internationalized. Objects.requireNonNull(aSource, "source must not be null"); diff --git a/code/src/java/pcgen/persistence/lst/LstToken.java b/code/src/java/pcgen/persistence/lst/LstToken.java index 062065296c3..191007c67e9 100644 --- a/code/src/java/pcgen/persistence/lst/LstToken.java +++ b/code/src/java/pcgen/persistence/lst/LstToken.java @@ -21,10 +21,9 @@ public interface LstToken { - - /** + /** * Get Token Name * @return token name */ - public abstract String getTokenName(); + String getTokenName(); } diff --git a/code/src/java/pcgen/persistence/lst/URIFactory.java b/code/src/java/pcgen/persistence/lst/URIFactory.java index df698cd08b1..35a38d65bcc 100644 --- a/code/src/java/pcgen/persistence/lst/URIFactory.java +++ b/code/src/java/pcgen/persistence/lst/URIFactory.java @@ -19,12 +19,11 @@ package pcgen.persistence.lst; import java.io.File; -import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; import java.util.Objects; +import org.apache.commons.lang3.ObjectUtils; import pcgen.base.lang.UnreachableError; import pcgen.core.utils.CoreUtility; import pcgen.system.ConfigurationSettings; @@ -67,7 +66,7 @@ public class URIFactory /** * Constructs a new URIFactory with the given root URI and offset - * + * * @param root * The root URI used as the starting point to determine the final * URI @@ -77,17 +76,15 @@ public class URIFactory public URIFactory(URI root, String offset) { Objects.requireNonNull(root, "root URI cannot be null"); - if (offset == null || offset.isEmpty()) - { - throw new IllegalArgumentException("URI offset cannot be null"); - } - rootURI = root; + ObjectUtils.requireNonEmpty(offset, "offset URI cannot be null or empty"); + + this.rootURI = root; this.offset = offset; } /** * Returns the root URI for this URIFactory. - * + * * @return The root URI for this URIFactory */ public URI getRootURI() @@ -97,7 +94,7 @@ public URI getRootURI() /** * Returns the offset for this URIFactory. - * + * * @return The offset for this URIFactory */ public String getOffset() @@ -108,7 +105,7 @@ public String getOffset() /** * Returns the normalized URI resulting from the root URI and offset of this * URIFactory. - * + * * @return The normalized URI resulting from the root URI and offset of this * URIFactory */ @@ -136,7 +133,7 @@ public boolean equals(Object o) /** * This method converts the provided filePath to either a URL or absolute * path as appropriate. - * + * * @param pccPath * URL where the Campaign that contained the source was at * @param basePath @@ -196,13 +193,11 @@ else if (basePath.indexOf(':') > 0) { try { - // if it's a URL, then we are all done, just return a URI - URL url = new URL(basePath); - return new URI(url.getProtocol(), url.getHost(), url.getPath(), null); + return new URI(basePath); } - catch (URISyntaxException | MalformedURLException e) + catch (URISyntaxException e) { - //Something broke, so wasn't a URL + return FAILED_URI; } } @@ -245,7 +240,7 @@ else if (basePath.indexOf(':') > 0) /** * This method trims the leading file separator or URL separator from the * front of a string. - * + * * @param basePath * String containing the base path to trim * @return String containing the trimmed path String diff --git a/code/src/java/pcgen/persistence/lst/VariableLoader.java b/code/src/java/pcgen/persistence/lst/VariableLoader.java index a8f0b15ec2d..836280641a6 100644 --- a/code/src/java/pcgen/persistence/lst/VariableLoader.java +++ b/code/src/java/pcgen/persistence/lst/VariableLoader.java @@ -3,17 +3,17 @@ * Copyright 2008-10 (C) Tom Parker * Copyright 2003 (C) David Hibbs * Copyright 2001 (C) Bryan McRoberts - * + * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. - * + * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA @@ -89,7 +89,7 @@ public void loadLstFiles(LoadContext context, List fileList /** * This method loads a single LST formatted file. - * + * * @param sourceEntry * CampaignSourceEntry containing the absolute file path or the * URL from which to read LST formatted data. @@ -100,11 +100,10 @@ protected void loadLstFile(LoadContext context, CampaignSourceEntry sourceEntry) URI uri = sourceEntry.getURI(); notifyObservers(uri); - String dataBuffer; - + String aString; try { - dataBuffer = LstFileLoader.readFromURI(uri); + aString = LstFileLoader.readFromURI(uri).get(); } catch (PersistenceLayerException ple) { @@ -115,7 +114,6 @@ protected void loadLstFile(LoadContext context, CampaignSourceEntry sourceEntry) return; } - String aString = dataBuffer; if (context != null) { context.setSourceURI(uri); diff --git a/code/src/java/pcgen/persistence/lst/prereq/PreParserFactory.java b/code/src/java/pcgen/persistence/lst/prereq/PreParserFactory.java index 08988282731..d90cec3dc5f 100644 --- a/code/src/java/pcgen/persistence/lst/prereq/PreParserFactory.java +++ b/code/src/java/pcgen/persistence/lst/prereq/PreParserFactory.java @@ -166,13 +166,13 @@ public Prerequisite parse(String prereqStr) throws PersistenceLayerException } catch (Throwable t) { - throw new PersistenceLayerException("Can not parse '" + prereqStr + "': " + t.getMessage(), t); + throw new PersistenceLayerException("Cannot parse '" + prereqStr + "': " + t.getMessage(), t); } } /** * Identify if the token passed in defines a prerequisite. - * + * * @param token The token to be checked. * @return True if the string is a prereq string. */ diff --git a/code/src/java/pcgen/rules/context/LoadContextInst.java b/code/src/java/pcgen/rules/context/LoadContextInst.java index 2bfee8a75a8..0394b798f64 100644 --- a/code/src/java/pcgen/rules/context/LoadContextInst.java +++ b/code/src/java/pcgen/rules/context/LoadContextInst.java @@ -1,16 +1,16 @@ /* * Copyright 2007 (C) Tom Parker - * + * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. - * + * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA @@ -24,6 +24,8 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.logging.Logger; +import java.util.logging.Level; import pcgen.base.formula.inst.NEPFormula; import pcgen.base.proxy.DeferredMethodController; @@ -63,6 +65,7 @@ abstract class LoadContextInst implements LoadContext { + Logger LOG = Logger.getLogger(LoadContextInst.class.getName()); private static final PrerequisiteWriter PREREQ_WRITER = new PrerequisiteWriter(); @@ -135,7 +138,7 @@ public int getWriteMessageCount() /** * Sets the extract URI. This is a shortcut for setting the URI on both the * graph and obj members. - * + * * @param extractURI */ @Override @@ -149,7 +152,7 @@ public void setExtractURI(URI extractURI) /** * Sets the source URI. This is a shortcut for setting the URI on both the * graph and obj members. - * + * * @param sourceURI */ @Override @@ -160,7 +163,7 @@ public void setSourceURI(URI sourceURI) getReferenceContext().setSourceURI(sourceURI); getListContext().setSourceURI(sourceURI); clearStatefulInformation(); - Logging.debugPrint("Starting Load of " + sourceURI); + LOG.log(Level.FINER, "Starting Load of " + sourceURI); } @Override @@ -346,7 +349,7 @@ public void unconditionallyProcess(T cdo, String key, Strin /** * Produce the LST code for any occurrences of subtokens of the parent token. - * + * * @param The type of object to be processed, generally a CDOMObject. * @param cdo The object to be partially unparsed * @param tokenName The name of the parent token @@ -373,12 +376,12 @@ public T cloneConstructedCDOMObject(T cdo, String newName } /** - * Create a copy of a CDOMObject duplicating any references to the old + * Create a copy of a CDOMObject duplicating any references to the old * object. (e.g. Spell, Domain etc) - * + * * Package protected rather than private for testing only - * - * @param cdo The original object being copied. + * + * @param cdo The original object being copied. * @param newName The name that should be given to the new object. * @return The newly created CDOMObject. */ diff --git a/code/src/java/pcgen/rules/persistence/token/CDOMToken.java b/code/src/java/pcgen/rules/persistence/token/CDOMToken.java index 821befee3f5..2c3fbb3c4ad 100644 --- a/code/src/java/pcgen/rules/persistence/token/CDOMToken.java +++ b/code/src/java/pcgen/rules/persistence/token/CDOMToken.java @@ -22,7 +22,7 @@ public interface CDOMToken extends LstToken { - public ParseResult parseToken(LoadContext context, T obj, String value); + ParseResult parseToken(LoadContext context, T obj, String value); - public Class getTokenClass(); + Class getTokenClass(); } diff --git a/code/src/java/pcgen/rules/persistence/token/CDOMWriteToken.java b/code/src/java/pcgen/rules/persistence/token/CDOMWriteToken.java index 9d108f43825..aa74d7fba39 100644 --- a/code/src/java/pcgen/rules/persistence/token/CDOMWriteToken.java +++ b/code/src/java/pcgen/rules/persistence/token/CDOMWriteToken.java @@ -22,5 +22,5 @@ public interface CDOMWriteToken extends LstToken { - public String[] unparse(LoadContext context, T obj); + String[] unparse(LoadContext context, T obj); } diff --git a/code/src/java/pcgen/system/CommandLineArguments.java b/code/src/java/pcgen/system/CommandLineArguments.java index 20888b76d39..ed898da23fc 100644 --- a/code/src/java/pcgen/system/CommandLineArguments.java +++ b/code/src/java/pcgen/system/CommandLineArguments.java @@ -1,25 +1,42 @@ package pcgen.system; import java.io.File; +import java.util.HashMap; import java.util.List; import java.util.Optional; +import java.util.function.Predicate; + import net.sourceforge.argparse4j.ArgumentParsers; +import net.sourceforge.argparse4j.helper.HelpScreenException; import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.ArgumentParser; +import net.sourceforge.argparse4j.inf.ArgumentParserException; import net.sourceforge.argparse4j.inf.MutuallyExclusiveGroup; import net.sourceforge.argparse4j.inf.Namespace; import org.apache.commons.lang3.Validate; import pcgen.cdom.base.Constants; +import pcgen.util.GracefulExit; public class CommandLineArguments { - - private final Namespace namespace; + private Namespace namespace = new Namespace(new HashMap<>()); public CommandLineArguments(String[] args) { Validate.notNull(args, "Parameter 'args' must not be null"); - this.namespace = getParser().parseArgsOrFail(args); + var parser = this.getParser(); + try + { + this.namespace = parser.parseArgs(args); + } catch (HelpScreenException e) + { + parser.handleError(e); + GracefulExit.exit(0); + } catch (ArgumentParserException e) + { + parser.handleError(e); + GracefulExit.exit(1); + } } /** @@ -67,12 +84,9 @@ private ArgumentParser getParser() private Optional getSingle(List list) { - if (list == null || list.isEmpty()) - { - return Optional.empty(); - } - - return Optional.ofNullable(list.get(0)); + return Optional.ofNullable(list) + .filter(Predicate.not(List::isEmpty)) + .map(List::getFirst); } public Optional getSettingsDir() @@ -117,13 +131,15 @@ public Optional getOutputFile() public boolean isStartNameGenerator() { - return namespace.get("name_generator"); + return Optional.ofNullable(namespace.getBoolean("name_generator")) + .orElse(false); } public boolean isVerbose() { // Why allow the flag multiple times and count them if we just evaluate them to boolean afterward? // Seems unintentional. - return namespace.getInt("verbose") > 0; + return Optional.ofNullable(namespace.getInt("verbose")) + .orElse(0) > 0; } } diff --git a/code/src/java/pcgen/system/Main.java b/code/src/java/pcgen/system/Main.java index 5e528ac22bb..06aec375d08 100644 --- a/code/src/java/pcgen/system/Main.java +++ b/code/src/java/pcgen/system/Main.java @@ -57,6 +57,7 @@ import pcgen.system.application.DeadlockDetectorTask; import pcgen.system.application.LoggingUncaughtExceptionHandler; import pcgen.system.application.PCGenLoggingDeadlockHandler; +import pcgen.util.GracefulExit; import pcgen.util.Logging; import pcgen.util.PJEP; @@ -128,7 +129,7 @@ public static void main(String... args) { Component dialog = new RandomNameDialog(null, null); dialog.setVisible(true); - System.exit(0); + GracefulExit.exit(0); } if (commandLineArguments.getExportSheet().isEmpty()) @@ -249,7 +250,7 @@ private static void validateEnvironment(boolean useGui) JOptionPane.showMessageDialog(null, message + "\nPlease reinstall PCGen.", Constants.APPLICATION_NAME, JOptionPane.ERROR_MESSAGE); } - System.exit(1); + GracefulExit.exit(1); } } @@ -261,7 +262,7 @@ public static void loadProperties(boolean useGui) if (!useGui) { Logging.errorPrint("No settingsDir specified via -s in batch mode and no default exists."); - System.exit(1); + GracefulExit.exit(1); } var panel = new JFXPanelFromResource<>( OptionsPathDialogController.class, @@ -367,7 +368,7 @@ public static void shutdown(boolean success) CustomData.writeCustomItems(); } - System.exit(success ? 0 : 1); + GracefulExit.exit(success ? 0 : 1); } private static void initPrintPreviewFonts() diff --git a/code/src/java/pcgen/system/PCGenTask.java b/code/src/java/pcgen/system/PCGenTask.java index b17051d6cbe..7d7d3d9342f 100644 --- a/code/src/java/pcgen/system/PCGenTask.java +++ b/code/src/java/pcgen/system/PCGenTask.java @@ -1,20 +1,20 @@ /* * Copyright 2010 Connor Petty - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * */ package pcgen.system; @@ -27,7 +27,6 @@ public abstract class PCGenTask implements Runnable, ProgressContainer { - private final EventListenerList listenerList = new EventListenerList(); private int progress = 0; private int maximum = 0; @@ -159,5 +158,4 @@ protected void fireErrorOccurredEvent(LogRecord message) } } } - } diff --git a/code/src/java/pcgen/system/PCGenTaskExecutor.java b/code/src/java/pcgen/system/PCGenTaskExecutor.java index 0aff7eefbac..8a875eb22ac 100644 --- a/code/src/java/pcgen/system/PCGenTaskExecutor.java +++ b/code/src/java/pcgen/system/PCGenTaskExecutor.java @@ -1,20 +1,20 @@ /* * Copyright 2010 Connor Petty - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * */ package pcgen.system; @@ -24,7 +24,6 @@ class PCGenTaskExecutor extends PCGenTask implements PCGenTaskListener { - private final LinkedList tasks = new LinkedList<>(); private PCGenTask currentTask = null; private Fraction progressMultiplier = null; diff --git a/code/src/java/pcgen/system/PluginClassLoader.java b/code/src/java/pcgen/system/PluginClassLoader.java index 7c5ce16a984..67f9948b5dc 100644 --- a/code/src/java/pcgen/system/PluginClassLoader.java +++ b/code/src/java/pcgen/system/PluginClassLoader.java @@ -1,31 +1,31 @@ /* * Copyright 2009 Connor Petty - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * */ package pcgen.system; import java.io.File; -import java.io.FilenameFilter; import java.io.IOException; -import java.io.InputStream; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collection; -import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; @@ -33,7 +33,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import pcgen.base.util.HashMapToList; @@ -44,21 +43,10 @@ class PluginClassLoader extends PCGenTask { - - private static final FilenameFilter PLUGIN_FILTER = (dir, name) -> { - if (name.contains("plugin")) - { - return true; - } - return StringUtils.endsWithIgnoreCase(name, ".jar"); - }; private final File pluginDir; private final MapToList, PluginLoader> loaderMap; - private final ExecutorService dispatcher = Executors.newSingleThreadExecutor(r -> { - Thread thread = new Thread(r, "Plugin-loading-thread"); - thread.setDaemon(true); - return thread; - }); + private final ExecutorService dispatcher = Executors.newSingleThreadExecutor( + runnable -> Thread.ofPlatform().name("Plugin-loading-thread").daemon().unstarted(runnable)); private final LinkedList jarFiles = new LinkedList<>(); private int progress = 0; @@ -87,33 +75,31 @@ private void loadClasses(final File pluginJar) throws IOException try (JarClassLoader loader = new JarClassLoader(pluginJar.toURI().toURL()); ZipFile file = new ZipFile(pluginJar)) { + var jarClasses = file.stream() + .filter(entry -> entry.getName().endsWith(".class")); final Collection classList = new LinkedList<>(); - Enumeration entries = file.entries(); - while (entries.hasMoreElements()) - { - ZipEntry entry = entries.nextElement(); - String name = entry.getName(); - if (!name.endsWith(".class")) - { - continue; - } - name = StringUtils.removeEnd(name, ".class").replace('/', '.'); + jarClasses.forEach(entry -> { + String name = StringUtils.removeEnd(entry.getName(), ".class").replace('/', '.'); + - byte[] buffer; - try (InputStream in = file.getInputStream(entry)) + try (var in = file.getInputStream(entry)) { - buffer = in.readAllBytes(); + byte[] buffer = in.readAllBytes(); + loader.storeClassDef(name, buffer); + classList.add(name); + } catch (IOException e) + { + Logging.errorPrint("Error occurred while extracting a class file " + name + " from JAR: " + pluginJar, e); } - loader.storeClassDef(name, buffer); - classList.add(name); - } - file.close(); + }); + /* * Loading files and loading classes can both be lengthy processes. This splits the tasks - * so that class loading occurs in another thread thus allowing both processes to + * so that class loading occurs in another thread, thus allowing both processes to * operate at the same time. */ - dispatcher.execute(() -> { + dispatcher.execute(() -> + { boolean pluginFound = false; for (final String string : classList) { @@ -153,7 +139,7 @@ private boolean processClass(Class clazz) } catch (final Exception ex) { - Logging.errorPrint("Error occurred while loading plugin class: " + clazz.getName(), ex); + Logging.errorPrint("Error occurred while loading a plugin class: " + clazz.getName(), ex); } finally { @@ -183,55 +169,44 @@ public void loadPlugins() } catch (ExecutionException | InterruptedException ex) { - Logging.debugPrint("exception during shutdown", ex); + Logging.errorPrint("Exception during shutdown", ex); } } - @SuppressWarnings("PMD.UseArraysAsList") private void findJarFiles(File pluginDir) { if (!pluginDir.isDirectory()) { return; } - File[] pluginFiles = pluginDir.listFiles(PluginClassLoader.PLUGIN_FILTER); - if (pluginFiles != null) + + try (DirectoryStream stream = Files.newDirectoryStream(pluginDir.toPath(), "*plugins.jar")) { - for (final File file : pluginFiles) - { - if (file.isDirectory()) - { - findJarFiles(file); - continue; - } - jarFiles.add(file); - } + stream.forEach(path -> jarFiles.add(path.toFile())); } - else + catch (IOException e) { - Logging.errorPrint("pluginFiles array was NULL after trying to load the plugins from the plugin class loader"); + Logging.errorPrint("Couldn't process the ./plugins/ folder.", e); } } private void loadClasses() { - while (!jarFiles.isEmpty()) - { - File file = jarFiles.poll(); + jarFiles.forEach(file -> { try { loadClasses(file); } catch (final IOException ex) { - Logging.errorPrint("Could not load classes from file: " + file.getAbsolutePath(), ex); + Logging.errorPrint("Could not load classes from a file: " + file.getAbsolutePath(), ex); } - } + }); + jarFiles.clear(); } private static final class JarClassLoader extends URLClassLoader { - private final Map classDefinitions = new HashMap<>(); private JarClassLoader(URL url) @@ -250,7 +225,7 @@ protected Class findClass(String name) throws ClassNotFoundException byte[] bytes = classDefinitions.remove(name); if (bytes == null) { - throw new ClassNotFoundException(); + throw new ClassNotFoundException("The class with the name cannot be found: " + name); } return defineClass(name, bytes, 0, bytes.length); } diff --git a/code/src/java/pcgen/system/PluginLoader.java b/code/src/java/pcgen/system/PluginLoader.java index 509adf66a23..9d33a4866fe 100644 --- a/code/src/java/pcgen/system/PluginLoader.java +++ b/code/src/java/pcgen/system/PluginLoader.java @@ -1,28 +1,27 @@ /* * Copyright 2009 Connor Petty - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * */ package pcgen.system; public interface PluginLoader { - public void loadPlugin(Class clazz) throws Exception; - - public Class[] getPluginClasses(); + void loadPlugin(Class clazz) throws Exception; + Class[] getPluginClasses(); } diff --git a/code/src/java/pcgen/util/ExitFunction.java b/code/src/java/pcgen/util/ExitFunction.java new file mode 100644 index 00000000000..4d4208122c9 --- /dev/null +++ b/code/src/java/pcgen/util/ExitFunction.java @@ -0,0 +1,15 @@ +package pcgen.util; + +/** + * An interface that can be used to exit the JVM. + */ +public interface ExitFunction +{ + /** + * The method must be used to exit from JVM in GracefulExit class. + * @see pcgen.util.GracefulExit + * + * @param status The status code to exit with + */ + void exit(int status); +} diff --git a/code/src/java/pcgen/util/ExitInterceptor.java b/code/src/java/pcgen/util/ExitInterceptor.java new file mode 100644 index 00000000000..a49d1975e46 --- /dev/null +++ b/code/src/java/pcgen/util/ExitInterceptor.java @@ -0,0 +1,15 @@ +package pcgen.util; + +/** + * An interface that can be used to intercept the exit of the JVM. + */ +public interface ExitInterceptor +{ + /** + * Intercepts the exit of the JVM. + * + * @param status The status code to exit with + * @return true if the exit should proceed, false if the exit should be canceled + */ + boolean intercept(int status); +} diff --git a/code/src/java/pcgen/util/GracefulExit.java b/code/src/java/pcgen/util/GracefulExit.java new file mode 100644 index 00000000000..94747274b88 --- /dev/null +++ b/code/src/java/pcgen/util/GracefulExit.java @@ -0,0 +1,142 @@ +package pcgen.util; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Predicate; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This class provides a mechanism to intercept calls to System.exit and perform additional actions before the JVM + * exits. + *

+ * This class is thread-safe. + */ +public final class GracefulExit +{ + private static final Logger LOG = Logger.getLogger(GracefulExit.class.getName()); + + /* This lock protects the preceding static fields */ + private static final ReentrantLock lock = new ReentrantLock(); + + private static final List interceptors = new ArrayList<>(); + + private static boolean isShuttingDown; + + private static ExitFunction exitFunction = System::exit; + + private GracefulExit() + { + } + + /** + * Exits the JVM with the given status code. This method will call all registered interceptors before exiting. + *

+ * If any interceptor returns false, the exit will be canceled. + * + * @param status The status code to exit with + */ + public static void exit(int status) + { + try + { + lock.lock(); + LOG.log(Level.FINEST, () -> MessageFormat.format( + "Started exiting with the status {0}. There are {1} interceptors.", status, + interceptors.size())); + isShuttingDown = true; + + boolean shouldExit = interceptors.stream() + .map((ExitInterceptor interceptor) -> { + boolean intercepted = interceptor.intercept(status); + LOG.log(Level.FINEST, + MessageFormat.format("Intercepted exit: {0} from registered interceptor: {1}", + intercepted, interceptor.getClass().getName())); + return intercepted; + }) + .filter(Predicate.isEqual(Boolean.FALSE)) + .findAny() + .orElse(Boolean.TRUE); + + if (shouldExit) + { + LOG.info(() -> MessageFormat.format("Exiting gracefully with status {0}", status)); + exitFunction.exit(status); + } + } finally + { + isShuttingDown = false; + lock.unlock(); + } + } + + /** + * Adds an interceptor to the list of interceptors. This method can be called before or after shutdown has started, + * otherwise an exception will be thrown. + * + * @param interceptor The interceptor to add + */ + public static void addExitInterceptor(ExitInterceptor interceptor) + { + try + { + lock.lock(); + if (isShuttingDown) + { + throw new IllegalStateException("Cannot add an interceptor after shutdown has started"); + } + if (interceptors.contains(interceptor)) + { + throw new IllegalArgumentException("Interceptor already registered"); + } + interceptors.add(interceptor); + } finally + { + lock.unlock(); + } + } + + /** + * Clears all registered interceptors. This method can be called before or after shutdown has started, otherwise an + * exception will be thrown. + */ + public static void clearExitInterceptors() + { + try + { + lock.lock(); + if (isShuttingDown) + { + throw new IllegalStateException("Cannot clear interceptors after shutdown has started"); + } + interceptors.clear(); + } finally + { + lock.unlock(); + } + } + + /** + * Registers an exit function to be called when the JVM exits. This method can be called before or after shutdown has + * started, otherwise an exception will be thrown. + * + * @param exitFunction The exit function to register + */ + public static void registerExitFunction(ExitFunction exitFunction) + { + try + { + lock.lock(); + if (isShuttingDown) + { + throw new IllegalStateException("Cannot register an exit function after shutdown has started"); + } + GracefulExit.exitFunction = exitFunction; + } finally + { + lock.unlock(); + } + } +} diff --git a/code/src/java/pcgen/util/Logging.java b/code/src/java/pcgen/util/Logging.java index d3c390434a1..fc1ec42fb96 100644 --- a/code/src/java/pcgen/util/Logging.java +++ b/code/src/java/pcgen/util/Logging.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.PrintStream; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.text.NumberFormat; import java.util.ArrayList; import java.util.LinkedList; @@ -69,8 +70,8 @@ public final class Logging /** Log level for application debug output. */ public static final Level DEBUG = Level.FINER; - private static Logger pcgenLogger; - private static Logger pluginLogger; + private static Logger pcgenLogger = Logger.getLogger("pcgen"); + private static Logger pluginLogger = Logger.getLogger("plugin"); /** * Do any required initialization of the Logger. @@ -113,31 +114,19 @@ private Logging() */ public static void setDebugMode(final boolean argDebugMode) { - retainRootLoggers(); - debugMode = argDebugMode; if (debugMode) { - Logger.getLogger("pcgen").setLevel(DEBUG); - Logger.getLogger("plugin").setLevel(DEBUG); + pcgenLogger.setLevel(DEBUG); + pluginLogger.setLevel(DEBUG); } else { - Logger.getLogger("pcgen").setLevel(LST_WARNING); - Logger.getLogger("plugin").setLevel(LST_WARNING); + pcgenLogger.setLevel(LST_WARNING); + pluginLogger.setLevel(LST_WARNING); } } - /** - * Ensure that our root loggers (pcgen and plugin) do not get garbage - * collected, otherwise we lose the logging level! - */ - private static void retainRootLoggers() - { - pcgenLogger = Logger.getLogger("pcgen"); - pluginLogger = Logger.getLogger("plugin"); - } - /** * Is someone debugging PCGen? * @@ -396,8 +385,7 @@ public static void errorPrint(final String s, final URI sourceURI) } /** - * Print error message with a stack trace if PCGen is - * debugging. + * Print an error message with a stack trace if PCGen is debugging. * * @param s String error message * @param thr Throwable stack frame @@ -405,7 +393,7 @@ public static void errorPrint(final String s, final URI sourceURI) public static void errorPrint(final String s, final Throwable thr) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - PrintStream ps = new PrintStream(baos); + PrintStream ps = new PrintStream(baos, true, StandardCharsets.UTF_8); thr.printStackTrace(ps); errorPrint(s + '\n' + baos); } @@ -471,7 +459,7 @@ public static void log(Level lvl, String msg, StackTraceElement[] stackTrace) public static void errorPrintLocalised(final String s, final Throwable thr) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (PrintStream ps = new PrintStream(baos)) + try (PrintStream ps = new PrintStream(baos, true, StandardCharsets.UTF_8)) { thr.printStackTrace(ps); } @@ -613,10 +601,9 @@ public static Level getCurrentLoggingLevel() */ public static void setCurrentLoggingLevel(Level level) { - retainRootLoggers(); debugMode = (level == Logging.DEBUG); - Logger.getLogger("pcgen").setLevel(level); - Logger.getLogger("plugin").setLevel(level); + pcgenLogger.setLevel(level); + pluginLogger.setLevel(level); } private static final LinkedList queuedMessages = new LinkedList<>(); diff --git a/code/src/java/pcgen/util/enumeration/Load.java b/code/src/java/pcgen/util/enumeration/Load.java index c71b7751bd6..cc73bf74875 100644 --- a/code/src/java/pcgen/util/enumeration/Load.java +++ b/code/src/java/pcgen/util/enumeration/Load.java @@ -33,7 +33,6 @@ public enum Load HEAVY(FontManipulation::bold_italic, UIPropertyContext::getVirtualColor), OVERLOAD(FontManipulation::bold_italic, UIPropertyContext::getNotQualifiedColor); - private static final DoubleFunction LIGHT_ENCUMBERED_MOVE = unencumberedMove -> unencumberedMove; private static final DoubleFunction MEDIUM_HEAVY_ENCUMBERED_MOVE = unencumberedMove -> { if (CoreUtility.doublesEqual(unencumberedMove, 5) || CoreUtility.doublesEqual(unencumberedMove, 10)) diff --git a/code/src/java/plugin/exporttokens/SpellMemToken.java b/code/src/java/plugin/exporttokens/SpellMemToken.java index 9924d6c204b..b0588c8d4ee 100644 --- a/code/src/java/plugin/exporttokens/SpellMemToken.java +++ b/code/src/java/plugin/exporttokens/SpellMemToken.java @@ -260,7 +260,9 @@ else if ("DESC".equals(aLabel) || "EFFECT".equals(aLabel)) { String mString = aPC.getInfoToken(".INFO.DESC", aSpell); if (mString.equals(".INFO.DESC")) + { mString = aPC.parseSpellString(selectedCSpell, aPC.getDescription(aSpell)); + } retValue.append(mString); } else if ("TARGET".equals(aLabel) || "EFFECTYPE".equals(aLabel)) diff --git a/code/src/java/plugin/lsttokens/TemplateLst.java b/code/src/java/plugin/lsttokens/TemplateLst.java index 6c765d9559c..27ea7cca9dd 100644 --- a/code/src/java/plugin/lsttokens/TemplateLst.java +++ b/code/src/java/plugin/lsttokens/TemplateLst.java @@ -116,8 +116,8 @@ else if (specialLegal && templKey.equals(Constants.LST_PERCENT_LIST)) if (ref == null) { // If we have an invalid template reference, regardless of type, log it - Logging.log(Logging.WARNING, "Invalid template reference in TEMPLATE token in " + - cdo.getDisplayName() + ": " + templKey); + Logging.log(Logging.WARNING, "Invalid template reference in TEMPLATE token in " + + cdo.getDisplayName() + ": " + templKey); return ParseResult.INTERNAL_ERROR; } diff --git a/code/src/java/plugin/lsttokens/equipment/IconToken.java b/code/src/java/plugin/lsttokens/equipment/IconToken.java index 341cdc738f8..a204c240be9 100644 --- a/code/src/java/plugin/lsttokens/equipment/IconToken.java +++ b/code/src/java/plugin/lsttokens/equipment/IconToken.java @@ -30,10 +30,8 @@ import pcgen.rules.persistence.token.ParseResult; /** - * The Class {@code IconToken} processes the ICPON token. This + * The Class {@code IconToken} processes the ICON token. This * allows an icon to be specified for the item of equipment. - * - * */ public class IconToken extends AbstractNonEmptyToken implements CDOMPrimaryToken { diff --git a/code/src/resources/pcgen/gui3/SimpleHtmlPanel.fxml b/code/src/resources/pcgen/gui3/SimpleHtmlPanel.fxml index bf6ee0ce5f2..ca6bb40fb91 100644 --- a/code/src/resources/pcgen/gui3/SimpleHtmlPanel.fxml +++ b/code/src/resources/pcgen/gui3/SimpleHtmlPanel.fxml @@ -23,7 +23,7 @@ --> - + diff --git a/code/src/resources/pcgen/gui3/dialog/AboutDialog.fxml b/code/src/resources/pcgen/gui3/dialog/AboutDialog.fxml index 0b0f9642878..7611e4b43d4 100644 --- a/code/src/resources/pcgen/gui3/dialog/AboutDialog.fxml +++ b/code/src/resources/pcgen/gui3/dialog/AboutDialog.fxml @@ -25,7 +25,7 @@ - diff --git a/code/src/resources/pcgen/gui3/dialog/CalculatorDialog.fxml b/code/src/resources/pcgen/gui3/dialog/CalculatorDialog.fxml index c6e57db10ce..43f730effd0 100644 --- a/code/src/resources/pcgen/gui3/dialog/CalculatorDialog.fxml +++ b/code/src/resources/pcgen/gui3/dialog/CalculatorDialog.fxml @@ -26,7 +26,7 @@ - diff --git a/code/src/resources/pcgen/gui3/dialog/DebugDialog.fxml b/code/src/resources/pcgen/gui3/dialog/DebugDialog.fxml index 0eaacc029de..684aee66f17 100644 --- a/code/src/resources/pcgen/gui3/dialog/DebugDialog.fxml +++ b/code/src/resources/pcgen/gui3/dialog/DebugDialog.fxml @@ -21,8 +21,8 @@ - diff --git a/code/src/resources/pcgen/gui3/dialog/ExportDialog.fxml b/code/src/resources/pcgen/gui3/dialog/ExportDialog.fxml index dcd204cc0a1..6a0e5bb5c82 100644 --- a/code/src/resources/pcgen/gui3/dialog/ExportDialog.fxml +++ b/code/src/resources/pcgen/gui3/dialog/ExportDialog.fxml @@ -31,8 +31,8 @@ - diff --git a/code/src/resources/pcgen/gui3/dialog/TipOfTheDay.fxml b/code/src/resources/pcgen/gui3/dialog/TipOfTheDay.fxml index bde462a2055..c639884c4cb 100644 --- a/code/src/resources/pcgen/gui3/dialog/TipOfTheDay.fxml +++ b/code/src/resources/pcgen/gui3/dialog/TipOfTheDay.fxml @@ -32,7 +32,7 @@ --> - + diff --git a/code/src/resources/pcgen/gui3/preferences/CenteredLabelPanel.fxml b/code/src/resources/pcgen/gui3/preferences/CenteredLabelPanel.fxml index ba7823e0018..b7e8d537660 100644 --- a/code/src/resources/pcgen/gui3/preferences/CenteredLabelPanel.fxml +++ b/code/src/resources/pcgen/gui3/preferences/CenteredLabelPanel.fxml @@ -19,7 +19,7 @@ --> - +

diff --git a/code/src/resources/pcgen/gui3/preloader/PCGenPreloader.fxml b/code/src/resources/pcgen/gui3/preloader/PCGenPreloader.fxml index c8e9da59ae5..97723324c8f 100644 --- a/code/src/resources/pcgen/gui3/preloader/PCGenPreloader.fxml +++ b/code/src/resources/pcgen/gui3/preloader/PCGenPreloader.fxml @@ -23,7 +23,7 @@ - diff --git a/code/src/resources/pcgen/lang/LanguageBundle.properties b/code/src/resources/pcgen/lang/LanguageBundle.properties index c12df858f6c..0b9bde9d640 100644 --- a/code/src/resources/pcgen/lang/LanguageBundle.properties +++ b/code/src/resources/pcgen/lang/LanguageBundle.properties @@ -369,6 +369,7 @@ in_loadPartyNoSources=Could not find source files for party: {0} in_err_browser_err=Couldn't launch browser in_err_browser_uri=Unable to view {0} +in_err_remote_lst_warn=Preferences are currently set to NOT allow\nloading of sources from web links.\n{0} is a web link. #About dialog in_abt_title=About PCGen diff --git a/code/src/resources/plugin/experience/gui3/PreferencesExperiencesPanel.fxml b/code/src/resources/plugin/experience/gui3/PreferencesExperiencesPanel.fxml index 282a41917d5..ba6c7d2a5a5 100644 --- a/code/src/resources/plugin/experience/gui3/PreferencesExperiencesPanel.fxml +++ b/code/src/resources/plugin/experience/gui3/PreferencesExperiencesPanel.fxml @@ -19,8 +19,8 @@ --> - diff --git a/code/src/test/pcgen/inttest/PcgenFtlErrorTest.java b/code/src/test/pcgen/inttest/PcgenFtlErrorTest.java new file mode 100644 index 00000000000..dc2200dad43 --- /dev/null +++ b/code/src/test/pcgen/inttest/PcgenFtlErrorTest.java @@ -0,0 +1,39 @@ +package pcgen.inttest; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import pcgen.system.Main; +import pcgen.util.GracefulExit; + +import java.text.MessageFormat; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class PcgenFtlErrorTest +{ + @BeforeEach + public void setUp() + { + GracefulExit.registerExitFunction((int status) -> { + assertEquals(1, status, + MessageFormat.format("The PCGen execution returned an unexpected status code: {0}.", status)); + throw new IllegalStateException("The test execution is aborted intentionally."); + }); + } + + @AfterEach + public void tearDown() + { + GracefulExit.registerExitFunction(System::exit); + } + + @Test + void testWrongInputParameters() + { + assertThrows(IllegalStateException.class, () -> Main.main("--character", "dummy.chr")); + assertThrows(IllegalStateException.class, () -> Main.main("--exportsheet", "dummy.xml")); + assertThrows(IllegalStateException.class, () -> Main.main("--verbose", "")); + } +} diff --git a/code/src/test/pcgen/inttest/PcgenFtlTestCase.java b/code/src/test/pcgen/inttest/PcgenFtlTestCase.java index 38d2175fd07..ab80eb14a7b 100644 --- a/code/src/test/pcgen/inttest/PcgenFtlTestCase.java +++ b/code/src/test/pcgen/inttest/PcgenFtlTestCase.java @@ -17,19 +17,24 @@ */ package pcgen.inttest; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.Locale; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.xmlunit.builder.DiffBuilder; import org.xmlunit.builder.Input; import org.xmlunit.diff.Diff; import pcgen.LocaleDependentTestCase; -import pcgen.cdom.base.Constants; +import pcgen.system.Main; +import pcgen.util.GracefulExit; import pcgen.util.TestHelper; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.text.MessageFormat; +import java.util.Locale; +import java.util.logging.Logger; + +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; /** @@ -39,6 +44,8 @@ */ public abstract class PcgenFtlTestCase { + private static final Logger LOG = Logger.getLogger(PcgenFtlTestCase.class.getName()); + private static final String TEST_CONFIG_FILE = "config.ini.junit"; @BeforeEach @@ -51,72 +58,59 @@ public void setUp() public void tearDown() { LocaleDependentTestCase.after(); + GracefulExit.registerExitFunction(System::exit); } /** * Run the test. * * @param character The PC - * @param mode The game mode + * @param mode The game mode * @throws IOException Signals that an I/O exception has occurred. */ public static void runTest(String character, String mode) throws IOException { - System.out.println("RUNTEST with the character: " + character - + " and the game mode: " + mode); + LOG.info("RUNTEST with the character: " + character + " and the game mode: " + mode); // Delete the old generated output for this test - File outputFolder = new File("code/testsuite/output"); + String characterFileName = character + ".xml"; + String characterPCFileName = character + ".pcg"; + var inputFolder = new File("code/testsuite/PCGfiles"); + var outputFolder = new File("code/testsuite/output"); + var csheetsFolder = new File("code/testsuite/csheets"); outputFolder.mkdirs(); - String outputFileName = character + ".xml"; - File outputFileFile = new File(outputFolder, outputFileName); + + var inputFile = new File(inputFolder, characterPCFileName); + var outputFile = new File(outputFolder, characterFileName); + var expectedFile = new File(csheetsFolder, characterFileName); String pccLoc = TestHelper.findDataFolder(); - // The String holder for the XML of the expected result - String expected; - // The String holder for the XML of the actual result - String actual; /* - * Override the pcc location, game mode and several other properties in - * the options.ini file + * Override the pcc location, game mode and several other properties in the options.ini file */ String configFolder = "testsuite"; - TestHelper.createDummySettingsFile(TEST_CONFIG_FILE, configFolder, - pccLoc); + TestHelper.createDummySettingsFile(TEST_CONFIG_FILE, configFolder, pccLoc); - // Fire off PCGen, which will produce an XML file - String characterFile = "code/testsuite/PCGfiles/" + character - + Constants.EXTENSION_CHARACTER_FILE; + GracefulExit.registerExitFunction((int status) -> + assertEquals(0, status, + MessageFormat.format("The export of {0} failed with an error: {1}.", character, status))); - String outputFile = outputFileFile.getCanonicalPath(); + Main.main("--character", inputFile.getCanonicalPath(), + "--exportsheet", "code/testsuite/base-xml.ftl", + "--outputfile", outputFile.getCanonicalPath(), + "--configfilename", TEST_CONFIG_FILE); - // The code below had to be commented out as in Java 21+ there's no more SecurityManager - // At time of writing there is no replacement, see JDK-8199704 - // The tests seem to pass regardless. - /* - Runnable revertSystemExitInterceptor = SystemExitInterceptor.startInterceptor(); - - assertEquals(0, - assertThrows(SystemExitInterceptor.SystemExitCalledException.class, - () -> Main.main("--character", characterFile, - "--exportsheet", "code/testsuite/base-xml.ftl", - "--outputfile", outputFile, - "--configfilename", TEST_CONFIG_FILE) - ).getStatusCode(), - "Export of " + character + " failed."); - - revertSystemExitInterceptor.run(); - */ - - // Read in the actual XML produced by PCGen - actual = Files.readString(outputFileFile.toPath()); - // Read in the expected XML - expected = Files.readString( - new File("code/testsuite/csheets/" + character + ".xml").toPath()); + // the XML of the expected result + var expected = Files.readString(expectedFile.toPath()); + // the XML of the actual result + var actual = Files.readString(outputFile.toPath()); + LOG.info(() -> MessageFormat.format("Comparing the expected ({0}) and actual ({1}) results", + expectedFile, outputFile)); Diff myDiff = DiffBuilder.compare(Input.fromString(expected)) - .withTest(Input.fromString(actual)).build(); + .withTest(Input.fromString(actual)) + .build(); - assertFalse(myDiff.hasDifferences(), myDiff.toString()); + assertFalse(myDiff.hasDifferences(), myDiff.fullDescription()); } } diff --git a/code/src/test/pcgen/util/TestHelper.java b/code/src/test/pcgen/util/TestHelper.java index a75801b32ea..222aef4fa35 100644 --- a/code/src/test/pcgen/util/TestHelper.java +++ b/code/src/test/pcgen/util/TestHelper.java @@ -81,7 +81,11 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.StringTokenizer; +import java.util.logging.Logger; +import java.util.logging.Level; /** * Helps Junit tests @@ -89,6 +93,8 @@ @SuppressWarnings("nls") public final class TestHelper { + private static final Logger LOG = Logger.getLogger(TestHelper.class.getName()); + private static boolean loaded = false; private static final LstObjectFileLoader eqLoader = new GenericLoader<>(Equipment.class); private static final LstObjectFileLoader abLoader = new AbilityLoader(); @@ -208,7 +214,7 @@ public static Object findField(final Class aClass, final String fieldName) } catch (SecurityException e) { - System.out.println(e); + LOG.log(Level.SEVERE, "SecurityException is thrown in findField", e); } return null; } @@ -460,29 +466,25 @@ public static boolean hasWeaponProfKeyed(PlayerCharacter pc, } /** - * Locate the data folder which contains the primary set of LST data. This - * defaults to the data folder under the current directory, but can be - * customised in the config.ini folder. + * Locate the data folder which contains the primary set of LST data. This defaults to the data folder under the + * current directory but can be customized in the config.ini folder. * @return The path of the data folder. */ public static String findDataFolder() { // Set the pcc location to "data" String pccLoc = "data"; - // Read in options.ini and override the pcc location if it exists - try (BufferedReader br = new BufferedReader(new InputStreamReader( - new FileInputStream("config.ini"), StandardCharsets.UTF_8))) + + // Read in config.ini and override the pcc location if it exists + try (var lines = Files.lines(Path.of("config.ini"))) { - while (br.ready()) - { - String line = br.readLine(); - if (line != null - && line.startsWith("pccFilesPath=")) - { - pccLoc = line.substring(13); - break; - } - } + var pccFilesPath = lines + .filter(line -> line.startsWith("pccFilesPath=")) + .map(line -> line.substring(13)) + .findFirst() + .orElse(pccLoc); + + return pccFilesPath; } catch (IOException e) { // Ignore, see method comment @@ -508,19 +510,18 @@ public static void createDummySettingsFile(String configFileName, bw.write("settingsPath=" + configFolder + "\r\n"); if (pccLoc != null) { - System.out.println("Using PCC Location of '" + pccLoc + "'."); + LOG.info("Using PCC Location of '" + pccLoc + "'."); bw.write("pccFilesPath=" + pccLoc + "\r\n"); } bw.write("customPath=testsuite\\\\customdata\r\n"); } - } public static void loadGameModes(String testConfigFile) { String configFolder = "testsuite"; String pccLoc = TestHelper.findDataFolder(); - System.out.println("Got data folder of " + pccLoc); + LOG.info("Got data folder of " + pccLoc); try { TestHelper.createDummySettingsFile(testConfigFile, configFolder, @@ -570,7 +571,7 @@ public static PCClass parsePCClassText(String classPCCText, String line = tok.nextToken(); if (!StringUtils.isBlank(line)) { - System.out.println("Processing line:'" + line + "'."); + LOG.info("Processing line:'" + line + "'."); reconstClass = pcClassLoader.parseLine(Globals.getContext(), reconstClass, line, source); diff --git a/code/src/test/plugin/PluginBuildTest.java b/code/src/test/plugin/PluginBuildTest.java index ac662b27405..05ef65a9c73 100644 --- a/code/src/test/plugin/PluginBuildTest.java +++ b/code/src/test/plugin/PluginBuildTest.java @@ -20,716 +20,183 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * {@code PluginBuildTest} verifies that the pluginbuild.xml file has all - * required data. As a result this unit test is a bit different in structure to - * a normal test. + * required data. As a result this unit test is a bit different in structure to + * a normal test. */ class PluginBuildTest { - /** - * Array of exceptions to normal names. Each entry is a pair of - * Java source file name and JAR file name. - */ - private String[][] exceptions; + private static final String JAVA_EXT = ".java"; + private static final String CLASS_EXT = ".class"; @BeforeEach void setUp() { - exceptions = new String[][]{ - {"EqBuilderSpell", "EqBuilder.Spell"}, - {"EqBuilderEqType", "EqBuilder.EqType"}, - {"GetVar", "Var"}, - {"HitdieLst", "Hitdie"}, - {"Casttime", "Casttimes"}, - {"PreVariable", "PreVar"} - }; } /** - * Check for the presence of all 'bonus' token plugins. - */ - @Test - public void testBonusPlugins() - { - String jarPrefix = "BonusToken-"; - File sourceFolder = new File("code/src/java/plugin/bonustokens"); - File jarFolder = new File("plugins/bonusplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all deprecated token plugins. + * Check for the presence of all Bonus token parsing plugins. */ @Test - public void testDeprecatedPlugins() + public void testBonusPlugins() throws IOException { - String jarPrefix = "^[a-zA-Z]*-DEPRECATED-"; - String sourceSuffix = "Lst"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/deprecated"); - File[] jarFolder = {new File("plugins/lstplugins"), new File("plugins/preplugins"), - new File("plugins/bonusplugins")}; - assertTrue( - sourceFolder.isDirectory(), - "Source folder " + sourceFolder.getAbsolutePath() + " should be a directory" - ); - String[] sources = sourceFolder.list(); - List srcList = new ArrayList<>(Arrays.asList(sources)); - srcList.remove("PreDefaultMonsterTester.java"); - srcList.remove("PreDefaultMonsterWriter.java"); - sources = srcList.toArray(sources); - String[][] exceptions = { - {"MoveBonus", "Move"} - }; - checkPluginJarsByRegex(jarPrefix, jarFolder, sourceSuffix, sources, exceptions); + Path sourceFolder = Paths.get("code/src/java/plugin/bonustokens"); + Path jar = Paths.get("plugins/bonusplugins.jar"); + checkPluginJar(jar, sourceFolder); } /** - * Check for the presence of all export token plugins. - */ - @Test - public void testExportPlugins() - { - String jarPrefix = "ExportToken-"; - File sourceFolder = new File("code/src/java/plugin/exporttokens"); - File jarFolder = new File("plugins/outputplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all JEP command plugins. - */ - @Test - public void testJepPlugins() - { - String jarPrefix = "JepCommand-"; - File sourceFolder = new File("code/src/java/plugin/jepcommands"); - File jarFolder = new File("plugins/jepplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder, "Command"); - } - - /** - * Check for the presence of all ability - * token parsing plugins. - */ - @Test - public void testLstAbilityPlugins() - { - String jarPrefix = "AbilityLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/ability"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all 'add' - * token parsing plugins. - */ - @Test - public void testLstAddPlugins() - { - String jarPrefix = "AddLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/add"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all 'automatic' - * token parsing plugins. - */ - @Test - public void testLstAutoPlugins() - { - String jarPrefix = "AutoLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/auto"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all campaign - * token parsing plugins. - */ - @Test - public void testLstCampaignPlugins() - { - String jarPrefix = "CampaignLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/campaign"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all campaign - * token parsing plugins. - */ - @Test - public void testLstInstallableCampaignPlugins() - { - String jarPrefix = "InstCampaignLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/campaign/installable"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all choose - * token parsing plugins. - */ - @Test - public void testLstChoosePlugins() - { - String jarPrefix = "ChooseToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/choose"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all choose - * token parsing plugins. - */ - @Test - public void testLstEqModChoosePlugins() - { - String jarPrefix = "EquipmentModifierChooseLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/equipmentmodifier/choose"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all class - * token parsing plugins. - */ - @Test - public void testLstClassPlugins() - { - String jarPrefix = "ClassLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/pcclass"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all class - * token parsing plugins. - */ - @Test - public void testLstClassLevelPlugins() - { - String jarPrefix = "ClassLevelLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/pcclass/level"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all companion mod - * token parsing plugins. - */ - @Test - public void testLstCompanionModPlugins() - { - String jarPrefix = "CompanionModLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/companionmod"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all deity - * token parsing plugins. - */ - @Test - public void testLstDietyPlugins() - { - String jarPrefix = "DeityLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/deity"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all domain - * token parsing plugins. - */ - @Test - public void testLstDomainPlugins() - { - String jarPrefix = "DomainLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/domain"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all equipment - * token parsing plugins. - */ - @Test - public void testLstEquipmentPlugins() - { - String jarPrefix = "EquipmentLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/equipment"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Check for the presence of all equipment modifier - * token parsing plugins. + * Check for the presence of all Converter plugins. */ @Test - public void testLstEquipmentModifierPlugins() + public void testConverterPlugins() throws IOException { - String jarPrefix = "EquipmentModifierLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/equipmentmodifier"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); + Path sourceFolder = Paths.get("code/src/java/plugin/converter"); + Path jar = Paths.get("plugins/converterplugins.jar"); + checkPluginJar(jar, sourceFolder); } - + /** - * Check for the presence of all global - * token parsing plugins. + * Check for the presence of all Export Token plugins. */ @Test - public void testLstGlobalPlugins() + public void testExportTokensPlugins() throws IOException { - String jarPrefix = "LstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder, "Lst"); + Path sourceFolder = Paths.get("code/src/java/plugin/exporttokens"); + Path jar = Paths.get("plugins/exportplugins.jar"); + checkPluginJar(jar, sourceFolder); } - - /** - * Check for the presence of all kit - * token parsing plugins. - */ - @Test - public void testLstKitPlugins() - { - String jarPrefix = "KitLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/kit"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "BaseKitLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/kit/basekit"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "KitAbilityLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/kit/ability"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "KitClassLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/kit/clazz"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "KitDeityLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/kit/deity"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "KitFundsLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/kit/funds"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "KitGearLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/kit/gear"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "KitLevelAbilityLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/kit/levelability"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - jarPrefix = "KitProfLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/kit/prof"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "KitSkillLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/kit/skill"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "KitSpellsLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/kit/spells"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "KitStartpackLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/kit/startpack"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "KitTableLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/kit/table"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - /** - * Check for the presence of all race - * token parsing plugins. + * Check for the presence of all Function plugins. */ @Test - public void testLstRacePlugins() + public void testFunctionPlugins() throws IOException { - String jarPrefix = "RaceLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/race"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); + Path sourceFolder = Paths.get("code/src/java/plugin/function"); + Path jar = Paths.get("plugins/functionplugins.jar"); + checkPluginJar(jar, sourceFolder); } - + /** - * Check for the presence of all skill - * token parsing plugins. + * Check for the presence of all Grouping plugins. */ @Test - public void testLstSkillPlugins() + public void testGroupingPlugins() throws IOException { - String jarPrefix = "SkillLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/skill"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); + Path sourceFolder = Paths.get("code/src/java/plugin/grouping"); + Path jar = Paths.get("plugins/groupingplugins.jar"); + checkPluginJar(jar, sourceFolder); } - + /** - * Check for the presence of all spell - * token parsing plugins. + * Check for the presence of all JEP Commands plugins. */ @Test - public void testLstSpellPlugins() + public void testJepCommandsPlugins() throws IOException { - String jarPrefix = "SpellLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/spell"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); + Path sourceFolder = Paths.get("code/src/java/plugin/jepcommands"); + Path jar = Paths.get("plugins/jepcommandsplugins.jar"); + checkPluginJar(jar, sourceFolder); } - + /** - * Check for the presence of all sub class - * token parsing plugins. + * Check for the presence of all LST token parsing plugins. */ @Test - public void testLstSubclassPlugins() + public void testLstPlugins() throws IOException { - String jarPrefix = "SubClassLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/subclass"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); + Path sourceFolder = Paths.get("code/src/java/plugin/lsttokens"); + Path jar = Paths.get("plugins/lstplugins.jar"); + checkPluginJar(jar, sourceFolder); } - + /** - * Check for the presence of all template - * token parsing plugins. + * Check for the presence of all Modifier plugins. */ @Test - public void testLstTemplatePlugins() + public void testModifierPlugins() throws IOException { - String jarPrefix = "TemplateLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/template"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); + Path sourceFolder = Paths.get("code/src/java/plugin/modifier"); + Path jar = Paths.get("plugins/modifierplugins.jar"); + checkPluginJar(jar, sourceFolder); } - + /** - * Check for the presence of all weapon proficiency - * token parsing plugins. + * Check for the presence of all PRE tokens plugins. */ @Test - public void testLstWeaponProfPlugins() + public void testPreTokensPlugins() throws IOException { - String jarPrefix = "WeaponProfLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/weaponprof"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); + Path sourceFolder = Paths.get("code/src/java/plugin/pretokens"); + Path jar = Paths.get("plugins/preplugins.jar"); + checkPluginJar(jar, sourceFolder); } - + /** - * Check for the presence of all prerequisite - * token parsing plugins. + * Check for the presence of all Primitive plugins. */ @Test - public void testPrePlugins() + public void testPrimitivePlugins() throws IOException { - String jarPrefix = "PreToken-"; - File sourceFolder = new File("code/src/java/plugin/pretokens/parser"); - File jarFolder = new File("plugins/preplugins"); - assertTrue( - //$NON-NLS-1$ - sourceFolder.isDirectory(), - "Source folder " //$NON-NLS-1$ - + sourceFolder.getAbsolutePath() - + " should be a directory" - ); - String[] sources = sourceFolder.list(); - List srcList = new ArrayList<>(Arrays.asList(sources)); - srcList.remove("PreSkillTotalParser.java"); - sources = srcList.toArray(sources); - checkPluginJars(jarPrefix, jarFolder, "Parser", sources); + Path sourceFolder = Paths.get("code/src/java/plugin/primitive"); + Path jar = Paths.get("plugins/primitiveplugins.jar"); + checkPluginJar(jar, sourceFolder); } - + /** - * Check for the presence of all variable - * token parsing plugins. + * Check for the presence of all Qualifier plugins. */ @Test - public void testLstVariablePlugins() + public void testQualifierPlugins() throws IOException { - String jarPrefix = "VariableLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/variable"); - File jarFolder = new File("plugins/lstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); + Path sourceFolder = Paths.get("code/src/java/plugin/qualifier"); + Path jar = Paths.get("plugins/qualifierplugins.jar"); + checkPluginJar(jar, sourceFolder); } - - /** - * Check for the presence of all system (gamemode, miscinfo etc) file - * token parsing plugins. - */ - @Test - public void testSystemLstPlugins() - { - String jarPrefix = "GameModeLstToken-"; - File sourceFolder = new File("code/src/java/plugin/lsttokens/gamemode"); - File jarFolder = new File("plugins/systemlstplugins"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "GameMode-BaseDiceLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/gamemode/basedice"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "GameMode-EqSizePenaltyToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/gamemode/eqsizepenalty"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "GameMode-MigrateToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/gamemode/migrate"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - jarPrefix = "GameMode-RollMethodToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/gamemode/rollmethod"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "GameMode-TabToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/gamemode/tab"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "GameMode-UnitSetToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/gamemode/unitset"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "GameMode-WieldCategory-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/gamemode/wieldcategory"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "GameMode-AbilityCategory-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/gamemode/abilitycategory"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "PointBuyLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/pointbuy"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "RuleCheckLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/rules"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "SizeAdjustmentLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/sizeadjustment"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "StatsAndChecksLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/statsandchecks"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "StatsAndChecks-AlignmentLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/statsandchecks/alignment"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "StatsAndChecks-BonusSpellLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/statsandchecks/bonusspell"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - -// jarPrefix = "StatsAndChecks-CheckLstToken-"; -// sourceFolder = new File("code/src/java/plugin/lsttokens/statsandchecks/check"); -// checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "StatsAndChecks-StatLstToken-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/statsandchecks/stat"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "Level-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/level"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "Eqslot-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/eqslot"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "Load-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/load"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - - jarPrefix = "Paper-"; - sourceFolder = new File("code/src/java/plugin/lsttokens/paper"); - checkPluginJars(jarPrefix, sourceFolder, jarFolder); - } - - /** - * Verify that all non-excluded java files are represented by a jar - * file of the same name. An exceptions list is used to track jars - * which are know to have an name varying from the standard. - * - * @param jarPrefix The text that the jar file name is expected to start with. - * @param sourceFolder The folder containing the source files. - * @param jarFolder The folder containing the JAR files. - */ - private void checkPluginJars(String jarPrefix, File sourceFolder, - File jarFolder) + private void checkPluginJar(Path jar, Path sourceFolder) throws IOException { - checkPluginJars(jarPrefix, sourceFolder, jarFolder, ""); - } + try (Stream stream = Files.walk(sourceFolder)) { + var javaPlugins = stream + .filter(Files::isRegularFile) + .map(Path::getFileName) + .map(Path::toString) + .filter(s -> s.endsWith(JAVA_EXT)) + .map(s -> s.substring(0, s.length() - JAVA_EXT.length())) + .collect(Collectors.toSet()); - /** - * Verify that all non-excluded java files are represented by a jar - * file of the same name. An exceptions list is used to track jars - * which are know to have an name varying from the standard. - * - * @param jarPrefix The text that the jar file name is expected to start with. - * @param sourceFolder The folder containing the source files. - * @param jarFolder The folder containing the JAR files. - * @param classSuffix The optional suffix on the class name that should be ignored. - */ - private void checkPluginJars(String jarPrefix, File sourceFolder, - File jarFolder, String classSuffix) - { - assertTrue( - //$NON-NLS-1$ - sourceFolder.isDirectory(), - "Source folder " //$NON-NLS-1$ - + sourceFolder.getAbsolutePath() - + " should be a directory" - ); - String[] sources = sourceFolder.list(); - checkPluginJars(jarPrefix, jarFolder, classSuffix, sources); - } - - /** - * Verify that all non-excluded java files are represented by a jar - * file of the same name. An exceptions list is used to track jars - * which are know to have an name varying from the standard. - * - * @param jarPrefix The text that the jar file name is expected to start with. - * @param jarFolder The folder containing the JAR files. - * @param classSuffix The optional suffix on the class name that should be ignored. - * @param sources The array of names of java source files. - */ - private void checkPluginJars(String jarPrefix, - File jarFolder, String classSuffix, String[] sources) - { - assertTrue( - jarFolder.isDirectory(), - "Jar folder " + jarFolder.getAbsolutePath() + " should be a directory" - ); - Collection jarSet; - String[] jars = jarFolder.list(); - jarSet = Arrays.stream(jars) - .filter(jar -> jar.startsWith(jarPrefix)) - .map(String::toLowerCase) - .collect(Collectors.toSet()); - for (String source : sources) - { - if ((source != null) && source.endsWith(".java")) - { - //String testString = jarPrefix + sources[i].substring(0, sources[i].length()-5); - String testString = source; - testString = testString.replaceAll(".java", ""); - testString = testString.replaceAll("Token", ""); - if (!classSuffix.isEmpty()) - { - testString = testString.replaceAll(classSuffix, ""); - } - for (String[] exception : exceptions) - { - testString = testString.replaceAll(exception[0], exception[1]); - } + try (JarFile jarFile = new JarFile(jar.toFile())) { + var res = jarFile.stream() + .map(JarEntry::getRealName) + .filter(s -> s.endsWith(CLASS_EXT)) + .map(s -> s.substring(s.lastIndexOf('/') + 1)) // trim everything before the last '/' + .map(s -> s.substring(0, s.length() - CLASS_EXT.length())) // trim the class extension + .collect(Collectors.toSet()); - testString = jarPrefix + testString + ".jar"; - testString = testString.toLowerCase(); - assertTrue( - jarSet.contains(testString), - "Jar for " + source - + " should be present in jars list as " + testString - ); - } - } - } - - /** - * Verify that all non-excluded java files are represented by a jar - * file of the same name. An exceptions list is used to track jars - * which are know to have an name varying from the standard. - * - * @param jarRegexPrefix The regex text that the jar file name is expected to start with. - * @param jarFolder The folder containing the JAR files. - * @param classSuffix The optional suffix on the class name that should be ignored. - * @param sources The array of names of java source files. - * @param exceptions The list of known exceptions to the naming standards. - */ - private static void checkPluginJarsByRegex(String jarRegexPrefix, - File[] jarFolder, String classSuffix, String[] sources, - String[][] exceptions) - { - for (File folder : jarFolder) - { - assertTrue( - folder.isDirectory(), - "Jar folder " + folder.getAbsolutePath() + " should be a directory" - ); - } - Collection jarSet = new HashSet<>(); - for (File folder : jarFolder) - { - String[] jars = folder.list(); - String jarRegexPattern = jarRegexPrefix + ".*"; - Arrays.stream(jars) - .filter(jar -> jar.matches(jarRegexPattern)) - .map(jar -> jar.replaceFirst(jarRegexPrefix, "")) - .map(String::toLowerCase) - .forEach(jarSet::add); - } - for (String source : sources) - { - if ((source != null) && source.endsWith(".java")) - { - //String testString = jarPrefix + sources[i].substring(0, sources[i].length()-5); - String testString = source; - testString = testString.replaceAll(".java", ""); - testString = testString.replaceAll("Token", ""); - if (!classSuffix.isEmpty()) - { - testString = testString.replaceAll(classSuffix, ""); - } - for (String[] exception : exceptions) - { - testString = testString.replaceAll(exception[0], exception[1]); - } + javaPlugins.removeAll(res); - testString += ".jar"; - testString = testString.toLowerCase(); - assertTrue( - jarSet.contains(testString), - "Jar for " + source + " should be present in jars list" - ); + assertTrue(javaPlugins.isEmpty(), "All java plugins should be represented in jar " + jar + " but the following were not: " + javaPlugins); } } } - } diff --git a/code/src/testResources/pcgen/lang/cleaned.properties b/code/src/testResources/pcgen/lang/cleaned.properties index b8405da5cf5..b44578e4ee8 100644 --- a/code/src/testResources/pcgen/lang/cleaned.properties +++ b/code/src/testResources/pcgen/lang/cleaned.properties @@ -307,6 +307,7 @@ in_loadPartyNoSources=Could not find source files for party: {0} in_err_browser_err=Couldn't launch browser in_err_browser_uri=Unable to view {0} +in_err_remote_lst_warn=Preferences are currently set to NOT allow\nloading of sources from web links.\n{0} is a web link. #About dialog diff --git a/code/src/testcommon/util/GracefulExitTest.java b/code/src/testcommon/util/GracefulExitTest.java new file mode 100644 index 00000000000..c08515d4cd9 --- /dev/null +++ b/code/src/testcommon/util/GracefulExitTest.java @@ -0,0 +1,125 @@ +package util; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import pcgen.util.ExitFunction; +import pcgen.util.ExitInterceptor; +import pcgen.util.GracefulExit; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class GracefulExitTest +{ + public static final ExitFunction EXIT_FUNCTION_WITH_EXCEPTION = status -> { + throw new RuntimeException("Test exception"); + }; + + public static final ExitInterceptor EMPTY_EXIT_INTERCEPTOR = status -> true; + + @BeforeEach + void setUp() + { + GracefulExit.registerExitFunction(status -> { /* do nothing */ }); + GracefulExit.clearExitInterceptors(); + } + + @Test + void testDefaultExit() + { + GracefulExit.registerExitFunction(status -> { + assertEquals(23, status, "Status code should be 23"); + }); + GracefulExit.exit(23); + } + + @Test + void testExitWithThrownException() + { + GracefulExit.registerExitFunction(EXIT_FUNCTION_WITH_EXCEPTION); + assertThrows(RuntimeException.class, () -> GracefulExit.exit(0), "Should throw an exception"); + } + + @Test + void testNoExitWithExceptionThrown() + { + GracefulExit.registerExitFunction(EXIT_FUNCTION_WITH_EXCEPTION); + GracefulExit.addExitInterceptor(status -> false); + assertDoesNotThrow(() -> GracefulExit.exit(0), + "Should not throw an exception because the exit was intercepted"); + } + + @Test + void testUniqueInterceptor() + { + ExitInterceptor customInterceptor = status -> false; + GracefulExit.addExitInterceptor(customInterceptor); + + assertThrows(IllegalArgumentException.class, () -> { + GracefulExit.addExitInterceptor(customInterceptor); + }, "Should throw an exception because the interceptor is already registered"); + } + + @Test + void testAddHookDuringExit() + { + GracefulExit.registerExitFunction(status -> { + }); + + GracefulExit.addExitInterceptor(status -> { + assertThrows(IllegalStateException.class, () -> { + GracefulExit.addExitInterceptor(EMPTY_EXIT_INTERCEPTOR); + }, "Should throw an exception because the interceptor is added during exit"); + return true; + }); + GracefulExit.exit(0); + } + + @Test + void testAddExitDuringExit() + { + GracefulExit.registerExitFunction(status -> { + assertThrows(IllegalStateException.class, () -> { + GracefulExit.registerExitFunction(EXIT_FUNCTION_WITH_EXCEPTION); + }, "Should throw an exception because the exit function is added during exit"); + }); + GracefulExit.exit(0); + } + + @Test + void testClearHooksDuringExit() + { + GracefulExit.registerExitFunction(status -> { + assertThrows(IllegalStateException.class, GracefulExit::clearExitInterceptors, + "Should throw an exception because the interceptors are cleared during exit"); + }); + GracefulExit.exit(0); + } + + @Test + void testExitHookSequence() + { + AtomicInteger count = new AtomicInteger(0); + + GracefulExit.addExitInterceptor(status -> { + assertEquals(0, count.get(), "The first interceptor should be called first"); + count.incrementAndGet(); + return true; + }); + GracefulExit.addExitInterceptor(status -> { + assertEquals(1, count.get(), "The second interceptor should be called next"); + count.incrementAndGet(); + return true; + }); + GracefulExit.addExitInterceptor(status -> { + assertEquals(2, count.get(), "The last interceptor should be called next"); + count.incrementAndGet(); + return true; + }); + GracefulExit.exit(0); + assertEquals(3, count.get(), "All interceptors should have been called"); + } +} diff --git a/code/src/testcommon/util/SystemExitInterceptor.java b/code/src/testcommon/util/SystemExitInterceptor.java deleted file mode 100644 index d54a5c461b4..00000000000 --- a/code/src/testcommon/util/SystemExitInterceptor.java +++ /dev/null @@ -1,57 +0,0 @@ -package util; - -import java.security.Permission; - -/** - * Intercepts calls to System.exit and throws an exception instead. Otherwise, these calls would terminate the Unit Test runner. - * That exception can be tested for inside unit tests. - *

- * Note: A replacement API to intercept System.exit is in discussion in JDK-8199704. - * The SecurityManager is unlikely to be removed from Java before such an alternative exists. - */ -public class SystemExitInterceptor -{ - - /** - * Temporarily replaces the security manager in order to intercept calls to System.exit. - * - * @return A runnable that needs to be called in order to revert the change. - */ - @SuppressWarnings("removal") - public static Runnable startInterceptor() - { - SecurityManager previousSecurityManager = System.getSecurityManager(); - System.setSecurityManager(new SecurityManager() - { - @Override - public void checkExit(int status) - { - throw new SystemExitCalledException(status); - } - - @Override - public void checkPermission(Permission perm) - { - // Allow other activities by default - } - }); - - return () -> System.setSecurityManager(previousSecurityManager); - } - - public static class SystemExitCalledException extends SecurityException - { - private final int statusCode; - - public SystemExitCalledException(int statusCode) - { - super("System.exit called with status code " + statusCode); - this.statusCode = statusCode; - } - - public int getStatusCode() - { - return statusCode; - } - } -} diff --git a/code/src/utest/pcgen/persistence/lst/URIFactoryTest.java b/code/src/utest/pcgen/persistence/lst/URIFactoryTest.java new file mode 100644 index 00000000000..4f24fddf073 --- /dev/null +++ b/code/src/utest/pcgen/persistence/lst/URIFactoryTest.java @@ -0,0 +1,127 @@ +package pcgen.persistence.lst; + +import org.junit.jupiter.api.Test; +import pcgen.system.ConfigurationSettings; +import pcgen.system.PCGenSettings; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class URIFactoryTest +{ + @Test + void testGetURI_WithValidRootAndOffset() throws URISyntaxException + { + URI rootURI = new URI("file://example/root/"); + String offset = "data/file.txt"; + URIFactory uriFactory = new URIFactory(rootURI, offset); + + URI expected = new URI("file:" + System.getProperty("user.dir") + "/data/file.txt"); + URI result = uriFactory.getURI(); + + assertEquals(expected, result); + } + + @Test + void testGetURI_WithOffsetStartingWithAtSymbol() throws URISyntaxException + { + URI rootURI = new URI("file:/example/root/"); + String offset = "@subdir/file.txt"; + URIFactory uriFactory = new URIFactory(rootURI, offset); + + File expectedFile = new File(ConfigurationSettings.getPccFilesDir(), "subdir/file.txt"); + URI result = uriFactory.getURI(); + + assertEquals(expectedFile.toURI(), result); + } + + @Test + void testGetURI_WithOffsetStartingWithAmpersand() throws URISyntaxException + { + URI rootURI = new URI("file:/example/root/"); + String offset = "&subdir/file.txt"; + URIFactory uriFactory = new URIFactory(rootURI, offset); + + File expectedFile = new File(PCGenSettings.getVendorDataDir(), "subdir/file.txt"); + URI result = uriFactory.getURI(); + + assertEquals(expectedFile.toURI(), result); + } + + @Test + void testGetURI_WithOffsetStartingWithDollarSign() throws URISyntaxException + { + URI rootURI = new URI("file:/example/root/"); + String offset = "$subdir/file.txt"; + URIFactory uriFactory = new URIFactory(rootURI, offset); + + File expectedFile = new File(PCGenSettings.getHomebrewDataDir(), "subdir/file.txt"); + URI result = uriFactory.getURI(); + + assertEquals(expectedFile.toURI(), result); + } + + @Test + void testGetURI_WithOffsetStartingWithAsterisk() throws URISyntaxException + { + URI rootURI = new URI("file:/example/root/"); + String offset = "*subdir/file.txt"; + URIFactory uriFactory = new URIFactory(rootURI, offset); + + File homebrewFile = new File(PCGenSettings.getHomebrewDataDir(), "subdir/file.txt"); + File vendorFile = new File(PCGenSettings.getVendorDataDir(), "subdir/file.txt"); + + URI result = uriFactory.getURI(); + + if (homebrewFile.exists()) + { + assertEquals(homebrewFile.toURI(), result); + } else if (vendorFile.exists()) + { + assertEquals(vendorFile.toURI(), result); + } else + { + assertEquals(new File(ConfigurationSettings.getPccFilesDir(), "subdir/file.txt").toURI(), result); + } + } + + @Test + void testGetURI_WithEmptyOffset() throws URISyntaxException + { + URI rootURI = new URI("file:/example/root/"); + String offset = ""; + + assertThrows(IllegalArgumentException.class, () -> { + new URIFactory(rootURI, offset).getURI(); + }); + } + + @Test + void testGetURI_WithInvalidURI() throws URISyntaxException + { + URI rootURI = new URI("file:/example/root/"); + String offset = "http://:@/\\invalid/uri"; + URIFactory uriFactory = new URIFactory(rootURI, offset); + + URI result = uriFactory.getURI(); + + assertEquals(URIFactory.FAILED_URI, result); + } + + @Test + void testGetURI_WithOffsetContainingProtocol() throws URISyntaxException + { + URI rootURI = new URI("file:/example/root/"); + String offset = "http://example.com/data/file.txt"; + URIFactory uriFactory = new URIFactory(rootURI, offset); + + URI result = uriFactory.getURI(); + + URI expected = new URI("http", "example.com", "/data/file.txt", null); + assertEquals(expected, result); + } +} diff --git a/code/src/utest/pcgen/system/CommandLineArgumentsTest.java b/code/src/utest/pcgen/system/CommandLineArgumentsTest.java index ef1cabecf51..438aa12c8b8 100644 --- a/code/src/utest/pcgen/system/CommandLineArgumentsTest.java +++ b/code/src/utest/pcgen/system/CommandLineArgumentsTest.java @@ -1,351 +1,421 @@ package pcgen.system; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.converter.ArgumentConversionException; import org.junit.jupiter.params.converter.ConvertWith; import org.junit.jupiter.params.converter.TypedArgumentConverter; import org.junit.jupiter.params.provider.EmptySource; import org.junit.jupiter.params.provider.ValueSource; -import util.SystemExitInterceptor; +import pcgen.util.GracefulExit; import java.io.File; +import java.text.MessageFormat; import java.util.Optional; -@Disabled -class CommandLineArgumentsTest { - private static Runnable revertSystemExitInterceptor; - - /** - * We temporarily replace the security manager in order to - * intercept calls to System.exit from the argparse4j library. - * Otherwise, these calls would terminate the Unit Test runner. - */ - @BeforeAll - static void initialize() { - revertSystemExitInterceptor = SystemExitInterceptor.startInterceptor(); - } - - @AfterAll - static void cleanup() { - revertSystemExitInterceptor.run(); - } - - private CommandLineArguments from(String[] args) { - return new CommandLineArguments(args); - } - - public static class CSVtoArrayConverter extends TypedArgumentConverter { - protected CSVtoArrayConverter() { - super(String.class, String[].class); - } - - @Override - protected String[] convert(String s) throws ArgumentConversionException { - if (s == null) { - return null; - } - - return s.split("\\s*,\\s*"); - } - } - - @ParameterizedTest - @EmptySource - void noArgs(String... args) { - CommandLineArguments classUnderTest = from(args); - Assertions.assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getExportSheet()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getOutputFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getPartyFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); - Assertions.assertFalse(classUnderTest.isStartNameGenerator()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getTab()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getTab()); - Assertions.assertFalse(classUnderTest.isVerbose()); - } - - @Nested - class CharacterTest { - @ParameterizedTest - @ValueSource(strings = {"-c,characters/Sorcerer.pcg", "--character,characters/Sorcerer.pcg"}) - void validUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - CommandLineArguments classUnderTest = from(args); - - Assertions.assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); - Assertions.assertEquals(Optional.of(new File("characters/Sorcerer.pcg")), classUnderTest.getCharacterFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getExportSheet()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getOutputFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getPartyFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); - Assertions.assertFalse(classUnderTest.isStartNameGenerator()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getTab()); - Assertions.assertFalse(classUnderTest.isVerbose()); - } - - @ParameterizedTest - @ValueSource(strings = {"-c,file/that/doesn't/exist", "-c,", "-c", "-c,characters/Sorcerer.pcg,characters/Everything.pcg", - "--character,file/that/doesn't/exist", "--character,", "--character", "--character,characters/Sorcerer.pcg,characters/Everything.pcg"}) - void invalidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - Assertions.assertThrows(SystemExitInterceptor.SystemExitCalledException.class, - () -> from(args)); - } - } - - @Nested - class VerboseTest { - @ParameterizedTest - @ValueSource(strings = {"-v", "-vv"}) - void validUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - CommandLineArguments classUnderTest = from(args); - - Assertions.assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getExportSheet()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getOutputFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getPartyFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); - Assertions.assertFalse(classUnderTest.isStartNameGenerator()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getTab()); - Assertions.assertTrue(classUnderTest.isVerbose()); - } - } - - @Nested - class SettingsDirTest { - @ParameterizedTest - @ValueSource(strings = {"-s,characters", "--settingsdir,characters"}) - void validUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - CommandLineArguments classUnderTest = from(args); - - Assertions.assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getExportSheet()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getOutputFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getPartyFile()); - Assertions.assertEquals(Optional.of(new File("characters")), classUnderTest.getSettingsDir()); - Assertions.assertFalse(classUnderTest.isStartNameGenerator()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getTab()); - Assertions.assertFalse(classUnderTest.isVerbose()); - } - - @ParameterizedTest - @ValueSource(strings = {"-s,folder/that/doesn't/exist", "-s,", "-s", "-s,characters,data", "-s,characters/Sorcerer.pcg", - "--settingsdir,folder/that/doesn't/exist", "--settingsdir,", "--settingsdir", "--settingsdir,characters,data"}) - void invalidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - Assertions.assertThrows(SystemExitInterceptor.SystemExitCalledException.class, - () -> from(args)); - } - } - - - @Nested - class ConfigFileNameTest { - @ParameterizedTest - @ValueSource(strings = {"-S,testname.ini", "--configfilename,testname.ini"}) - void validUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - CommandLineArguments classUnderTest = from(args); - - Assertions.assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getExportSheet()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getOutputFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getPartyFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); - Assertions.assertEquals(Optional.of("testname.ini"), classUnderTest.getConfigFileName()); - - Assertions.assertFalse(classUnderTest.isStartNameGenerator()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getTab()); - Assertions.assertFalse(classUnderTest.isVerbose()); - } - - @ParameterizedTest - @ValueSource(strings = {"-S,", "-S", "-S,too,many", - "--configfilename,", "--configfilename", "--configfilename,too,many"}) - void invalidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - Assertions.assertThrows(SystemExitInterceptor.SystemExitCalledException.class, - () -> from(args)); - } - } - - @Nested - class CampaignModeTest { - @ParameterizedTest - @ValueSource(strings = {"-m,testCampaign", "--campaignmode,testCampaign"}) - void validUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - CommandLineArguments classUnderTest = from(args); - - Assertions.assertEquals(Optional.of("testCampaign"), classUnderTest.getCampaignMode()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getExportSheet()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getOutputFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getPartyFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); - Assertions.assertFalse(classUnderTest.isStartNameGenerator()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getTab()); - Assertions.assertFalse(classUnderTest.isVerbose()); - } - - @ParameterizedTest - @ValueSource(strings = {"-m,", "-m", "-m,too,many", - "--campaignmode,", "--campaignmode", "--campaignmode,too,many"}) - void invalidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - Assertions.assertThrows(SystemExitInterceptor.SystemExitCalledException.class, - () -> from(args)); - } - } - - @Nested - class TabTest { - @ParameterizedTest - @ValueSource(strings = {"-D,test", "--tab,test"}) - void validUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - CommandLineArguments classUnderTest = from(args); - - Assertions.assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getExportSheet()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getOutputFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getPartyFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); - Assertions.assertFalse(classUnderTest.isStartNameGenerator()); - Assertions.assertEquals(Optional.of("test"), classUnderTest.getTab()); - Assertions.assertFalse(classUnderTest.isVerbose()); - } - - @ParameterizedTest - @ValueSource(strings = {"-D,", "-D", "-D,too,many", - "--tab,", "--tab", "--tab,too,many"}) - void invalidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - Assertions.assertThrows(SystemExitInterceptor.SystemExitCalledException.class, - () -> from(args)); - } - } - - - @Nested - class ExportSheetTest { - @ParameterizedTest - @ValueSource(strings = {"-E,characters/Sorcerer.pcg", "--exportsheet,characters/Sorcerer.pcg"}) - void validUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - CommandLineArguments classUnderTest = from(args); - - Assertions.assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); - Assertions.assertEquals(Optional.of(new File("characters/Sorcerer.pcg")), classUnderTest.getExportSheet()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getOutputFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getPartyFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); - Assertions.assertFalse(classUnderTest.isStartNameGenerator()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getTab()); - Assertions.assertFalse(classUnderTest.isVerbose()); - } - - @ParameterizedTest - @ValueSource(strings = {"-E,file/that/doesn't/exist", "-E,", "-E", "-E,characters/Sorcerer.pcg,characters/Everything.pcg", - "--exportsheet,file/that/doesn't/exist", "--exportsheet,", "--exportsheet", "--exportsheet,characters/Sorcerer.pcg,characters/Everything.pcg"}) - void testillegalUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - Assertions.assertThrows(SystemExitInterceptor.SystemExitCalledException.class, - () -> from(args)); - } - } - - @Nested - class PartyTest { - @ParameterizedTest - @ValueSource(strings = {"-p,characters/Sorcerer.pcg", "--party,characters/Sorcerer.pcg"}) - void validUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - CommandLineArguments classUnderTest = from(args); - - Assertions.assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getExportSheet()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getOutputFile()); - Assertions.assertEquals(Optional.of(new File("characters/Sorcerer.pcg")), classUnderTest.getPartyFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); - Assertions.assertFalse(classUnderTest.isStartNameGenerator()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getTab()); - Assertions.assertFalse(classUnderTest.isVerbose()); - } - - @ParameterizedTest - @ValueSource(strings = {"-c,file/that/doesn't/exist", "-c,", "-c", "-c,characters/Sorcerer.pcg,characters/Everything.pcg", - "--character,file/that/doesn't/exist", "--character,", "--character", "--character,characters/Sorcerer.pcg,characters/Everything.pcg"}) - void invalidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - Assertions.assertThrows(SystemExitInterceptor.SystemExitCalledException.class, - () -> from(args)); - } - } - - @Nested - class OutputFileTest { - @ParameterizedTest - @ValueSource(strings = {"-o,characters/nonexisting-file", "--outputfile,characters/nonexisting-file"}) - void validNonexistingUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - CommandLineArguments classUnderTest = from(args); - - Assertions.assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getExportSheet()); - Assertions.assertEquals(Optional.of(new File("characters/nonexisting-file")), classUnderTest.getOutputFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getPartyFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); - Assertions.assertFalse(classUnderTest.isStartNameGenerator()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getTab()); - Assertions.assertFalse(classUnderTest.isVerbose()); - } - - @ParameterizedTest - @ValueSource(strings = {"-o,characters/Sorcerer.pcg", "--outputfile,characters/Sorcerer.pcg"}) - void validExistingUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - CommandLineArguments classUnderTest = from(args); - - Assertions.assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getExportSheet()); - Assertions.assertEquals(Optional.of(new File("characters/Sorcerer.pcg")), classUnderTest.getOutputFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getPartyFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); - Assertions.assertFalse(classUnderTest.isStartNameGenerator()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getTab()); - Assertions.assertFalse(classUnderTest.isVerbose()); - } - - @ParameterizedTest - @ValueSource(strings = {"-o,", "-o", "-o,more/than,one/file", - "--outputfile,", "--outputfile", "--outputfile,more/than,one/file"}) - void invalidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - Assertions.assertThrows(SystemExitInterceptor.SystemExitCalledException.class, - () -> from(args)); - } - } - - @Nested - class NameGeneratorTest { - @ParameterizedTest - @ValueSource(strings = {"--name-generator"}) - void validUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) { - CommandLineArguments classUnderTest = from(args); - - Assertions.assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getExportSheet()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getOutputFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getPartyFile()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); - Assertions.assertTrue(classUnderTest.isStartNameGenerator()); - Assertions.assertEquals(Optional.empty(), classUnderTest.getTab()); - Assertions.assertFalse(classUnderTest.isVerbose()); - } - } +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SuppressWarnings("squid:S2698") // Don't show the warning in SonarQube related to a missing assertion text message +class CommandLineArgumentsTest +{ + /** + * We temporarily replace the interceptors to prevent the JVM from exiting. The exception is thrown to prevent + * the system from executing the code after the command line arguments were parsed with errors. + */ + @BeforeEach + void initialize() + { + GracefulExit.registerExitFunction((int status) -> { + assertEquals(1, status, + MessageFormat.format("The command line parsing must always fail. But the current status is {0}.", + status)); + throw new IllegalStateException("The test execution is aborted intentionally."); + }); + } + + /** + * We revert the changes to the interceptors to allow the JVM to exit normally. + */ + @BeforeEach + void cleanup() + { + GracefulExit.registerExitFunction(System::exit); + } + + /** + * Helper method to create a CommandLineArguments object from the given arguments. + * @param args The arguments to parse as an array of strings (can be taken from {@link Main#main(String...)}) + * @return The {@link CommandLineArguments} object + */ + private CommandLineArguments from(String[] args) + { + return new CommandLineArguments(args); + } + + public static class CSVtoArrayConverter extends TypedArgumentConverter + { + protected CSVtoArrayConverter() + { + super(String.class, String[].class); + } + + @Override + protected String[] convert(String s) throws ArgumentConversionException + { + return s.split("\\s*,\\s*"); + } + } + + @ParameterizedTest + @EmptySource + void noArgs(String... args) + { + CommandLineArguments classUnderTest = from(args); + assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); + assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); + assertEquals(Optional.empty(), classUnderTest.getExportSheet()); + assertEquals(Optional.empty(), classUnderTest.getOutputFile()); + assertEquals(Optional.empty(), classUnderTest.getPartyFile()); + assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); + assertFalse(classUnderTest.isStartNameGenerator()); + assertEquals(Optional.empty(), classUnderTest.getTab()); + assertEquals(Optional.empty(), classUnderTest.getTab()); + assertFalse(classUnderTest.isVerbose()); + } + + @ParameterizedTest + @ValueSource(strings = {"-h", "--help"}) + void testHelpArgs(@ConvertWith(CSVtoArrayConverter.class) String... args) { + GracefulExit.registerExitFunction((int status) -> { + assertEquals(0, status, + MessageFormat.format("The command line parsing must be successful because help command arguments are parsed. But the current status is {0}.", + status)); + throw new IllegalStateException("The test execution is aborted intentionally."); + }); + + assertThrows(IllegalStateException.class, () -> from(args), + "The help arguments were parsed successfully. The test must be interrupted intentionally."); + } + + @Nested + class CharacterTest + { + @ParameterizedTest + @ValueSource(strings = {"-c,characters/Sorcerer.pcg", "--character,characters/Sorcerer.pcg"}) + void testValidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + CommandLineArguments classUnderTest = from(args); + + assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); + assertEquals(Optional.of(new File("characters/Sorcerer.pcg")), classUnderTest.getCharacterFile()); + assertEquals(Optional.empty(), classUnderTest.getExportSheet()); + assertEquals(Optional.empty(), classUnderTest.getOutputFile()); + assertEquals(Optional.empty(), classUnderTest.getPartyFile()); + assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); + assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); + assertFalse(classUnderTest.isStartNameGenerator()); + assertEquals(Optional.empty(), classUnderTest.getTab()); + assertFalse(classUnderTest.isVerbose()); + } + + @ParameterizedTest + @ValueSource(strings = {"-c,file/that/doesn't/exist", "-c,", "-c", + "-c,characters/Sorcerer.pcg,characters/Everything.pcg", + "--character,file/that/doesn't/exist", "--character,", "--character", + "--character,characters/Sorcerer.pcg,characters/Everything.pcg"}) + void testInvalidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + assertThrows(IllegalStateException.class, () -> from(args), + "The character file does not exist. The parsing must fail."); + } + } + + @Nested + class VerboseTest + { + @ParameterizedTest + @ValueSource(strings = {"-v", "-vv"}) + void testValidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + CommandLineArguments classUnderTest = from(args); + + assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); + assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); + assertEquals(Optional.empty(), classUnderTest.getExportSheet()); + assertEquals(Optional.empty(), classUnderTest.getOutputFile()); + assertEquals(Optional.empty(), classUnderTest.getPartyFile()); + assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); + assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); + assertFalse(classUnderTest.isStartNameGenerator()); + assertEquals(Optional.empty(), classUnderTest.getTab()); + assertTrue(classUnderTest.isVerbose()); + } + } + + @Nested + class SettingsDirTest + { + @ParameterizedTest + @ValueSource(strings = {"-s,characters", "--settingsdir,characters"}) + void testValidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + CommandLineArguments classUnderTest = from(args); + + assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); + assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); + assertEquals(Optional.empty(), classUnderTest.getExportSheet()); + assertEquals(Optional.empty(), classUnderTest.getOutputFile()); + assertEquals(Optional.empty(), classUnderTest.getPartyFile()); + assertEquals(Optional.of(new File("characters")), classUnderTest.getSettingsDir()); + assertFalse(classUnderTest.isStartNameGenerator()); + assertEquals(Optional.empty(), classUnderTest.getTab()); + assertFalse(classUnderTest.isVerbose()); + } + + @ParameterizedTest + @ValueSource(strings = {"-s,folder/that/doesn't/exist", "-s,", "-s", "-s,characters,data", + "-s,characters/Sorcerer.pcg", + "--settingsdir,folder/that/doesn't/exist", "--settingsdir,", "--settingsdir", + "--settingsdir,characters,data"}) + void testInvalidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + assertThrows(IllegalStateException.class, () -> from(args), + "The settings directory not exist. The parsing must fail."); + } + } + + @Nested + class ConfigFileNameTest + { + @ParameterizedTest + @ValueSource(strings = {"-S,testname.ini", "--configfilename,testname.ini"}) + void testValidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + CommandLineArguments classUnderTest = from(args); + + assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); + assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); + assertEquals(Optional.empty(), classUnderTest.getExportSheet()); + assertEquals(Optional.empty(), classUnderTest.getOutputFile()); + assertEquals(Optional.empty(), classUnderTest.getPartyFile()); + assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); + assertEquals(Optional.of("testname.ini"), classUnderTest.getConfigFileName()); + + assertFalse(classUnderTest.isStartNameGenerator()); + assertEquals(Optional.empty(), classUnderTest.getTab()); + assertFalse(classUnderTest.isVerbose()); + } + + @ParameterizedTest + @ValueSource(strings = {"-S,", "-S", "-S,too,many", + "--configfilename,", "--configfilename", "--configfilename,too,many"}) + void testInvalidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + assertThrows(IllegalStateException.class, () -> from(args), + "The config file name is wrong. The parsing must fail."); + } + } + + @Nested + class CampaignModeTest + { + @ParameterizedTest + @ValueSource(strings = {"-m,testCampaign", "--campaignmode,testCampaign"}) + void testValidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + CommandLineArguments classUnderTest = from(args); + + assertEquals(Optional.of("testCampaign"), classUnderTest.getCampaignMode()); + assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); + assertEquals(Optional.empty(), classUnderTest.getExportSheet()); + assertEquals(Optional.empty(), classUnderTest.getOutputFile()); + assertEquals(Optional.empty(), classUnderTest.getPartyFile()); + assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); + assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); + assertFalse(classUnderTest.isStartNameGenerator()); + assertEquals(Optional.empty(), classUnderTest.getTab()); + assertFalse(classUnderTest.isVerbose()); + } + + @ParameterizedTest + @ValueSource(strings = {"-m,", "-m", "-m,too,many", + "--campaignmode,", "--campaignmode", "--campaignmode,too,many"}) + void testInvalidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + assertThrows(IllegalStateException.class, () -> from(args), + "The campaign mode is wrong. The parsing must fail."); + } + } + + @Nested + class TabTest + { + @ParameterizedTest + @ValueSource(strings = {"-D,test", "--tab,test"}) + void testValidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + CommandLineArguments classUnderTest = from(args); + + assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); + assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); + assertEquals(Optional.empty(), classUnderTest.getExportSheet()); + assertEquals(Optional.empty(), classUnderTest.getOutputFile()); + assertEquals(Optional.empty(), classUnderTest.getPartyFile()); + assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); + assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); + assertFalse(classUnderTest.isStartNameGenerator()); + assertEquals(Optional.of("test"), classUnderTest.getTab()); + assertFalse(classUnderTest.isVerbose()); + } + + @ParameterizedTest + @ValueSource(strings = {"-D,", "-D", "-D,too,many", + "--tab,", "--tab", "--tab,too,many"}) + void testInvalidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + assertThrows(IllegalStateException.class, () -> from(args), + "The tab is wrong. The parsing must fail."); + } + } + + @Nested + class ExportSheetTest + { + @ParameterizedTest + @ValueSource(strings = {"-E,characters/Sorcerer.pcg", "--exportsheet,characters/Sorcerer.pcg"}) + void testValidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + CommandLineArguments classUnderTest = from(args); + + assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); + assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); + assertEquals(Optional.of(new File("characters/Sorcerer.pcg")), classUnderTest.getExportSheet()); + assertEquals(Optional.empty(), classUnderTest.getOutputFile()); + assertEquals(Optional.empty(), classUnderTest.getPartyFile()); + assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); + assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); + assertFalse(classUnderTest.isStartNameGenerator()); + assertEquals(Optional.empty(), classUnderTest.getTab()); + assertFalse(classUnderTest.isVerbose()); + } + + @ParameterizedTest + @ValueSource(strings = {"-E,file/that/doesn't/exist", "-E,", "-E", + "-E,characters/Sorcerer.pcg,characters/Everything.pcg", + "--exportsheet,file/that/doesn't/exist", "--exportsheet,", "--exportsheet", + "--exportsheet,characters/Sorcerer.pcg,characters/Everything.pcg"}) + void testillegalUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + assertThrows(IllegalStateException.class, () -> from(args), + "The export sheet does not exist. The parsing must fail."); + } + } + + @Nested + class PartyTest + { + @ParameterizedTest + @ValueSource(strings = {"-p,characters/Sorcerer.pcg", "--party,characters/Sorcerer.pcg"}) + void testValidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + CommandLineArguments classUnderTest = from(args); + + assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); + assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); + assertEquals(Optional.empty(), classUnderTest.getExportSheet()); + assertEquals(Optional.empty(), classUnderTest.getOutputFile()); + assertEquals(Optional.of(new File("characters/Sorcerer.pcg")), classUnderTest.getPartyFile()); + assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); + assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); + assertFalse(classUnderTest.isStartNameGenerator()); + assertEquals(Optional.empty(), classUnderTest.getTab()); + assertFalse(classUnderTest.isVerbose()); + } + + @ParameterizedTest + @ValueSource(strings = {"-c,file/that/doesn't/exist", "-c,", "-c", + "-c,characters/Sorcerer.pcg,characters/Everything.pcg", + "--character,file/that/doesn't/exist", "--character,", "--character", + "--character,characters/Sorcerer.pcg,characters/Everything.pcg"}) + void testInvalidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + assertThrows(IllegalStateException.class, () -> from(args), + "The character file does not exist. The parsing must fail."); + } + } + + @Nested + class OutputFileTest + { + @ParameterizedTest + @ValueSource(strings = {"-o,characters/nonexisting-file", "--outputfile,characters/nonexisting-file"}) + void testValidNonExistingUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + CommandLineArguments classUnderTest = from(args); + + assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); + assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); + assertEquals(Optional.empty(), classUnderTest.getExportSheet()); + assertEquals(Optional.of(new File("characters/nonexisting-file")), classUnderTest.getOutputFile()); + assertEquals(Optional.empty(), classUnderTest.getPartyFile()); + assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); + assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); + assertFalse(classUnderTest.isStartNameGenerator()); + assertEquals(Optional.empty(), classUnderTest.getTab()); + assertFalse(classUnderTest.isVerbose()); + } + + @ParameterizedTest + @ValueSource(strings = {"-o,characters/Sorcerer.pcg", "--outputfile,characters/Sorcerer.pcg"}) + void testValidExistingUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + CommandLineArguments classUnderTest = from(args); + + assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); + assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); + assertEquals(Optional.empty(), classUnderTest.getExportSheet()); + assertEquals(Optional.of(new File("characters/Sorcerer.pcg")), classUnderTest.getOutputFile()); + assertEquals(Optional.empty(), classUnderTest.getPartyFile()); + assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); + assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); + assertFalse(classUnderTest.isStartNameGenerator()); + assertEquals(Optional.empty(), classUnderTest.getTab()); + assertFalse(classUnderTest.isVerbose()); + } + + @ParameterizedTest + @ValueSource(strings = {"-o,", "-o", "-o,more/than,one/file", + "--outputfile,", "--outputfile", "--outputfile,more/than,one/file"}) + void testInvalidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + assertThrows(IllegalStateException.class, () -> from(args), + "The output file is wrong. The parsing must fail."); + } + } + + @Nested + class NameGeneratorTest + { + @ParameterizedTest + @ValueSource(strings = {"--name-generator"}) + void testValidUsage(@ConvertWith(CSVtoArrayConverter.class) String... args) + { + CommandLineArguments classUnderTest = from(args); + + assertEquals(Optional.empty(), classUnderTest.getCampaignMode()); + assertEquals(Optional.empty(), classUnderTest.getCharacterFile()); + assertEquals(Optional.empty(), classUnderTest.getExportSheet()); + assertEquals(Optional.empty(), classUnderTest.getOutputFile()); + assertEquals(Optional.empty(), classUnderTest.getPartyFile()); + assertEquals(Optional.empty(), classUnderTest.getSettingsDir()); + assertEquals(Optional.empty(), classUnderTest.getConfigFileName()); + assertTrue(classUnderTest.isStartNameGenerator()); + assertEquals(Optional.empty(), classUnderTest.getTab()); + assertFalse(classUnderTest.isVerbose()); + } + } } diff --git a/code/standards/checkstyle-suppressions.xml b/code/standards/checkstyle-suppressions.xml index dcb01a4f205..1428123ccb4 100644 --- a/code/standards/checkstyle-suppressions.xml +++ b/code/standards/checkstyle-suppressions.xml @@ -1,9 +1,8 @@ - - + diff --git a/code/standards/checkstyle.xml b/code/standards/checkstyle.xml index 13b757ebd61..74373ea8d23 100644 --- a/code/standards/checkstyle.xml +++ b/code/standards/checkstyle.xml @@ -2,7 +2,7 @@ - + @@ -47,4 +47,8 @@ + + + + diff --git a/code/standards/ruleset.xml b/code/standards/ruleset.xml index 078cc7fcf36..6f8715d2755 100644 --- a/code/standards/ruleset.xml +++ b/code/standards/ruleset.xml @@ -34,6 +34,9 @@ + + + @@ -52,6 +55,7 @@ + @@ -101,6 +105,8 @@ + + @@ -173,6 +179,9 @@ + + + diff --git a/code/standards/spotbugs_ignore.xml b/code/standards/spotbugs_ignore.xml index cb43d880e14..3d9656711e8 100644 --- a/code/standards/spotbugs_ignore.xml +++ b/code/standards/spotbugs_ignore.xml @@ -21,7 +21,7 @@ - + diff --git a/code/testsuite/PCGfiles/pf_Cleric.pcg b/code/testsuite/PCGfiles/pf_Cleric.pcg index 7e2b2f29ee1..deec9285db3 100644 --- a/code/testsuite/PCGfiles/pf_Cleric.pcg +++ b/code/testsuite/PCGfiles/pf_Cleric.pcg @@ -263,7 +263,6 @@ CHARACTERMAGIC: CHARACTERDMNOTES: # Kits -KIT:Character Traits # Character Master/Follower diff --git a/code/testsuite/base-xml.ftl b/code/testsuite/base-xml.ftl index 5801f06d4e9..2f83a9bf626 100644 --- a/code/testsuite/base-xml.ftl +++ b/code/testsuite/base-xml.ftl @@ -1,6 +1,6 @@ <#ftl encoding="UTF-8" strip_whitespace=true > -<#-- +<#-- # Freemarker template for the character integration tests. # Copyright James Dempsey, 2013 # @@ -57,7 +57,7 @@ ${class} - + <@pcstring tag="TOTALLEVELS"/> <@pcstring tag="ECL"/> @@ -187,7 +187,7 @@ ==================================== ====================================--> -<@loop from=0 to=pcvar('COUNT[STATS]-1') ; stat , stat_has_next > +<@loop from=0 to=pcvar('COUNT[STATS]-1') ; stat , stat_has_next > <@pcstring tag="STAT.${stat}.LONGNAME"/> @@ -202,7 +202,7 @@ <@pcstring tag="STAT.${stat}.NOTEMP"/> <@pcstring tag="STAT.${stat}.MOD.NOTEMP"/> - + <#if weaponCategory?lower_case?contains('both')> <#if !weaponCategory?lower_case?contains('ranged')> @@ -852,7 +852,7 @@ F F - + <@loop from=0 to=pcvar('countdistinct("ABILITIES","CATEGORY=FEAT","VISIBILITY=DEFAULT[or]VISIBILITY=OUTPUT_ONLY","NATURE=AUTOMATIC")-1') ; feat , feat_has_next > @@ -866,7 +866,7 @@ F F - + <@loop from=0 to=pcvar('COUNT[VFEATS.VISIBLE]-1') ; feat , feat_has_next > @@ -880,7 +880,7 @@ F T - + <@loop from=0 to=pcvar('COUNT[FEATS.HIDDEN]-1') ; feat , feat_has_next > @@ -1241,7 +1241,7 @@ <#else> - + <#if (pcvar('COUNT[SPELLRACE]') > 0) > <#assign spellbook = 1 /> @@ -1278,7 +1278,7 @@ - + <@loop from=2 to=pcvar('COUNT[SPELLBOOKS]-1') ; spellbook > <#assign foo = pcvar('COUNT[SPELLRACE]') /> diff --git a/code/testsuite/base.xml b/code/testsuite/base.xml index a8616a1fc79..09a73bacfe8 100644 --- a/code/testsuite/base.xml +++ b/code/testsuite/base.xml @@ -95,9 +95,9 @@ |HITDICE| |INTERESTS| -|FOR,%lang,0,COUNT[LANGUAGES]-1,1,0| +|FOR,%lang,0,COUNT[LANGUAGES]-1,1,0| |LANGUAGES.%lang| -|ENDFOR| +|ENDFOR| |LANGUAGES| |LOCATION| @@ -162,7 +162,7 @@ ==================================== ====================================--> -|FOR,%stat,0,COUNT[STATS]-1,1,0| +|FOR,%stat,0,COUNT[STATS]-1,1,0| |STAT.%stat.LONGNAME| @@ -177,7 +177,7 @@ |STAT.%stat.NOTEMP| |STAT.%stat.MOD.NOTEMP| -|ENDFOR| +|ENDFOR| - + |FOR,%weap,0,COUNT[EQTYPE.WEAPON]-1,1,1| |IIF(WEAPON.%weap.CATEGORY:BOTH)| @@ -1008,7 +1008,7 @@ ====================================--> -|FOR,%feat,0,countdistinct("ABILITIES","CATEGORY=FEAT","VISIBILITY=DEFAULT[or]VISIBILITY=OUTPUT_ONLY","NATURE=NORMAL")-1,1,0| +|FOR,%feat,0,countdistinct("ABILITIES","CATEGORY=FEAT","VISIBILITY=DEFAULT[or]VISIBILITY=OUTPUT_ONLY","NATURE=NORMAL")-1,1,0| |ABILITY.FEAT.VISIBLE.%feat| |ABILITY.FEAT.VISIBLE.%feat.DESC| @@ -1019,10 +1019,10 @@ F F -|ENDFOR| +|ENDFOR| -|FOR,%feat,0,countdistinct("ABILITIES","CATEGORY=FEAT","VISIBILITY=DEFAULT[or]VISIBILITY=OUTPUT_ONLY","NATURE=AUTOMATIC")-1,1,0| +|FOR,%feat,0,countdistinct("ABILITIES","CATEGORY=FEAT","VISIBILITY=DEFAULT[or]VISIBILITY=OUTPUT_ONLY","NATURE=AUTOMATIC")-1,1,0| |ABILITYAUTO.FEAT.VISIBLE.%feat| |ABILITYAUTO.FEAT.VISIBLE.%feat.DESC| @@ -1033,10 +1033,10 @@ F F -|ENDFOR| +|ENDFOR| -|FOR,%feat,0,COUNT[VFEATS.VISIBLE]-1,1,0| +|FOR,%feat,0,COUNT[VFEATS.VISIBLE]-1,1,0| |VFEAT.VISIBLE.%feat| |VFEAT.VISIBLE.%feat.DESC| @@ -1047,7 +1047,7 @@ F T -|ENDFOR| +|ENDFOR| |FOR,%feat,0,COUNT[FEATS.HIDDEN]-1,1,0| @@ -1511,7 +1511,7 @@ ==================================== ====================================--> -|%FOLLOWERTYPE.FAMILIAR| +|%FOLLOWERTYPE.FAMILIAR| |FOLLOWERTYPE.FAMILIAR.0.NAME| @@ -1881,7 +1881,7 @@ |ELSE| - + |FOR,%spellrace,COUNT[SPELLRACE],COUNT[SPELLRACE],1,0| |IIF(%spellrace:0)| |ELSE| diff --git a/code/testsuite/csheets/35e_Alice.xml b/code/testsuite/csheets/35e_Alice.xml index 6c6e3264a2f..1ec316488ac 100644 --- a/code/testsuite/csheets/35e_Alice.xml +++ b/code/testsuite/csheets/35e_Alice.xml @@ -1898,7 +1898,7 @@ BIO - + @@ -1968,7 +1968,7 @@ BIO - + diff --git a/code/testsuite/csheets/35e_CloudGiantHalfDragon.xml b/code/testsuite/csheets/35e_CloudGiantHalfDragon.xml index 043261bfc24..738dacbf0a2 100644 --- a/code/testsuite/csheets/35e_CloudGiantHalfDragon.xml +++ b/code/testsuite/csheets/35e_CloudGiantHalfDragon.xml @@ -1920,7 +1920,7 @@ BIO - + @@ -2017,7 +2017,7 @@ BIO - + diff --git a/code/testsuite/csheets/35e_Jango.xml b/code/testsuite/csheets/35e_Jango.xml index 37ca2ec0f6d..5c3bd9c6171 100644 --- a/code/testsuite/csheets/35e_Jango.xml +++ b/code/testsuite/csheets/35e_Jango.xml @@ -1661,7 +1661,7 @@ BIO - + @@ -1905,7 +1905,7 @@ BIO - + diff --git a/code/testsuite/csheets/35e_Lauren.xml b/code/testsuite/csheets/35e_Lauren.xml index b789c5cece1..0c4e09170bf 100644 --- a/code/testsuite/csheets/35e_Lauren.xml +++ b/code/testsuite/csheets/35e_Lauren.xml @@ -1263,7 +1263,7 @@ BIO - + @@ -1302,7 +1302,7 @@ BIO - + diff --git a/code/testsuite/csheets/35e_Mallory.xml b/code/testsuite/csheets/35e_Mallory.xml index a34ad7c9ba8..aaac9559384 100644 --- a/code/testsuite/csheets/35e_Mallory.xml +++ b/code/testsuite/csheets/35e_Mallory.xml @@ -1520,7 +1520,7 @@ BIO - + @@ -1677,7 +1677,7 @@ BIO - + diff --git a/code/testsuite/csheets/3e_BarJack.xml b/code/testsuite/csheets/3e_BarJack.xml index 92409d2f50a..dfb25b6a701 100644 --- a/code/testsuite/csheets/3e_BarJack.xml +++ b/code/testsuite/csheets/3e_BarJack.xml @@ -1664,7 +1664,7 @@ BIO - + @@ -1790,7 +1790,7 @@ BIO - + diff --git a/code/testsuite/csheets/msrd_Elwood.xml b/code/testsuite/csheets/msrd_Elwood.xml index 48ef0ef18df..9ea68c5a45d 100644 --- a/code/testsuite/csheets/msrd_Elwood.xml +++ b/code/testsuite/csheets/msrd_Elwood.xml @@ -2050,9 +2050,9 @@ BIO - + - + diff --git a/code/testsuite/csheets/pf_Cleric.xml b/code/testsuite/csheets/pf_Cleric.xml index e8750108a25..73bd1693f97 100644 --- a/code/testsuite/csheets/pf_Cleric.xml +++ b/code/testsuite/csheets/pf_Cleric.xml @@ -876,9 +876,9 @@ BIO 2 - +2 - +0 - +2 + +0 + -2 + +0 +0 @@ -888,7 +888,7 @@ BIO 1 5 M - + You draw a light crossbow back by pulling a lever. Loading a light crossbow is a move action that provokes attacks of opportunity.&nl;Normally, operating a light crossbow requires two hands. However, you can shoot, but not load, a light crossbow with one hand at a -2 penalty on attack rolls. You can shoot a light crossbow with each hand, but you take a penalty on attack rolls as if attacking with two light weapons. This penalty is cumulative with the penalty for one-handed firing.