diff --git a/.circleci/config.yml b/.circleci/config.yml index 22263b46..74873bc4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ jobs: jacoco: docker: # specify the version you desire here - - image: cimg/openjdk:17.0.2 + - image: openjdk:21 # Specify service dependencies here if necessary # CircleCI maintains a library of pre-built images @@ -37,7 +37,7 @@ jobs: path: ./build/reports pitest: docker: - - image: cimg/openjdk:17.0.2 + - image: openjdk:21 parallelism: 6 resource_class: large diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3cffb81a..ba5077ac 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,22 +11,23 @@ updates: interval: "weekly" target-branch: "dev" allow: + - dependency-name: "org.beryx.jlink" + - dependency-name: "org.testng:testng" - dependency-name: "com.opencsv:opencsv" - - dependency-name: "org.javatuples:javatuples" + - dependency-name: "info.solidsoft.pitest" - dependency-name: "org.jgrapht:jgrapht-ext" - - dependency-name: "org.jetbrains:annotations" - - dependency-name: "org.testng:testng" - - dependency-name: "org.slf4j:slf4j-api" - - dependency-name: "org.slf4j:slf4j-simple" - - dependency-name: "nl.jqno.equalsverifier:equalsverifier" - dependency-name: "org.openjfx.javafxplugin" - - dependency-name: "org.beryx.jlink" + - dependency-name: "org.javatuples:javatuples" + - dependency-name: "org.jetbrains:annotations" + - dependency-name: "ch.qos.logback:logback-classic" - dependency-name: "org.javamodularity.moduleplugin" + - dependency-name: "org.pitest:pitest-testng-plugin" - dependency-name: "com.github.breadmoirai.github-release" + - dependency-name: "nl.jqno.equalsverifier:equalsverifier" + ignore: - dependency-name: "com.jfoenix:jfoenix" # Must stay at 9.0.4 - dependency-name: "io.github.palexdev:materialfx" # Must stay at 11.12.0 - - dependency-name: "info.solidsoft.pitest" # Must stay at 1.7.4 - package-ecosystem: "github-actions" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index ff65cd50..9497d728 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL @@ -41,12 +41,10 @@ jobs: # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - - name: Set up JDK 17 - uses: actions/setup-java@v3 + - name: Set up JDK 21 + uses: oracle-actions/setup-java@v1 with: - java-version: 17 - distribution: 'adopt' - check-latest: true + release: 21 - name: Grant execute permission for gradlew run: chmod +x gradlew diff --git a/.github/workflows/publish_executables.yml b/.github/workflows/publish-executables.yml similarity index 75% rename from .github/workflows/publish_executables.yml rename to .github/workflows/publish-executables.yml index 353b5721..9ca02264 100644 --- a/.github/workflows/publish_executables.yml +++ b/.github/workflows/publish-executables.yml @@ -1,7 +1,7 @@ # This workflow will build a Java project with Gradle # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle -name: Publishing Executable files using Gradle +name: Publishing Executable files on: push: @@ -14,14 +14,12 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v3 + - name: Set up JDK 21 + uses: oracle-actions/setup-java@v1 with: - java-version: 17 - distribution: 'adopt' - check-latest: true + release: 21 - name: Grant execute permission for gradlew run: chmod +x gradlew @@ -56,20 +54,18 @@ jobs: needs: windows-pipeline steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v3 + - name: Set up JDK 21 + uses: oracle-actions/setup-java@v1 with: - java-version: 17 - distribution: 'adopt' - check-latest: true + release: 21 - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle - run: ./gradlew build + run: ./gradlew jar # - name: Linking Dependencies with Gradle # run: ./gradlew jlink @@ -86,20 +82,18 @@ jobs: needs: linux-pipeline steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v3 + - name: Set up JDK 21 + uses: oracle-actions/setup-java@v1 with: - java-version: 17 - distribution: 'adopt' - check-latest: true + release: 21 - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle - run: ./gradlew build + run: ./gradlew jar # - name: Linking Dependencies with Gradle # run: ./gradlew jlink diff --git a/.github/workflows/test-build-executables.yml b/.github/workflows/test-build-executables.yml new file mode 100644 index 00000000..a9757225 --- /dev/null +++ b/.github/workflows/test-build-executables.yml @@ -0,0 +1,36 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: JPackage Simulation + +on: + push: + branches: + - dev + +jobs: + windows-pipeline: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: oracle-actions/setup-java@v1 + with: + release: 21 + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew build + + - name: Creating the Executable + run: ./gradlew jpackageImage + + - name: Creating the Tarball + run: ./gradlew tarExecutableFiles + + - name: Creating the Zip File + run: ./gradlew zipExecutableFiles diff --git a/.gitignore b/.gitignore index 8468d478..15f8c00b 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ hs_err_pid* Dockafile - Copy /.project /.settings/org.eclipse.buildship.core.prefs +/index.html +/script.js +/styles.css diff --git a/LICENSE b/LICENSE index 13014e9f..4db59d9d 100644 --- a/LICENSE +++ b/LICENSE @@ -19,3 +19,11 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Notice Regarding Images: +All pictures used in this project are sourced from the game and are not owned +by the author of this software. The images are utilized for illustrative and +educational purposes only, and all rights, credits, and acknowledgments for +the images belong to their respective owners or copyright holders. +If any copyright owner wishes to have their images removed, please contact +the author, and appropriate actions will be taken. \ No newline at end of file diff --git a/Learned.md b/Learned.md index c5f200c0..be7ed0b3 100644 --- a/Learned.md +++ b/Learned.md @@ -6,10 +6,10 @@ ## Project Tech/Design Choices When my friend was working on the project, he helped me decide to move to a RadioButton design for switching between puzzles. This helped me get away from my previous design of nested TabPanes in a TabPane, which probably isn't the most visually appealing. -Another thing we talked about while he was on the project was moving back to Java 8 since it was easier to work with JavaFX. This decision was later reversed after some time experimenting in Gradle. Since I have dependencies that needed to be taken into account, Java 15 was the one I landed on. +Another thing we talked about while he was on the project was moving back to Java 8 since it was easier to work with JavaFX. This decision was later reversed after some time experimenting in Gradle. ## Gradle/Continuous Integration - Using the build.gradle file to organize dependencies and plugins, facilitate testing, set up distributions in zip and tar files, and releases for GitHub. -- Making custom tasks and using parameters for CI pipelines to test certain parts of the code. +- Making custom tasks and using parameters for CI pipelines to partition my test suite. - Automating tests with CircleCI using this [video](https://www.youtube.com/watch?v=9PgZCJNzY9M) as a guide. - Coming across ~~LGTM~~ (_when it was still around_) Codacy and taking advantage of its code quality checking for flaws in my Java code. ## Graphs and their Algorithms @@ -23,12 +23,12 @@ Another thing we talked about while he was on the project was moving back to Jav ### School #### Tools & Practices - Understanding how to use Git and GitHub to host my project. -- Having an introduction to Gradle from one of my teammates trying to bring it into the project. (Wasn't successful, but still) +- Having an introduction to Gradle from one of my teammates trying to bring it into another project. (_Wasn't successful, but still_) #### Verification - Understanding the perks of TestNG vs. my previous experience with J-Unit. - Discovering Pitest, Exploratory Testing, Mockito, and Jacoco as different ways to test code and thinking about which concepts I should implement into the project. #### Design Patterns -- Using Factories, Facade, and Observers to facilitate repeating code and updating related modules. +- Using Adapters, Factories, Facades, and Observers to facilitate repeating code and updating related modules. - Being introduced to code coupling and cohesion, which was later applied when I had a CodeMR free trial. ### Udemy - Understanding how to write Groovy code. [Link](https://www.udemy.com/course/gradle-for-java-developers/) diff --git a/README.md b/README.md index 24fdae9f..3b8db918 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![CircleCI](https://circleci.com/gh/Ultraviolet-Ninja/GradleCenturion/tree/main.svg?style=shield)](https://circleci.com/gh/Ultraviolet-Ninja/GradleCenturion/tree/main) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/b4b8571475d543a2afc720f5f96ae2cf)](https://www.codacy.com/gh/Ultraviolet-Ninja/GradleCenturion/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Ultraviolet-Ninja/GradleCenturion&utm_campaign=Badge_Grade) -![Project Version](https://img.shields.io/badge/version-0.23.0-blueviolet) +![Project Version](https://img.shields.io/badge/version-0.23.1-blueviolet) ## Intro This project is designed to solve all puzzles found on the Centurion Bomb from Keep Talking and Nobody Explodes, which is a combination of many community-made puzzles and some from the base game set in different languages.
@@ -11,20 +11,19 @@ This project is designed to solve all puzzles found on the Centurion Bomb from K This is a huge project for one man to tackle, but I've [learned a lot](Learned.md) from the challenges I've faced. ## Technologies -- Java 17 -- Gradle 7.4 +- Java 21 +- Gradle 8.4 ### Plugins - JavaFX -- Palantir Docker - Breadmoirai GitHub Release ### Dependencies -- MaterialFX ver. 11.12.0 -- JFoenix ver. 9.0.4 -- JavaTuple ver. 1.2 -- JGraphT ver. 1.5.1 -- OpenCSV ver. 5.7.1 +- MaterialFX ver. `11.12.0` +- JFoenix ver. `9.0.4` +- JavaTuple ver. `1.2` +- JGraphT ver. `1.5.2` +- OpenCSV ver. `5.8` ### Other Technologies -- Circle CI +- Circle CI with TestNG and Pitest - CodeMR Free Trial ## Status - In progress diff --git a/build.gradle b/build.gradle index fd284aed..7f8215ae 100644 --- a/build.gradle +++ b/build.gradle @@ -2,20 +2,18 @@ plugins { id 'java' id 'jacoco' id 'application' - id 'org.beryx.jlink' version '2.26.0' - id 'info.solidsoft.pitest' version '1.7.4' - id 'org.openjfx.javafxplugin' version '0.0.13' + id 'org.beryx.jlink' version '3.0.0' + id 'info.solidsoft.pitest' version '1.15.0' + id 'org.openjfx.javafxplugin' version '0.1.0' id 'org.javamodularity.moduleplugin' version '1.8.12' id 'com.github.breadmoirai.github-release' version '2.4.1' } group 'jasmine.jragon' -version '0.23.0' +version '0.23.1' -java { - sourceCompatibility(17) - targetCompatibility(17) -} +sourceCompatibility = 21 +targetCompatibility = 21 application { mainClassName = project.hasProperty('testingArea') ? @@ -42,11 +40,12 @@ repositories { } dependencies { - implementation 'com.opencsv:opencsv:5.7.1' + implementation 'com.opencsv:opencsv:5.8' implementation 'org.javatuples:javatuples:1.2' - implementation 'org.jgrapht:jgrapht-ext:1.5.1' - implementation 'org.jetbrains:annotations:24.0.0' + implementation 'org.jgrapht:jgrapht-ext:1.5.2' + implementation 'org.jetbrains:annotations:24.0.1' //implementation 'io.github.fvarrui:javpackager:1.5.1' + implementation 'ch.qos.logback:logback-classic:1.4.8' implementation('com.jfoenix:jfoenix:9.0.4') { exclude group: "org.javafx" @@ -55,10 +54,9 @@ dependencies { exclude group: "org.javafx" } - testImplementation 'org.testng:testng:7.7.1' - testImplementation 'org.slf4j:slf4j-api:2.0.6' - testImplementation 'org.slf4j:slf4j-simple:2.0.6' - testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.13.2' + testImplementation 'org.testng:testng:7.8.0' + testImplementation 'org.pitest:pitest-testng-plugin:1.0.0' + testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.15.2' } def firstInstance = project.hasProperty('thread0') @@ -105,7 +103,7 @@ test { } jacocoTestReport { - reports.xml.enabled(true) + reports.xml.required = true if (includedTestDirectories.size() != 3) { afterEvaluate { @@ -135,26 +133,31 @@ static List getTargetClasses(boolean firstInstance, boolean secondInstan } pitest { - targetClasses = getTargetClasses(firstInstance, secondInstance, thirdInstance, fourthInstance, - fifthInstance, sixthInstance) - outputFormats = ['HTML'] + targetClasses.set( + getTargetClasses(firstInstance, secondInstance, thirdInstance, fourthInstance, + fifthInstance, sixthInstance) + ) + outputFormats.add('HTML') testPlugin = 'testng' + verbose = true } javafx { - version = '17.0.2' + version = '17.0.8' modules = ['javafx.controls', 'javafx.fxml'] } jar.manifest.attributes(['Main-Class': "${mainClassName}"]) +def buildDirectory = buildDir + jlink { - imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip") - options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'] + imageZip.set(project.file("${buildDirectory}/distributions/app-${javafx.platform.classifier}.zip")) + options.set(['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']) addExtraDependencies("javafx") def opSys = org.gradle.internal.os.OperatingSystem.current() - String iconBaseFilename = "${buildDir}/resources/main/bomb/KTANE logo" + String iconBaseFilename = "${buildDirectory}/resources/main/bomb/KTANE logo" if (opSys.windows) { launcher { @@ -190,37 +193,37 @@ jlink { tasks.register('zipExecutableFiles', Zip) { dependsOn(jpackageImage) - from buildDir + from buildDirectory def opSys = org.gradle.internal.os.OperatingSystem.current() String baseFilename = "Gradle-Centurion-${project.version}-" if (opSys.windows) { include 'jpackage/Gradle Centurion Win/**' - archiveName "${baseFilename}win.zip" + archiveFileName.set("${baseFilename}win.zip") } else if (opSys.linux) { include 'jpackage/Gradle Centurion Linux/**' - archiveName "${baseFilename}linux.zip" + archiveFileName.set("${baseFilename}linux.zip") } else { include 'jpackage/Gradle Centurion Mac/**' - archiveName "${baseFilename}mac.zip" + archiveFileName.set("${baseFilename}mac.zip") } } tasks.register('tarExecutableFiles', Tar) { dependsOn(jpackageImage) - from buildDir + from buildDirectory def opSys = org.gradle.internal.os.OperatingSystem.current() String baseFilename = "Gradle-Centurion-${project.version}-" if (opSys.windows) { include 'jpackage/Gradle Centurion Win/**' - archiveName "${baseFilename}win.tar" + archiveFileName.set("${baseFilename}win.tar") } else if (opSys.linux) { include 'jpackage/Gradle Centurion Linux/**' - archiveName "${baseFilename}linux.tar" + archiveFileName.set("${baseFilename}linux.tar") } else { include 'jpackage/Gradle Centurion Mac/**' - archiveName "${baseFilename}mac.tar" + archiveFileName.set("${baseFilename}mac.tar") } } @@ -229,7 +232,7 @@ githubRelease { owner = 'Ultraviolet-Ninja' def opSys = org.gradle.internal.os.OperatingSystem.current() - String baseFilename = "${buildDir}/distributions/Gradle-Centurion-${project.version}-" + String baseFilename = "${buildDirectory}/distributions/Gradle-Centurion-${project.version}-" allowUploadToExisting = !opSys.windows if (opSys.windows) { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 41dfb879..e411586a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/bomb/Main.java b/src/main/java/bomb/Main.java index eb4bc4a7..79dfac51 100644 --- a/src/main/java/bomb/Main.java +++ b/src/main/java/bomb/Main.java @@ -9,7 +9,6 @@ import javafx.scene.input.KeyEvent; import javafx.stage.Stage; -import java.util.List; import java.util.stream.Stream; import static javafx.scene.input.KeyCode.DIGIT0; @@ -23,10 +22,11 @@ import static javafx.scene.input.KeyCode.DIGIT8; import static javafx.scene.input.KeyCode.DIGIT9; import static javafx.scene.input.KeyCode.DOWN; +import static javafx.scene.input.KeyCode.E; import static javafx.scene.input.KeyCode.UP; import static javafx.scene.input.KeyCombination.CONTROL_DOWN; -public class Main extends Application { +public final class Main extends Application { @Override public void start(Stage primaryStage) throws Exception { FXMLLoader loader = new FXMLLoader(Main.class.getResource("manual.fxml")); @@ -36,6 +36,7 @@ public void start(Stage primaryStage) throws Exception { setSceneKeyboardEvents(scene, controller); setSceneArrowEvents(scene, controller); + setupSearchBarFocus(scene, controller); primaryStage.setTitle("Centurion Bomb Manual"); primaryStage.setScene(scene); @@ -44,7 +45,7 @@ public void start(Stage primaryStage) throws Exception { } private static void setSceneKeyboardEvents(Scene scene, ManualController controller) { - List digitList = Stream.of(DIGIT1, DIGIT2, DIGIT3, DIGIT4, DIGIT5, DIGIT6, DIGIT7, + var digitList = Stream.of(DIGIT1, DIGIT2, DIGIT3, DIGIT4, DIGIT5, DIGIT6, DIGIT7, DIGIT8, DIGIT9, DIGIT0) .map(code -> new KeyCodeCombination(code, CONTROL_DOWN)) .toList(); @@ -83,6 +84,18 @@ private static void setSceneArrowEvents(Scene scene, ManualController controller ); } + private static void setupSearchBarFocus(Scene scene, ManualController controller) { + KeyCodeCombination searchCombination = new KeyCodeCombination(E, CONTROL_DOWN); + + scene.addEventFilter( + KeyEvent.KEY_PRESSED, + event -> { + if (searchCombination.match(event)) + controller.focusOnSearchBar(); + } + ); + } + public static void main(String[] args) { launch(args); } diff --git a/src/main/java/bomb/ManualController.java b/src/main/java/bomb/ManualController.java index 971371d7..c5cf98cf 100644 --- a/src/main/java/bomb/ManualController.java +++ b/src/main/java/bomb/ManualController.java @@ -1,15 +1,9 @@ package bomb; -import bomb.annotation.DisplayComponent; -import bomb.modules.ab.blind.alley.BlindAlleyController; -import bomb.modules.s.souvenir.SouvenirController; -import bomb.tools.note.NoteController; -import bomb.tools.pattern.facade.FacadeFX; -import bomb.tools.pattern.observer.BlindAlleyPaneObserver; +import bomb.tools.boot.FxmlBootDrive; import bomb.tools.pattern.observer.ForgetMeNotToggleObserver; import bomb.tools.pattern.observer.ObserverHub; import bomb.tools.pattern.observer.ResetObserver; -import bomb.tools.pattern.observer.SouvenirPaneObserver; import bomb.tools.pattern.observer.SouvenirToggleObserver; import com.jfoenix.controls.JFXRadioButton; import javafx.fxml.FXML; @@ -18,32 +12,29 @@ import javafx.scene.control.RadioButton; import javafx.scene.control.TextField; import javafx.scene.control.Toggle; - import javafx.scene.control.ToggleGroup; import javafx.scene.layout.GridPane; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; -import org.javatuples.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; -import java.net.URL; -import java.util.ArrayDeque; +import java.time.Duration; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.SequencedMap; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import static bomb.tools.number.MathUtils.negativeSafeModulo; import static bomb.tools.pattern.facade.FacadeFX.GET_TOGGLE_NAME; import static bomb.tools.pattern.factory.TextFormatterFactory.createSearchBarFormatter; -import static bomb.tools.pattern.observer.ObserverHub.ObserverIndex.BLIND_ALLEY_PANE; import static bomb.tools.pattern.observer.ObserverHub.ObserverIndex.FORGET_ME_NOT_TOGGLE; import static bomb.tools.pattern.observer.ObserverHub.ObserverIndex.RESET; -import static bomb.tools.pattern.observer.ObserverHub.ObserverIndex.SOUVENIR_PANE; import static bomb.tools.pattern.observer.ObserverHub.ObserverIndex.SOUVENIR_TOGGLE; -import static java.util.Arrays.asList; import static java.util.concurrent.CompletableFuture.supplyAsync; import static java.util.function.UnaryOperator.identity; import static java.util.stream.Collectors.toMap; @@ -52,6 +43,7 @@ public final class ManualController { //TODO - Remove when every FXML file is being used private static final Region EMPTY_VIEW; + private static final Logger LOG = LoggerFactory.getLogger(ManualController.class); private Map regionMap; private List observableRadioList; @@ -99,19 +91,30 @@ public void initialize() throws ExecutionException, InterruptedException { long start = System.nanoTime(); regionMap = setupRegionMap().get(); long stop = System.nanoTime(); - System.out.printf("Timer: %,d%n", stop - start); + LOG.info("Boot Time: {}", convertTime(stop - start)); + ObserverHub.ensureMapIsPopulated(); + } + + private static String convertTime(long nanos) { + var duration = Duration.ofNanos(nanos); + long seconds = duration.getSeconds() % 60; + long millis = duration.toMillis() % 1000; + return String.format("%01d.%03d sec", seconds, millis); } private CompletableFuture> setupRegionMap() { - ResetObserver resetObserver = new ResetObserver(); + var resetObserver = new ResetObserver(); ObserverHub.addObserver(RESET, resetObserver); - var fxmlMapFuture = supplyAsync(() -> createFXMLMap(resetObserver)); + + //Change the drive to test a new way to load the fxml files + var drive = FxmlBootDrive.createStandardDrive(); + var fxmlMapFuture = supplyAsync(() -> drive.createFXMLMap(resetObserver)); var radioButtonNameFuture = createButtonNameFuture(options.getToggles()); - return radioButtonNameFuture.thenCombine(fxmlMapFuture, ManualController::createRegionMap); + return radioButtonNameFuture.thenCombine(fxmlMapFuture, ManualController::joinOnStringKeys); } - private static CompletableFuture> createButtonNameFuture(List radioButtonList) { + private static CompletableFuture> createButtonNameFuture(List radioButtonList) { return supplyAsync(radioButtonList::stream) .thenApply(stream -> stream.collect(toMap( GET_TOGGLE_NAME, @@ -121,84 +124,22 @@ private static CompletableFuture> createButtonNameFuture(Lis ))); } - private static Map createRegionMap(Map radioButtonMap, - Map filePathMap) { - Map regionMap = new LinkedHashMap<>(); - for (Map.Entry entry : radioButtonMap.entrySet()) + private static Map joinOnStringKeys(SequencedMap radioButtonMap, + SequencedMap filePathMap) { + Map regionMap = LinkedHashMap.newLinkedHashMap(radioButtonMap.size()); + for (var entry : radioButtonMap.sequencedEntrySet()) { regionMap.put( entry.getValue(), filePathMap.getOrDefault(entry.getKey(), EMPTY_VIEW) ); - return regionMap; - } - - private static Map createFXMLMap(ResetObserver resetObserver) { - var displayClassStream = getDisplayedClasses().parallelStream(); - - if (System.getProperty("os.name").toLowerCase().contains("linux")) { - displayClassStream = displayClassStream.sequential(); } - - return displayClassStream - .map(cls -> mapClassToRegion(cls, resetObserver)) - .collect(toMap(Pair::getValue0, Pair::getValue1)); - } - - private static List> getDisplayedClasses() { - List> list = new ArrayList<>(List.of(Widget.class, NoteController.class)); - ArrayDeque> files = new ArrayDeque<>(asList(Widget.class.getPermittedSubclasses())); - - Class temp; - while ((temp = files.poll()) != null) { - Class[] nextSubLevel = temp.getPermittedSubclasses(); - if (nextSubLevel != null) { - files.addAll(asList(nextSubLevel)); - } - - if (temp.isAnnotationPresent(DisplayComponent.class)) { - list.add(temp); - } - } - return list; - } - - private static Pair mapClassToRegion(Class clazz, ResetObserver resetObserver) { - DisplayComponent annotation = clazz.getAnnotation(DisplayComponent.class); - URL resource = clazz.getResource(annotation.resource()); - String buttonLinkerName = annotation.buttonLinkerName(); - - return new Pair<>( - buttonLinkerName, - createSingleRegion(new FXMLLoader(resource), resetObserver) - ); - } - - private static Region createSingleRegion(FXMLLoader loader, ResetObserver resetObserver) - throws IllegalArgumentException { - Region output = FacadeFX.load(loader); - String location = loader.getLocation().toString(); - - if (!location.endsWith("widget.fxml")) resetObserver.addController(loader); - - if (location.endsWith("souvenir.fxml")) loadSouvenirController(loader.getController()); - else if (location.endsWith("blind_alley.fxml")) loadBlindAlleyController(loader.getController()); - return output; - } - - private static void loadBlindAlleyController(BlindAlleyController controller) { - ObserverHub.addObserver(BLIND_ALLEY_PANE, new BlindAlleyPaneObserver(controller)); - } - - private static void loadSouvenirController(SouvenirController controller) { - ObserverHub.addObserver(SOUVENIR_PANE, new SouvenirPaneObserver(controller)); + return regionMap; } @FXML - public void switchPaneByButtonPress() { + private void switchPaneByButtonPress() { Toggle selected = options.getSelectedToggle(); - String selectedName = GET_TOGGLE_NAME.apply(selected); - if (selectedName.equals("Blind Alley")) ObserverHub.updateAtIndex(BLIND_ALLEY_PANE); - else if (selectedName.equals("Souvenir")) ObserverHub.updateAtIndex(SOUVENIR_PANE); + ObserverHub.scanButtonName(GET_TOGGLE_NAME.apply(selected)); paneSwitch(regionMap.get(selected)); } @@ -208,7 +149,7 @@ private void paneSwitch(final Region pane) { } @FXML - public void search() { + private void search() { String searchTerm = searchBar.getText().toLowerCase(); radioButtonHouse.getChildren().clear(); if (searchTerm.isEmpty()) { @@ -252,4 +193,8 @@ void switchPaneByDownArrow() { index %= size; switchPaneByIndex(index); } + + void focusOnSearchBar() { + searchBar.requestFocus(); + } } diff --git a/src/main/java/bomb/modules/ab/battleship/BattleshipController.java b/src/main/java/bomb/modules/ab/battleship/BattleshipController.java index d34908ef..6f375fda 100644 --- a/src/main/java/bomb/modules/ab/battleship/BattleshipController.java +++ b/src/main/java/bomb/modules/ab/battleship/BattleshipController.java @@ -13,7 +13,9 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashSet; import java.util.List; +import java.util.SequencedSet; import java.util.Set; import java.util.stream.Stream; @@ -209,9 +211,9 @@ private void solveBoard() { ships[i].setCurrentQuantity(shipCounts[i]); } - Set solve; + SequencedSet solve; try { - solve = Battleship.solveOcean(); + solve = new LinkedHashSet<>(Battleship.solveOcean()); } catch (IllegalArgumentException e) { FacadeFX.setAlert(ERROR, e.getMessage(), "Incomplete Information Given", "Information Error"); @@ -219,7 +221,7 @@ private void solveBoard() { } if (solve.size() == 1) { - translateToFrontendGrid(frontendGrid, solve.iterator().next()); + translateToFrontendGrid(frontendGrid, solve.getFirst()); } else { FacadeFX.setAlert(ERROR, "Displaying extra solutions in new window(s)", "Multiple solutions detected", "Unexpected state"); diff --git a/src/main/java/bomb/modules/ab/battleship/Ocean.java b/src/main/java/bomb/modules/ab/battleship/Ocean.java index 48bacc76..79e89fb6 100644 --- a/src/main/java/bomb/modules/ab/battleship/Ocean.java +++ b/src/main/java/bomb/modules/ab/battleship/Ocean.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.List; import java.util.function.IntFunction; import java.util.function.Predicate; diff --git a/src/main/java/bomb/modules/c/caesar/cipher/Caesar.java b/src/main/java/bomb/modules/c/caesar/cipher/Caesar.java index 599d93a1..4ac2f425 100644 --- a/src/main/java/bomb/modules/c/caesar/cipher/Caesar.java +++ b/src/main/java/bomb/modules/c/caesar/cipher/Caesar.java @@ -37,10 +37,9 @@ private static int createOffset() { if (hasLitIndicator(NSA) && doesPortExists(PARALLEL)) return 0; - int out = 0; - out += getAllBatteries(); + int out = getAllBatteries(); if (hasVowelInSerialCode()) out--; - if (getSerialCodeLastDigit() % 2 == 0) out++; + if ((getSerialCodeLastDigit() & 1) == 0) out++; if (hasIndicator(CAR)) out++; return out; } diff --git a/src/main/java/bomb/modules/c/cheap/checkout/CheapCheckout.java b/src/main/java/bomb/modules/c/cheap/checkout/CheapCheckout.java index 46a9912f..25b1dd4e 100644 --- a/src/main/java/bomb/modules/c/cheap/checkout/CheapCheckout.java +++ b/src/main/java/bomb/modules/c/cheap/checkout/CheapCheckout.java @@ -6,6 +6,7 @@ import java.time.DayOfWeek; import java.util.List; +import java.util.function.Consumer; import java.util.function.ToDoubleFunction; import static bomb.modules.c.cheap.checkout.CheckoutItem.Category.FRUIT; @@ -17,7 +18,7 @@ public final class CheapCheckout extends Widget { private static final double SUNDAY_ADDITION, THURSDAY_SALE, FRIDAY_MARK_UP, SATURDAY_SALE; private static final int REQUIRED_ITEM_COUNT, REQUIRED_WEIGHT_COUNT; - private static final ToDoubleFunction> TO_SUM; + private static final ToDoubleFunction> ITEM_LIST_TO_SUM; static { SUNDAY_ADDITION = 2.15; @@ -28,7 +29,7 @@ public final class CheapCheckout extends Widget { REQUIRED_ITEM_COUNT = 6; REQUIRED_WEIGHT_COUNT = 2; - TO_SUM = items -> items.stream() + ITEM_LIST_TO_SUM = items -> items.stream() .mapToDouble(CheckoutItem::getCurrentPiece) .sum(); } @@ -92,19 +93,20 @@ private static double calculateMalleableMonday(List items) { items.get(2).applyMultiplicand(sale); items.get(5).applyMultiplicand(sale); - return TO_SUM.applyAsDouble(items); + return ITEM_LIST_TO_SUM.applyAsDouble(items); } private static double calculateTroublesomeTuesday(List items) { - int counter = 0; - for (CheckoutItem item : items) { + Consumer applyTroublesomeTuesday = (item) -> { int digRoot = digitalRoot(item.getCurrentPiece()); item.addToPrice(digRoot); - if (++counter == 4) - break; - } + }; + + items.stream() + .limit(4) + .forEach(applyTroublesomeTuesday); - return TO_SUM.applyAsDouble(items); + return ITEM_LIST_TO_SUM.applyAsDouble(items); } private static double calculateWackyWednesday(List items) { @@ -121,7 +123,7 @@ private static double calculateThrillingThursday(List items) { for (int i = 0; i < size; i += 2) items.get(i).applyMultiplicand(THURSDAY_SALE); - return TO_SUM.applyAsDouble(items); + return ITEM_LIST_TO_SUM.applyAsDouble(items); } private static double calculateFruityFriday(List items) { diff --git a/src/main/java/bomb/modules/c/chess/ChessBoard.java b/src/main/java/bomb/modules/c/chess/ChessBoard.java index 028e683b..2c3c581c 100644 --- a/src/main/java/bomb/modules/c/chess/ChessBoard.java +++ b/src/main/java/bomb/modules/c/chess/ChessBoard.java @@ -6,7 +6,7 @@ import static bomb.modules.c.chess.Tile.TileColor.BLACK; import static bomb.modules.c.chess.Tile.TileColor.WHITE; -public class ChessBoard { +public final class ChessBoard { public static final byte BOARD_LENGTH = 6; private final Tile[][] board; diff --git a/src/main/java/bomb/modules/c/chess/CoverageCalculator.java b/src/main/java/bomb/modules/c/chess/CoverageCalculator.java index 8f492062..4603f993 100644 --- a/src/main/java/bomb/modules/c/chess/CoverageCalculator.java +++ b/src/main/java/bomb/modules/c/chess/CoverageCalculator.java @@ -13,7 +13,7 @@ import static bomb.modules.c.chess.ChessBoard.BOARD_LENGTH; import static bomb.modules.c.chess.ChessPiece.BISHOP; -public class CoverageCalculator { +public final class CoverageCalculator { public static Coordinates findNonCoveredTileLocation(ChessBoard board) { for (int x = 0; x < BOARD_LENGTH; x++) { for (int y = 0; y < BOARD_LENGTH; y++) { diff --git a/src/main/java/bomb/modules/c/chess/Tile.java b/src/main/java/bomb/modules/c/chess/Tile.java index be3e4133..408ec9c4 100644 --- a/src/main/java/bomb/modules/c/chess/Tile.java +++ b/src/main/java/bomb/modules/c/chess/Tile.java @@ -1,6 +1,6 @@ package bomb.modules.c.chess; -public class Tile { +public final class Tile { public enum TileColor { BLACK, WHITE } diff --git a/src/main/java/bomb/modules/c/colored/switches/ColoredSwitchGraphFactory.java b/src/main/java/bomb/modules/c/colored/switches/ColoredSwitchGraphFactory.java index b10d180e..7936990b 100644 --- a/src/main/java/bomb/modules/c/colored/switches/ColoredSwitchGraphFactory.java +++ b/src/main/java/bomb/modules/c/colored/switches/ColoredSwitchGraphFactory.java @@ -18,7 +18,7 @@ import static bomb.modules.c.colored.switches.ColoredSwitches.canFollowPath; @SuppressWarnings("ConstantConditions") -public class ColoredSwitchGraphFactory { +public final class ColoredSwitchGraphFactory { private static final byte OUTGOING_STATE = 1, COLOR_CONDITIONS = 2, SWITCH_TO_FLIP = 3; private static final String FILENAME = "graph.csv"; diff --git a/src/main/java/bomb/modules/c/colored/switches/ColoredSwitchNode.java b/src/main/java/bomb/modules/c/colored/switches/ColoredSwitchNode.java index 970c8adf..9b57a8e9 100644 --- a/src/main/java/bomb/modules/c/colored/switches/ColoredSwitchNode.java +++ b/src/main/java/bomb/modules/c/colored/switches/ColoredSwitchNode.java @@ -7,7 +7,7 @@ import java.util.Map; import java.util.Set; -public class ColoredSwitchNode { +public final class ColoredSwitchNode { private static final int SIZE_LIMIT = 3; private final byte state; diff --git a/src/main/java/bomb/modules/dh/forget/me/not/ForgetMeNot.java b/src/main/java/bomb/modules/dh/forget/me/not/ForgetMeNot.java index fca7a028..8b7cc44a 100644 --- a/src/main/java/bomb/modules/dh/forget/me/not/ForgetMeNot.java +++ b/src/main/java/bomb/modules/dh/forget/me/not/ForgetMeNot.java @@ -73,28 +73,28 @@ private static int createSecondNumber(int stageNumber) { if (doesPortExists(SERIAL) && countNumbersInSerialCode() > 2) return stageNumber + largestSerialCodeNumber; - return stageNumber + FINAL_CODE.get(0) + - ((FINAL_CODE.get(0) % 2 == 0) ? 1 : -1); + return stageNumber + FINAL_CODE.getFirst() + + ((FINAL_CODE.getFirst() % 2 == 0) ? 1 : -1); } private static int createSucceedingNumber(int stageNumber) { int length = FINAL_CODE.size(); - if (FINAL_CODE.get(length - 1) == 0 || FINAL_CODE.get(length - 2) == 0) + if (FINAL_CODE.getLast() == 0 || FINAL_CODE.get(length - 2) == 0) return stageNumber + largestSerialCodeNumber; if (bothPreviousNumbersAreEven()) return stageNumber + smallestOddDigitInSerialCode(); return stageNumber + MOST_SIG_DIGIT.applyAsInt( - FINAL_CODE.get(length - 1) + FINAL_CODE.get(length - 2) + FINAL_CODE.getLast() + FINAL_CODE.get(length - 2) ); } private static boolean bothPreviousNumbersAreEven() { int length = FINAL_CODE.size(); - int firstPrevious = FINAL_CODE.get(length - 1); + int firstPrevious = FINAL_CODE.getLast(); int secondPrevious = FINAL_CODE.get(length - 2); - return firstPrevious% 2 == 0 && secondPrevious % 2 == 0; + return firstPrevious% 2 == 0 && secondPrevious % 2 == 0; } private static int smallestOddDigitInSerialCode() { @@ -124,9 +124,8 @@ public static void updateLargestValueInSerial() { } public static void undoLastStage() { - int size = FINAL_CODE.size(); - if (size != 0) - FINAL_CODE.remove(size - 1); + if (!FINAL_CODE.isEmpty()) + FINAL_CODE.removeLast(); } public static @NotNull String stringifyFinalCode() { diff --git a/src/main/java/bomb/modules/dh/hexamaze/Hexamaze.java b/src/main/java/bomb/modules/dh/hexamaze/Hexamaze.java index 4429587c..daec61eb 100644 --- a/src/main/java/bomb/modules/dh/hexamaze/Hexamaze.java +++ b/src/main/java/bomb/modules/dh/hexamaze/Hexamaze.java @@ -56,9 +56,7 @@ public final class Hexamaze extends Widget { Grid original = new Grid(new HexagonalPlane(nodeList)); Grid found = MazeSearch.search(maze, original) - .orElseThrow(() -> { - throw new IllegalArgumentException("Could not find maze from given shapes"); - }); + .orElseThrow(() -> new IllegalArgumentException("Could not find maze from given shapes")); int colorValue = copyPegLocation(original, found); Optional>> exitInfoOptional = ExitChecker.findPossibleExits(found); diff --git a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/factory/MazeFactory.java b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/factory/MazeFactory.java index 88ce675a..cdf207dd 100644 --- a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/factory/MazeFactory.java +++ b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/factory/MazeFactory.java @@ -26,12 +26,13 @@ import static java.util.stream.Collectors.toCollection; @SuppressWarnings("ConstantConditions") -public class MazeFactory { +public final class MazeFactory { public static @NotNull List createMaze() throws IllegalStateException { InputStream in = MazeFactory.class.getResourceAsStream("maze.csv"); try (CSVReader csvReader = new CSVReader(new InputStreamReader(in, UTF_8))) { - return csvReader.readAll().stream() + return csvReader.readAll() + .stream() .flatMap(Arrays::stream) .map(line -> line.split(" ")) .map(data -> new HexNode(decodeShape(data[1]), decodeWalls(data[0]))) diff --git a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/maze_finding/MazeSearch.java b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/maze_finding/MazeSearch.java index 7f068a91..72ba85a5 100644 --- a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/maze_finding/MazeSearch.java +++ b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/maze_finding/MazeSearch.java @@ -14,7 +14,7 @@ import static bomb.modules.dh.hexamaze.hexalgorithm.storage.AbstractHexagon.calculateColumnLengthArray; -public class MazeSearch { +public final class MazeSearch { private static final int ROTATION_COUNT = 6; /** diff --git a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/pathfinding/ExitChecker.java b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/pathfinding/ExitChecker.java index 421ec4f4..0e03cf03 100644 --- a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/pathfinding/ExitChecker.java +++ b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/pathfinding/ExitChecker.java @@ -27,7 +27,7 @@ import static bomb.modules.dh.hexamaze.hexalgorithm.storage.HexNode.HexWall.TOP_RIGHT; import static bomb.modules.dh.hexamaze.hexalgorithm.storage.HexagonalPlane.CALCULATE_SPAN; -public class ExitChecker { +public final class ExitChecker { /** * Determines the possible exits and the exit description from the grid that was found in the * MazeSearch search function diff --git a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/pathfinding/MazeRunner.java b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/pathfinding/MazeRunner.java index da184790..c98324ff 100644 --- a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/pathfinding/MazeRunner.java +++ b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/pathfinding/MazeRunner.java @@ -21,7 +21,7 @@ import static bomb.modules.dh.hexamaze.hexalgorithm.storage.HexNode.HexWall.BOTTOM_RIGHT; import static bomb.modules.dh.hexamaze.hexalgorithm.storage.HexNode.HexWall.TOP_RIGHT; -public class MazeRunner { +public final class MazeRunner { //Movement Vectors private static final Coordinates MOVE_DOWN, MOVE_RIGHT, LEFT_SIDE_MOVE_DOWN_RIGHT, RIGHT_SIDE_MOVE_TOP_RIGHT; diff --git a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/AbstractHexagon.java b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/AbstractHexagon.java index 47dee4b1..64225164 100644 --- a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/AbstractHexagon.java +++ b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/AbstractHexagon.java @@ -6,7 +6,7 @@ import static java.util.Arrays.copyOf; -public abstract class AbstractHexagon { +public abstract sealed class AbstractHexagon permits Grid, Maze { protected HexagonalPlane hexagon; public AbstractHexagon() { diff --git a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/Grid.java b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/Grid.java index 70b41fad..d1a85920 100644 --- a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/Grid.java +++ b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/Grid.java @@ -11,7 +11,7 @@ import static javafx.scene.paint.Color.RED; import static javafx.scene.paint.Color.YELLOW; -public class Grid extends AbstractHexagon implements Rotatable { +public final class Grid extends AbstractHexagon implements Rotatable { public static final int GRID_SIDE_LENGTH = 4; private final ArrayRing colorRing; diff --git a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/HexNode.java b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/HexNode.java index f2f56905..dce0b90f 100644 --- a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/HexNode.java +++ b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/HexNode.java @@ -11,7 +11,7 @@ import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toCollection; -public class HexNode implements Rotatable { +public final class HexNode implements Rotatable { private EnumSet walls; private HexShape hexShape; private int color; diff --git a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/HexagonalPlane.java b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/HexagonalPlane.java index 6e93bc68..e8cd4ba0 100644 --- a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/HexagonalPlane.java +++ b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/HexagonalPlane.java @@ -12,7 +12,7 @@ import static bomb.tools.number.MathUtils.isAnInteger; import static java.util.Arrays.stream; -public class HexagonalPlane implements Iterable>, Rotatable { +public final class HexagonalPlane implements Iterable>, Rotatable { public static final IntUnaryOperator CALCULATE_SPAN, NODAL_SIDE_LENGTH; private final int sideLength; @@ -111,7 +111,7 @@ public int getSpan() { } @Override - public Iterator> iterator() { + public @NotNull Iterator> iterator() { return hexagon.iterator(); } diff --git a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/Maze.java b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/Maze.java index 84ae1994..bc206487 100644 --- a/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/Maze.java +++ b/src/main/java/bomb/modules/dh/hexamaze/hexalgorithm/storage/Maze.java @@ -2,7 +2,7 @@ import bomb.modules.dh.hexamaze.hexalgorithm.factory.MazeFactory; -public class Maze extends AbstractHexagon { +public final class Maze extends AbstractHexagon { private static final int FULL_MAZE_SIDE_LENGTH = 12; public Maze() throws IllegalArgumentException, IllegalStateException { diff --git a/src/main/java/bomb/modules/il/ice/cream/IceCream.java b/src/main/java/bomb/modules/il/ice/cream/IceCream.java index 2a227cf7..fb64db72 100644 --- a/src/main/java/bomb/modules/il/ice/cream/IceCream.java +++ b/src/main/java/bomb/modules/il/ice/cream/IceCream.java @@ -32,7 +32,7 @@ public final class IceCream extends Widget { EnumMap> personMap = Person.getPersonAllergens(createIndexFromSerialCode()); EnumSet personAllergens = personMap.get(person); possibleFlavors.removeIf(flavor -> - doesFlavorHaveAllergens(flavor.getAllergens(), personAllergens)); + intersectionIsNotEmpty(flavor.getAllergens(), personAllergens)); if (possibleFlavors.isEmpty()) return VANILLA; if (possibleFlavors.size() == 1) return possibleFlavors.iterator().next(); @@ -49,13 +49,10 @@ private static int createIndexFromSerialCode() { return (serialCode.charAt(5) - '0') / 2; } - private static boolean doesFlavorHaveAllergens(EnumSet flavorAllergens, - EnumSet personAllergens) { - for (Allergen personAllergen : personAllergens) { - if (personAllergen.test(flavorAllergens)) - return true; - } - return false; + private static boolean intersectionIsNotEmpty(EnumSet flavorAllergens, + EnumSet personAllergens) { + return personAllergens.stream() + .anyMatch(personAllergen -> personAllergen.test(flavorAllergens)); } private static List createPopularFlavorList(boolean hasEmptyPortPlate) { diff --git a/src/main/java/bomb/modules/il/logic/Logic.java b/src/main/java/bomb/modules/il/logic/Logic.java index cfe0147d..7fafa52a 100644 --- a/src/main/java/bomb/modules/il/logic/Logic.java +++ b/src/main/java/bomb/modules/il/logic/Logic.java @@ -25,7 +25,7 @@ public static boolean solve(@NotNull LetterRecord @NotNull[] letters, validateInput(letterList, operators); if (!isPriorityOnFirstTwo) { - LetterRecord lastRecord = letterList.remove(0); + LetterRecord lastRecord = letterList.removeFirst(); letterList.add(lastRecord); Collections.reverse(operators); @@ -38,8 +38,8 @@ public static boolean solve(@NotNull LetterRecord @NotNull[] letters, } } - boolean firstHalf = operators.get(0).test( - letterList.get(0).getBooleanValue(), + boolean firstHalf = operators.getFirst().test( + letterList.getFirst().getBooleanValue(), letterList.get(1).getBooleanValue() ); diff --git a/src/main/java/bomb/modules/il/logic/LogicController.java b/src/main/java/bomb/modules/il/logic/LogicController.java index 9cad301b..e1fed81a 100644 --- a/src/main/java/bomb/modules/il/logic/LogicController.java +++ b/src/main/java/bomb/modules/il/logic/LogicController.java @@ -94,7 +94,7 @@ private void updateDisabledState() { boolean areAllStringsFilled = Stream .concat(textFieldsStrings, comboBoxStrings) .map(text -> !text.isEmpty()) - .reduce((b1, b2) -> b1 && b2) + .reduce(AND::test) .orElse(false); submitButton.setDisable(!areAllStringsFilled); diff --git a/src/main/java/bomb/modules/m/morsematics/Morsematics.java b/src/main/java/bomb/modules/m/morsematics/Morsematics.java index 42e262bb..e92ac2b6 100644 --- a/src/main/java/bomb/modules/m/morsematics/Morsematics.java +++ b/src/main/java/bomb/modules/m/morsematics/Morsematics.java @@ -108,7 +108,7 @@ private static void executeFirstRule(ArrayRing firstLetter, ArrayRing indicatorSet, ArrayRing letterRing, List inputLetters) { - if (indicatorSet.size() == 0) return; + if (indicatorSet.isEmpty()) return; indicatorSet.stream() .map(Enum::name) diff --git a/src/main/java/bomb/modules/m/murder/LocationMapFactory.java b/src/main/java/bomb/modules/m/murder/LocationMapFactory.java index afe9120d..2f96d27a 100644 --- a/src/main/java/bomb/modules/m/murder/LocationMapFactory.java +++ b/src/main/java/bomb/modules/m/murder/LocationMapFactory.java @@ -14,7 +14,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; @SuppressWarnings("ConstantConditions") -public class LocationMapFactory { +public final class LocationMapFactory { private static final String FILENAME = "location_list.csv"; static Pair>, EnumMap>> createMaps() diff --git a/src/main/java/bomb/modules/np/neutralization/Neutralization.java b/src/main/java/bomb/modules/np/neutralization/Neutralization.java index ff424341..a30fdd76 100644 --- a/src/main/java/bomb/modules/np/neutralization/Neutralization.java +++ b/src/main/java/bomb/modules/np/neutralization/Neutralization.java @@ -137,13 +137,13 @@ private static double acidConcentration(int acidVol) { //Length of characters for the anion is equal to the cation symbol length concentrate *= 3; concentrate = Math.abs(concentrate) % 10; - if (concentrate == 0) + if (concentrate == 0.0) concentrate = acidVol * 2 / 5.0; return concentrate / 10.0; } private static int baseConcentration() { - if (overrule()) return 20; + if (isProcessOverruled()) return 20; int allIndicators = countIndicators(ALL_PRESENT); int portTypeCount = countPortTypes(); @@ -153,17 +153,15 @@ else if (portTypeCount > numHolders && portTypeCount > allIndicators) return 10; else if (allIndicators > numHolders && allIndicators > portTypeCount) return 20; - return closestNum(); + return getClosestDistance(); } - private static boolean overrule() { - return (currentAcid == HYDROIODIC_ACID && - currentBase == POTASSIUM_HYDROXIDE) || - (currentAcid == HYDROCHORIC_ACID && - currentBase == AMMONIA); + private static boolean isProcessOverruled() { + return (currentAcid == HYDROIODIC_ACID && currentBase == POTASSIUM_HYDROXIDE) || + (currentAcid == HYDROCHORIC_ACID && currentBase == AMMONIA); } - private static int closestNum() { + private static int getClosestDistance() { int atomicNum = currentBase.getAtomicNum(); int holderDistance = Math.abs(atomicNum - 5), portDistance = Math.abs(atomicNum - 10), diff --git a/src/main/java/bomb/modules/s/shape/shift/ShapeShift.java b/src/main/java/bomb/modules/s/shape/shift/ShapeShift.java index 8c860967..e1e5aa5d 100644 --- a/src/main/java/bomb/modules/s/shape/shift/ShapeShift.java +++ b/src/main/java/bomb/modules/s/shape/shift/ShapeShift.java @@ -6,7 +6,7 @@ import org.javatuples.Pair; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import static bomb.enumerations.Indicator.BOB; @@ -51,32 +51,27 @@ private static void initializeGraph() { } private static List> createList() { - List> list = new ArrayList<>(); - for (ShapeEnd left : SHAPE_END_ARRAY) { - for (ShapeEnd right : SHAPE_END_ARRAY) - list.add(new Pair<>(left, right)); - } - return list; + return Arrays.stream(SHAPE_END_ARRAY) + .flatMap(leftEnd -> Arrays.stream(SHAPE_END_ARRAY) + .map(rightEnd -> new Pair<>(leftEnd, rightEnd)) + ) + .toList(); } private static void initializeTriples() { List> list = createList(); - initializePairs(list.get(0), list.get(8), list.get(15)); - initializePairs(list.get(1), list.get(10), list.get(15)); - initializePairs(list.get(2), list.get(3), list.get(0)); - initializePairs(list.get(3), list.get(11), list.get(14)); - initializePairs(list.get(4), list.get(2), list.get(9)); - initializePairs(list.get(5), list.get(10), list.get(7)); - initializePairs(list.get(6), list.get(3), list.get(12)); - initializePairs(list.get(7), list.get(0), list.get(1)); - initializePairs(list.get(8), list.get(13), list.get(1)); - initializePairs(list.get(9), list.get(6), list.get(13)); - initializePairs(list.get(10), list.get(4), list.get(6)); - initializePairs(list.get(11), list.get(5), list.get(12)); - initializePairs(list.get(12), list.get(2), list.get(9)); - initializePairs(list.get(13), list.get(4), list.get(7)); - initializePairs(list.get(14), list.get(5), list.get(8)); - initializePairs(list.get(15), list.get(11), list.get(14)); + + int[][] elementIndexTriples = { + {0, 8, 15}, {1, 10, 15}, {2, 3, 0}, + {3, 11, 14}, {4, 2, 9}, {5, 10, 7}, + {6, 3, 12}, {7, 0, 1}, {8, 13, 1}, + {9, 6, 13}, {10, 4, 6}, {11, 5, 12}, + {12, 2, 9}, {13, 4, 7}, {14, 5, 8}, + {15, 11, 14} + }; + + Arrays.stream(elementIndexTriples) + .forEach(triple -> initializePairs(list.get(triple[0]), list.get(triple[1]), list.get(triple[2]))); } private static void initializePairs(Pair firstPair, diff --git a/src/main/java/bomb/modules/s/simon/states/SimonStates.java b/src/main/java/bomb/modules/s/simon/states/SimonStates.java index 42a842b3..c30d30b5 100644 --- a/src/main/java/bomb/modules/s/simon/states/SimonStates.java +++ b/src/main/java/bomb/modules/s/simon/states/SimonStates.java @@ -9,6 +9,7 @@ import java.util.AbstractCollection; import java.util.ArrayList; import java.util.EnumSet; +import java.util.HashSet; import java.util.List; import java.util.function.BiPredicate; import java.util.stream.Stream; @@ -18,6 +19,7 @@ import static bomb.modules.s.simon.SimonColors.StateColor.RED; import static bomb.modules.s.simon.SimonColors.StateColor.YELLOW; import static bomb.modules.s.simon.states.StageState.FIRST; +import static bomb.tools.logic.LogicOperator.OR; import static bomb.tools.string.StringFormat.FIRST_LETTER_CAPITAL; import static java.util.Arrays.asList; import static java.util.Collections.reverse; @@ -103,7 +105,7 @@ private static StateColor getStageTwo(EnumSet colorsFlashed) { getFirstInOrder(DID_NOT_FLASH, colorsFlashed, getLowestPriorityOrder()); case 3 -> getFirstInOrder(DID_NOT_FLASH, colorsFlashed, getHighestPriorityOrder()); - default -> PRESSED_COLOR_HISTORY.get(0); + default -> PRESSED_COLOR_HISTORY.getFirst(); }; } @@ -111,9 +113,9 @@ private static StateColor getStageThree(EnumSet colorsFlashed) { return switch (colorsFlashed.size()) { case 1 -> getColorFlashed(colorsFlashed); - case 2 -> PRESSED_COLOR_HISTORY.containsAll(colorsFlashed) ? + case 2 -> new HashSet<>(PRESSED_COLOR_HISTORY).containsAll(colorsFlashed) ? getFirstInOrder(DID_NOT_FLASH, colorsFlashed, getLowestPriorityOrder()) : - PRESSED_COLOR_HISTORY.get(0); + PRESSED_COLOR_HISTORY.getFirst(); case 3 -> historyContainsAnyFlashed(colorsFlashed) ? getFirstInOrder(//Get the highest priority that has flash and not been pressed @@ -129,7 +131,7 @@ private static StateColor getStageThree(EnumSet colorsFlashed) { private static boolean historyContainsAnyFlashed(EnumSet colorsFlashed) { return PRESSED_COLOR_HISTORY.stream() .map(colorsFlashed::contains) - .reduce((boolOne, boolTwo) -> boolOne || boolTwo) + .reduce(OR::test) .orElse(false); } @@ -141,10 +143,14 @@ private static StateColor getStageFour(EnumSet colorsFlashed) { int size = colorsFlashed.size(); if (size == 3) { - Stream stream = colorsFlashed.stream() - .filter(color -> !PRESSED_COLOR_HISTORY.contains(color)); - if (stream.count() == 1) - return stream.findFirst().orElseThrow(IllegalStateException::new); + long colorsRemaining = colorsFlashed.stream() + .filter(color -> !PRESSED_COLOR_HISTORY.contains(color)) + .count(); + if (colorsRemaining == 1) + return colorsFlashed.stream() + .filter(color -> !PRESSED_COLOR_HISTORY.contains(color)) + .findFirst() + .orElseThrow(IllegalStateException::new); } if (size >= 3) diff --git a/src/main/java/bomb/modules/s/souvenir/Souvenir.java b/src/main/java/bomb/modules/s/souvenir/Souvenir.java index 67d1af2a..fd96a354 100644 --- a/src/main/java/bomb/modules/s/souvenir/Souvenir.java +++ b/src/main/java/bomb/modules/s/souvenir/Souvenir.java @@ -4,6 +4,8 @@ import bomb.annotation.DisplayComponent; import org.javatuples.Pair; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.LinkedHashMap; import java.util.List; @@ -11,6 +13,7 @@ @DisplayComponent(resource = "souvenir.fxml", buttonLinkerName = "Souvenir") public final class Souvenir extends Widget { + private static final Logger LOG = LoggerFactory.getLogger(Souvenir.class); private static final Map MODULE_ARTIFACTS; static { @@ -18,6 +21,11 @@ public final class Souvenir extends Widget { } public static void addRelic(String key, String answer) { + if (MODULE_ARTIFACTS.containsKey(key)) { + LOG.debug("Souvenir Artifact Replaced: {} - {}", key, answer); + } else { + LOG.debug("Souvenir Artifact: {} - {}", key, answer); + } MODULE_ARTIFACTS.put(key, answer); } diff --git a/src/main/java/bomb/modules/s/square/button/SquareButton.java b/src/main/java/bomb/modules/s/square/button/SquareButton.java index d2872c02..72a63236 100644 --- a/src/main/java/bomb/modules/s/square/button/SquareButton.java +++ b/src/main/java/bomb/modules/s/square/button/SquareButton.java @@ -9,7 +9,6 @@ import java.util.Arrays; import java.util.List; import java.util.Set; -import java.util.TreeSet; import java.util.function.IntPredicate; import java.util.stream.IntStream; @@ -20,7 +19,6 @@ import static bomb.tools.filter.RegexFilter.NUMBER_PATTERN; import static bomb.tools.filter.RegexFilter.filter; import static bomb.tools.string.StringFormat.FIRST_LETTER_CAPITAL; -import static java.util.Arrays.asList; import static java.util.stream.Collectors.joining; @DisplayComponent(resource = "square_button.fxml", buttonLinkerName = "Square Button") @@ -38,7 +36,7 @@ public final class SquareButton extends Widget { DARK_GRAY = CYAN = 2; WHITE = 3; - COLOR_WORDS = new TreeSet<>(asList("Purple", "Indigo", "Maroon", "Jade")); + COLOR_WORDS = Set.of("Purple", "Indigo", "Maroon", "Jade"); } public static @NotNull String solve(int buttonColor, @NotNull String buttonText) throws IllegalArgumentException { diff --git a/src/main/java/bomb/modules/s/switches/Switches.java b/src/main/java/bomb/modules/s/switches/Switches.java index 7975e0ee..756e4b91 100644 --- a/src/main/java/bomb/modules/s/switches/Switches.java +++ b/src/main/java/bomb/modules/s/switches/Switches.java @@ -8,7 +8,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -23,7 +22,7 @@ public sealed class Switches extends Widget permits ColoredSwitches { static { Byte[] forbiddenMoveSource = {4, 11, 15, 18, 19, 23, 24, 26, 28, 30}; - FORBIDDEN_MOVES = new HashSet<>(asList(forbiddenMoveSource)); + FORBIDDEN_MOVES = Set.of(forbiddenMoveSource); SPECIAL_CONDITIONS = new HashMap<>(); setUpSpecialConditions(); } diff --git a/src/main/java/bomb/modules/t/the/bulb/Bulb.java b/src/main/java/bomb/modules/t/the/bulb/BulbModel.java similarity index 96% rename from src/main/java/bomb/modules/t/the/bulb/Bulb.java rename to src/main/java/bomb/modules/t/the/bulb/BulbModel.java index 11c4f861..0d6ff845 100644 --- a/src/main/java/bomb/modules/t/the/bulb/Bulb.java +++ b/src/main/java/bomb/modules/t/the/bulb/BulbModel.java @@ -1,6 +1,6 @@ package bomb.modules.t.the.bulb; -public class Bulb { +public final class BulbModel { private Color color; private Light light; private Opacity opacity; diff --git a/src/main/java/bomb/modules/t/the/bulb/TheBulb.java b/src/main/java/bomb/modules/t/the/bulb/TheBulb.java index 84c7c011..e0e0cb2a 100644 --- a/src/main/java/bomb/modules/t/the/bulb/TheBulb.java +++ b/src/main/java/bomb/modules/t/the/bulb/TheBulb.java @@ -36,10 +36,10 @@ public final class TheBulb extends Widget { private static boolean isLightOffAtStepOne; private static Indicator rememberedIndicator = null; - public static @NotNull List solve(@NotNull Bulb bulb) { - validateBulb(bulb); + public static @NotNull List solve(@NotNull BulbModel bulbModel) { + validateBulb(bulbModel); List outputList = new ArrayList<>(); - stepOne(bulb, outputList); + stepOne(bulbModel, outputList); if (isSouvenirActive) sendInfoToSouvenir(outputList); @@ -48,80 +48,80 @@ public final class TheBulb extends Widget { return outputList; } - private static void stepOne(Bulb bulb, List outputList) { - if (bulb.getLight() == Bulb.Light.OFF) { - unscrewBulb(bulb, outputList); - stepFour(bulb, outputList); + private static void stepOne(BulbModel bulbModel, List outputList) { + if (bulbModel.getLight() == BulbModel.Light.OFF) { + unscrewBulb(bulbModel, outputList); + stepFour(bulbModel, outputList); return; } - if (bulb.getOpacity() == Bulb.Opacity.TRANSLUCENT) { + if (bulbModel.getOpacity() == BulbModel.Opacity.TRANSLUCENT) { outputList.add(PRESS_I); - if (bulb.getColor() == Bulb.Color.WHITE) - checkIfLightTurnsOff(bulb, outputList); + if (bulbModel.getColor() == BulbModel.Color.WHITE) + checkIfLightTurnsOff(bulbModel, outputList); - stepTwo(bulb, outputList); + stepTwo(bulbModel, outputList); return; } outputList.add(PRESS_O); - stepThree(bulb, outputList); + stepThree(bulbModel, outputList); } - private static void stepTwo(Bulb bulb, List outputList) { - if (bulb.getColor() == Bulb.Color.RED) { - checkIfLightTurnsOff(bulb, outputList); + private static void stepTwo(BulbModel bulbModel, List outputList) { + if (bulbModel.getColor() == BulbModel.Color.RED) { + checkIfLightTurnsOff(bulbModel, outputList); outputList.add(PRESS_I); - unscrewBulb(bulb, outputList); - stepFive(bulb, outputList); + unscrewBulb(bulbModel, outputList); + stepFive(bulbModel, outputList); return; } - if (bulb.getColor() == Bulb.Color.WHITE) { + if (bulbModel.getColor() == BulbModel.Color.WHITE) { outputList.add(PRESS_O); - unscrewBulb(bulb, outputList); - stepSix(bulb, outputList); + unscrewBulb(bulbModel, outputList); + stepSix(bulbModel, outputList); return; } - unscrewBulb(bulb, outputList); - stepSeven(bulb, outputList); + unscrewBulb(bulbModel, outputList); + stepSeven(bulbModel, outputList); } - private static void stepThree(Bulb bulb, List outputList) { - if (bulb.getColor() == Bulb.Color.GREEN) { + private static void stepThree(BulbModel bulbModel, List outputList) { + if (bulbModel.getColor() == BulbModel.Color.GREEN) { outputList.add(PRESS_I); - checkIfLightTurnsOff(bulb, outputList); - unscrewBulb(bulb, outputList); - stepSix(bulb, outputList); + checkIfLightTurnsOff(bulbModel, outputList); + unscrewBulb(bulbModel, outputList); + stepSix(bulbModel, outputList); return; } - if (bulb.getColor() == Bulb.Color.PURPLE) { - checkIfLightTurnsOff(bulb, outputList); + if (bulbModel.getColor() == BulbModel.Color.PURPLE) { + checkIfLightTurnsOff(bulbModel, outputList); outputList.add(PRESS_O); - unscrewBulb(bulb, outputList); - stepFive(bulb, outputList); + unscrewBulb(bulbModel, outputList); + stepFive(bulbModel, outputList); return; } - unscrewBulb(bulb, outputList); - stepEight(bulb, outputList); + unscrewBulb(bulbModel, outputList); + stepEight(bulbModel, outputList); } - private static void stepFour(Bulb bulb, List outputList) { + private static void stepFour(BulbModel bulbModel, List outputList) { if (hasFollowingIndicators(CAR, IND, MSA, SND)) { outputList.add(PRESS_I); - stepNine(bulb, outputList); + stepNine(bulbModel, outputList); return; } outputList.add(PRESS_O); - stepTen(bulb, outputList); + stepTen(bulbModel, outputList); } - private static void stepFive(Bulb bulb, List outputList) { + private static void stepFive(BulbModel bulbModel, List outputList) { String previousPress = outputList.get(outputList.size() - 2); if (isLightOffAtStepOne) { @@ -131,173 +131,173 @@ private static void stepFive(Bulb bulb, List outputList) { outputList.add(wasPreviousPressI ? PRESS_O : PRESS_I); } - screwBulb(bulb, outputList); + screwBulb(bulbModel, outputList); } - private static void stepSix(Bulb bulb, List outputList) { + private static void stepSix(BulbModel bulbModel, List outputList) { String firstButtonPress = outputList.get(0); String lastButtonPress = outputList.get(outputList.size() - 2); outputList.add(isLightOffAtStepOne ? firstButtonPress : lastButtonPress); - screwBulb(bulb, outputList); + screwBulb(bulbModel, outputList); } - private static void stepSeven(Bulb bulb, List outputList) { - if (bulb.getColor() == Bulb.Color.GREEN) { + private static void stepSeven(BulbModel bulbModel, List outputList) { + if (bulbModel.getColor() == BulbModel.Color.GREEN) { rememberedIndicator = SIG; outputList.add(PRESS_I); - stepEleven(bulb, outputList); + stepEleven(bulbModel, outputList); return; } - if (bulb.getColor() == Bulb.Color.PURPLE) { + if (bulbModel.getColor() == BulbModel.Color.PURPLE) { outputList.add(PRESS_I); - screwBulb(bulb, outputList); - stepTwelve(bulb, outputList); + screwBulb(bulbModel, outputList); + stepTwelve(bulbModel, outputList); return; } - if (bulb.getColor() == Bulb.Color.BLUE) { + if (bulbModel.getColor() == BulbModel.Color.BLUE) { rememberedIndicator = CLR; outputList.add(PRESS_O); - stepEleven(bulb, outputList); + stepEleven(bulbModel, outputList); return; } outputList.add(PRESS_O); - screwBulb(bulb, outputList); - stepThirteen(bulb, outputList); + screwBulb(bulbModel, outputList); + stepThirteen(bulbModel, outputList); } - private static void stepEight(Bulb bulb, List outputList) { - if (bulb.getColor() == Bulb.Color.WHITE) { + private static void stepEight(BulbModel bulbModel, List outputList) { + if (bulbModel.getColor() == BulbModel.Color.WHITE) { rememberedIndicator = FRQ; outputList.add(PRESS_I); - stepEleven(bulb, outputList); + stepEleven(bulbModel, outputList); return; } - if (bulb.getColor() == Bulb.Color.RED) { + if (bulbModel.getColor() == BulbModel.Color.RED) { outputList.add(PRESS_I); - screwBulb(bulb, outputList); - stepThirteen(bulb, outputList); + screwBulb(bulbModel, outputList); + stepThirteen(bulbModel, outputList); return; } - if (bulb.getColor() == Bulb.Color.YELLOW) { + if (bulbModel.getColor() == BulbModel.Color.YELLOW) { rememberedIndicator = FRK; outputList.add(PRESS_O); - stepEleven(bulb, outputList); + stepEleven(bulbModel, outputList); return; } outputList.add(PRESS_O); - screwBulb(bulb, outputList); - stepTwelve(bulb, outputList); + screwBulb(bulbModel, outputList); + stepTwelve(bulbModel, outputList); } - private static void stepNine(Bulb bulb, List outputList) { - if (bulb.getColor() == Bulb.Color.BLUE) { + private static void stepNine(BulbModel bulbModel, List outputList) { + if (bulbModel.getColor() == BulbModel.Color.BLUE) { outputList.add(PRESS_I); - stepFourteen(bulb, outputList); + stepFourteen(bulbModel, outputList); return; } - if (bulb.getColor() == Bulb.Color.GREEN) { + if (bulbModel.getColor() == BulbModel.Color.GREEN) { outputList.add(PRESS_I); - screwBulb(bulb, outputList); - stepTwelve(bulb, outputList); + screwBulb(bulbModel, outputList); + stepTwelve(bulbModel, outputList); return; } - if (bulb.getColor() == Bulb.Color.YELLOW) { + if (bulbModel.getColor() == BulbModel.Color.YELLOW) { outputList.add(PRESS_O); - stepFifteen(bulb, outputList); + stepFifteen(bulbModel, outputList); return; } - if (bulb.getColor() == Bulb.Color.WHITE) { + if (bulbModel.getColor() == BulbModel.Color.WHITE) { outputList.add(PRESS_O); - screwBulb(bulb, outputList); - stepThirteen(bulb, outputList); + screwBulb(bulbModel, outputList); + stepThirteen(bulbModel, outputList); return; } - if (bulb.getColor() == Bulb.Color.PURPLE) { - screwBulb(bulb, outputList); + if (bulbModel.getColor() == BulbModel.Color.PURPLE) { + screwBulb(bulbModel, outputList); outputList.add(PRESS_I); - stepTwelve(bulb, outputList); + stepTwelve(bulbModel, outputList); return; } - screwBulb(bulb, outputList); + screwBulb(bulbModel, outputList); outputList.add(PRESS_O); - stepThirteen(bulb, outputList); + stepThirteen(bulbModel, outputList); } - private static void stepTen(Bulb bulb, List outputList) { - if (bulb.getColor() == Bulb.Color.PURPLE) { + private static void stepTen(BulbModel bulbModel, List outputList) { + if (bulbModel.getColor() == BulbModel.Color.PURPLE) { outputList.add(PRESS_I); - stepFourteen(bulb, outputList); + stepFourteen(bulbModel, outputList); return; } - if (bulb.getColor() == Bulb.Color.RED) { + if (bulbModel.getColor() == BulbModel.Color.RED) { outputList.add(PRESS_I); - screwBulb(bulb, outputList); - stepThirteen(bulb, outputList); + screwBulb(bulbModel, outputList); + stepThirteen(bulbModel, outputList); return; } - if (bulb.getColor() == Bulb.Color.BLUE) { + if (bulbModel.getColor() == BulbModel.Color.BLUE) { outputList.add(PRESS_O); - stepFifteen(bulb, outputList); + stepFifteen(bulbModel, outputList); return; } - if (bulb.getColor() == Bulb.Color.YELLOW) { + if (bulbModel.getColor() == BulbModel.Color.YELLOW) { outputList.add(PRESS_O); - screwBulb(bulb, outputList); - stepTwelve(bulb, outputList); + screwBulb(bulbModel, outputList); + stepTwelve(bulbModel, outputList); return; } - if (bulb.getColor() == Bulb.Color.GREEN) { - screwBulb(bulb, outputList); + if (bulbModel.getColor() == BulbModel.Color.GREEN) { + screwBulb(bulbModel, outputList); outputList.add(PRESS_I); - stepThirteen(bulb, outputList); + stepThirteen(bulbModel, outputList); return; } - screwBulb(bulb, outputList); + screwBulb(bulbModel, outputList); outputList.add(PRESS_O); - stepTwelve(bulb, outputList); + stepTwelve(bulbModel, outputList); } - private static void stepEleven(Bulb bulb, List outputList) { + private static void stepEleven(BulbModel bulbModel, List outputList) { boolean hasRememberedIndicator = rememberedIndicator != null && hasIndicator(rememberedIndicator); outputList.add(hasRememberedIndicator ? PRESS_I : PRESS_O); - screwBulb(bulb, outputList); + screwBulb(bulbModel, outputList); } - private static void stepTwelve(Bulb bulb, List outputList) { - boolean isLightOn = confirmLightIsOn(bulb, outputList); + private static void stepTwelve(BulbModel bulbModel, List outputList) { + boolean isLightOn = confirmLightIsOn(bulbModel, outputList); outputList.add(isLightOn ? PRESS_I : PRESS_O); } - private static void stepThirteen(Bulb bulb, List outputList) { - boolean isLightOn = confirmLightIsOn(bulb, outputList); + private static void stepThirteen(BulbModel bulbModel, List outputList) { + boolean isLightOn = confirmLightIsOn(bulbModel, outputList); outputList.add(isLightOn ? PRESS_O : PRESS_I); } - private static void stepFourteen(Bulb bulb, List outputList) { - outputList.add(bulb.getOpacity() == Bulb.Opacity.OPAQUE ? PRESS_I : PRESS_O); - screwBulb(bulb, outputList); + private static void stepFourteen(BulbModel bulbModel, List outputList) { + outputList.add(bulbModel.getOpacity() == BulbModel.Opacity.OPAQUE ? PRESS_I : PRESS_O); + screwBulb(bulbModel, outputList); } - private static void stepFifteen(Bulb bulb, List outputList) { - outputList.add(bulb.getOpacity() == Bulb.Opacity.TRANSLUCENT ? PRESS_I : PRESS_O); - screwBulb(bulb, outputList); + private static void stepFifteen(BulbModel bulbModel, List outputList) { + outputList.add(bulbModel.getOpacity() == BulbModel.Opacity.TRANSLUCENT ? PRESS_I : PRESS_O); + screwBulb(bulbModel, outputList); } private static void sendInfoToSouvenir(List outputList) { @@ -307,7 +307,7 @@ private static void sendInfoToSouvenir(List outputList) { Souvenir.addRelic("The Bulb button presses", outputText); } - private static void checkIfLightTurnsOff(Bulb bulb, List outputList) { + private static void checkIfLightTurnsOff(BulbModel bulbModel, List outputList) { Alert alert = new Alert(Alert.AlertType.CONFIRMATION); alert.setTitle("Light Confirmation"); alert.setContentText("Did the Bulb turn off when you pressed I?"); @@ -325,10 +325,10 @@ private static void checkIfLightTurnsOff(Bulb bulb, List outputList) { options.ifPresent(buttonType -> isLightOffAtStepOne = buttonType == yes); - bulb.setLight(isLightOffAtStepOne ? Bulb.Light.OFF : Bulb.Light.ON); + bulbModel.setLight(isLightOffAtStepOne ? BulbModel.Light.OFF : BulbModel.Light.ON); } - private static boolean confirmLightIsOn(Bulb bulb, List outputList) throws IllegalStateException { + private static boolean confirmLightIsOn(BulbModel bulbModel, List outputList) throws IllegalStateException { Alert alert = new Alert(Alert.AlertType.CONFIRMATION); alert.setTitle("Light Confirmation"); alert.setContentText("Is the bulb now on or off?"); @@ -348,28 +348,28 @@ private static boolean confirmLightIsOn(Bulb bulb, List outputList) thro throw new IllegalStateException("Unexpected state"); boolean isLightOn = options.get() == on; - bulb.setLight(isLightOn ? Bulb.Light.ON : Bulb.Light.OFF); + bulbModel.setLight(isLightOn ? BulbModel.Light.ON : BulbModel.Light.OFF); return isLightOn; } - private static void unscrewBulb(Bulb theBulb, List list) throws IllegalStateException { - theBulb.setPosition(Bulb.Position.UNSCREWED); + private static void unscrewBulb(BulbModel theBulbModel, List list) throws IllegalStateException { + theBulbModel.setPosition(BulbModel.Position.UNSCREWED); list.add(UNSCREW); } - private static void screwBulb(Bulb theBulb, List list) throws IllegalStateException { - theBulb.setPosition(Bulb.Position.SCREWED); + private static void screwBulb(BulbModel theBulbModel, List list) throws IllegalStateException { + theBulbModel.setPosition(BulbModel.Position.SCREWED); list.add(SCREW); } - private static void validateBulb(Bulb bulb) throws IllegalArgumentException { - if (bulb.getPosition() != Bulb.Position.SCREWED) + private static void validateBulb(BulbModel bulbModel) throws IllegalArgumentException { + if (bulbModel.getPosition() != BulbModel.Position.SCREWED) throw new IllegalArgumentException("Bulb must be screwed in at the start"); - if (bulb.getLight() == null) + if (bulbModel.getLight() == null) throw new IllegalArgumentException("Bulb must be lit or unlit"); - if (bulb.getColor() == null) + if (bulbModel.getColor() == null) throw new IllegalArgumentException("Bulb must have a color"); - if (bulb.getOpacity() == null) + if (bulbModel.getOpacity() == null) throw new IllegalArgumentException("Bulb must be Opaque or Translucent"); } } diff --git a/src/main/java/bomb/modules/t/the/bulb/TheBulbController.java b/src/main/java/bomb/modules/t/the/bulb/TheBulbController.java index beab992e..d806382d 100644 --- a/src/main/java/bomb/modules/t/the/bulb/TheBulbController.java +++ b/src/main/java/bomb/modules/t/the/bulb/TheBulbController.java @@ -9,17 +9,17 @@ import java.util.List; -import static bomb.modules.t.the.bulb.Bulb.Color.BLUE; -import static bomb.modules.t.the.bulb.Bulb.Color.GREEN; -import static bomb.modules.t.the.bulb.Bulb.Color.PURPLE; -import static bomb.modules.t.the.bulb.Bulb.Color.RED; -import static bomb.modules.t.the.bulb.Bulb.Color.WHITE; -import static bomb.modules.t.the.bulb.Bulb.Color.YELLOW; -import static bomb.modules.t.the.bulb.Bulb.Light.OFF; -import static bomb.modules.t.the.bulb.Bulb.Light.ON; -import static bomb.modules.t.the.bulb.Bulb.Opacity.OPAQUE; -import static bomb.modules.t.the.bulb.Bulb.Opacity.TRANSLUCENT; -import static bomb.modules.t.the.bulb.Bulb.Position.SCREWED; +import static bomb.modules.t.the.bulb.BulbModel.Color.BLUE; +import static bomb.modules.t.the.bulb.BulbModel.Color.GREEN; +import static bomb.modules.t.the.bulb.BulbModel.Color.PURPLE; +import static bomb.modules.t.the.bulb.BulbModel.Color.RED; +import static bomb.modules.t.the.bulb.BulbModel.Color.WHITE; +import static bomb.modules.t.the.bulb.BulbModel.Color.YELLOW; +import static bomb.modules.t.the.bulb.BulbModel.Light.OFF; +import static bomb.modules.t.the.bulb.BulbModel.Light.ON; +import static bomb.modules.t.the.bulb.BulbModel.Opacity.OPAQUE; +import static bomb.modules.t.the.bulb.BulbModel.Opacity.TRANSLUCENT; +import static bomb.modules.t.the.bulb.BulbModel.Position.SCREWED; public final class TheBulbController implements Resettable { @FXML @@ -46,7 +46,7 @@ private void enableSubmitButton() { @FXML private void submitBulbInfo() { - Bulb input = new Bulb(); + BulbModel input = new BulbModel(); input.setColor(retrieveColor()); input.setLight(retrieveLuminosity()); input.setOpacity(retrieveOpacity()); @@ -55,7 +55,7 @@ private void submitBulbInfo() { outputToTextArea(TheBulb.solve(input)); } - private Bulb.Color retrieveColor() { + private BulbModel.Color retrieveColor() { String resultingColor = FacadeFX.getToggleName(colorGroup); return switch(resultingColor) { case "Red" -> RED; @@ -67,12 +67,12 @@ private Bulb.Color retrieveColor() { }; } - private Bulb.Light retrieveLuminosity() { + private BulbModel.Light retrieveLuminosity() { String resultingLight = FacadeFX.getToggleName(luminosityGroup); return resultingLight.equals("Lit") ? ON : OFF; } - private Bulb.Opacity retrieveOpacity() { + private BulbModel.Opacity retrieveOpacity() { String resultingOpacity = FacadeFX.getToggleName(opacityGroup); return resultingOpacity.equals("Opaque") ? OPAQUE : TRANSLUCENT; } diff --git a/src/main/java/bomb/tools/boot/FxmlBootDrive.java b/src/main/java/bomb/tools/boot/FxmlBootDrive.java new file mode 100644 index 00000000..a765239b --- /dev/null +++ b/src/main/java/bomb/tools/boot/FxmlBootDrive.java @@ -0,0 +1,119 @@ +package bomb.tools.boot; + +import bomb.Widget; +import bomb.annotation.DisplayComponent; +import bomb.modules.ab.blind.alley.BlindAlley; +import bomb.modules.ab.blind.alley.BlindAlleyController; +import bomb.modules.s.souvenir.Souvenir; +import bomb.modules.s.souvenir.SouvenirController; +import bomb.tools.note.NoteController; +import bomb.tools.pattern.facade.FacadeFX; +import bomb.tools.pattern.observer.BlindAlleyPaneObserver; +import bomb.tools.pattern.observer.ObserverHub; +import bomb.tools.pattern.observer.ResetObserver; +import bomb.tools.pattern.observer.SouvenirPaneObserver; +import javafx.fxml.FXMLLoader; +import javafx.scene.layout.Region; +import org.javatuples.Pair; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URL; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; +import java.util.SequencedMap; + +import static bomb.tools.pattern.observer.ObserverHub.ObserverIndex.BLIND_ALLEY_PANE; +import static bomb.tools.pattern.observer.ObserverHub.ObserverIndex.SOUVENIR_PANE; +import static java.util.Arrays.asList; + +/** + * FxmlBootDrive uses the Adapter Pattern to perform the part of the boot sequence with the highest bottleneck in performance. + * The purpose for this set of classes is to have a way to quickly switch between methodologies for booting the program + * under different operating systems scenarios. As the number of FXML files for the project increases, + * maintaining a short boot time for this program will more become critical. + *

+ * The standard way uses a stream. This stream is sequential on Linux machines and parallel on Windows, + * thus using the thread pool initialized by Java. This means that performance is dependent on how many pooled threads + * can be created by the JVM. + *

+ * The new way uses Java 21's virtual threads so that we can see if there are any promising results in boot time. + * As of Oct 13, 2023 on Windows, the performance is comparable to the standard drive, perhaps slightly faster; however, + * it's not reliable. I.e. some runs will boot with no problem, and other runs will abruptly pause while loading FXML files. + * Reason is unknown, but more testing is required on other machines. + */ +public sealed interface FxmlBootDrive permits StandardFxmlBootDrive, VirtualThreadFxmlBootDrive { + String WIDGET_FILE = extractAssociatedFile(Widget.class); + String SOUVENIR_FILE = extractAssociatedFile(Souvenir.class); + String BLIND_ALLEY_FILE = extractAssociatedFile(BlindAlley.class); + + Logger LOG = LoggerFactory.getLogger(FxmlBootDrive.class); + SequencedMap createFXMLMap(ResetObserver resetObserver); + + @Contract(" -> new") + static @NotNull FxmlBootDrive createStandardDrive() { + return new StandardFxmlBootDrive(); + } + + @Contract(" -> new") + static @NotNull FxmlBootDrive createVirtualDrive() { + return new VirtualThreadFxmlBootDrive(); + } + + static @NotNull List> getAnnotatedClasses() { + var annotatedClasses = new ArrayList<>(List.of(Widget.class, NoteController.class)); + var queue = new ArrayDeque<>(asList(Widget.class.getPermittedSubclasses())); + + Class temp; + while ((temp = queue.poll()) != null) { + Class[] subclasses = temp.getPermittedSubclasses(); + if (subclasses != null) { + queue.addAll(asList(subclasses)); + } + + if (temp.isAnnotationPresent(DisplayComponent.class)) { + annotatedClasses.add(temp); + } + } + return annotatedClasses; + } + + static @NotNull Pair mapClassToRegion(@NotNull Class clazz, ResetObserver resetObserver) { + DisplayComponent annotation = clazz.getAnnotation(DisplayComponent.class); + URL resource = clazz.getResource(annotation.resource()); + String buttonLinkerName = annotation.buttonLinkerName(); + + return new Pair<>( + buttonLinkerName, + loadSingleRegion(new FXMLLoader(resource), resetObserver) + ); + } + + private static Region loadSingleRegion(FXMLLoader loader, ResetObserver resetObserver) + throws IllegalArgumentException { + Region output = FacadeFX.load(loader); + String location = loader.getLocation().toString(); + + if (!location.endsWith(WIDGET_FILE)) resetObserver.addController(loader); + + if (location.endsWith(SOUVENIR_FILE)) loadSouvenirController(loader.getController()); + else if (location.endsWith(BLIND_ALLEY_FILE)) loadBlindAlleyController(loader.getController()); + return output; + } + + private static void loadBlindAlleyController(BlindAlleyController controller) { + ObserverHub.addObserver(BLIND_ALLEY_PANE, new BlindAlleyPaneObserver(controller)); + } + + private static void loadSouvenirController(SouvenirController controller) { + ObserverHub.addObserver(SOUVENIR_PANE, new SouvenirPaneObserver(controller)); + } + + private static String extractAssociatedFile(Class clazz) { + return clazz.getAnnotation(DisplayComponent.class) + .resource(); + } +} diff --git a/src/main/java/bomb/tools/boot/StandardFxmlBootDrive.java b/src/main/java/bomb/tools/boot/StandardFxmlBootDrive.java new file mode 100644 index 00000000..d1693176 --- /dev/null +++ b/src/main/java/bomb/tools/boot/StandardFxmlBootDrive.java @@ -0,0 +1,31 @@ +package bomb.tools.boot; + +import bomb.tools.pattern.observer.ResetObserver; +import javafx.scene.layout.Region; +import org.javatuples.Pair; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.LinkedHashMap; +import java.util.SequencedMap; + +import static java.util.stream.Collectors.toMap; + +public final class StandardFxmlBootDrive implements FxmlBootDrive { + @SuppressWarnings("DataFlowIssue") + @Contract("_ -> new") + @Override + public @NotNull SequencedMap createFXMLMap(ResetObserver resetObserver) { + var displayClassStream = FxmlBootDrive.getAnnotatedClasses().parallelStream(); + + if (System.getProperty("os.name").toLowerCase().contains("linux")) { + displayClassStream = displayClassStream.sequential(); + } + + return new LinkedHashMap<>(displayClassStream + .map(cls -> FxmlBootDrive.mapClassToRegion(cls, resetObserver)) + .collect(toMap(Pair::getValue0, Pair::getValue1))); + } + + StandardFxmlBootDrive(){} +} diff --git a/src/main/java/bomb/tools/boot/VirtualThreadFxmlBootDrive.java b/src/main/java/bomb/tools/boot/VirtualThreadFxmlBootDrive.java new file mode 100644 index 00000000..15a8e7ab --- /dev/null +++ b/src/main/java/bomb/tools/boot/VirtualThreadFxmlBootDrive.java @@ -0,0 +1,47 @@ +package bomb.tools.boot; + +import bomb.tools.pattern.observer.ResetObserver; +import javafx.scene.layout.Region; +import org.javatuples.Pair; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.SequencedMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import static java.util.stream.Collectors.toMap; + +public final class VirtualThreadFxmlBootDrive implements FxmlBootDrive { + private static Pair extractFromFuture(Future> future) { + try { + return future.get(); + } catch (ExecutionException | InterruptedException e) { + LOG.error("", e); + throw new IllegalStateException("Unexpected Boot Exception"); + } + } + + @Contract("_ -> new") + @Override + public @NotNull SequencedMap createFXMLMap(ResetObserver resetObserver) { + var annotatedClasses = FxmlBootDrive.getAnnotatedClasses(); + var virtualThreadList = new ArrayList>>(annotatedClasses.size()); + + try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { + for (var annotatedClass : annotatedClasses) { + var future = executor.submit(() -> FxmlBootDrive.mapClassToRegion(annotatedClass, resetObserver)); + virtualThreadList.add(future); + } + } + + return new LinkedHashMap<>(virtualThreadList.stream() + .map(VirtualThreadFxmlBootDrive::extractFromFuture) + .collect(toMap(Pair::getValue0, Pair::getValue1))); + } + + VirtualThreadFxmlBootDrive(){} +} diff --git a/src/main/java/bomb/tools/data/structures/graph/list/ListGraph.java b/src/main/java/bomb/tools/data/structures/graph/list/ListGraph.java index 92fa3f48..5dfb7851 100644 --- a/src/main/java/bomb/tools/data/structures/graph/list/ListGraph.java +++ b/src/main/java/bomb/tools/data/structures/graph/list/ListGraph.java @@ -2,30 +2,33 @@ import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; +import java.util.SequencedMap; +import java.util.SequencedSet; -public class ListGraph { - private final LinkedHashMap> list; - private final boolean biDirectional; +public final class ListGraph { + private final SequencedMap> list; + private final boolean isBidirectional; - public ListGraph(boolean biDirectional) { - this.biDirectional = biDirectional; + public ListGraph(boolean isBidirectional) { + this.isBidirectional = isBidirectional; list = new LinkedHashMap<>(); } public void addVertex(E vertex) { if (containsVertex(vertex)) return; - list.put(vertex, new ArrayList<>()); + list.put(vertex, new LinkedHashSet<>()); } public void addEdge(E vertex, E edge) { if (!containsVertex(vertex)) addVertex(vertex); - if (biDirectional && !containsVertex(edge)) + if (isBidirectional && !containsVertex(edge)) addVertex(edge); if (isNotDuplicate(vertex, edge)) list.get(vertex).add(edge); - if (biDirectional && isNotDuplicate(edge, vertex)) + if (isBidirectional && isNotDuplicate(edge, vertex)) list.get(edge).add(vertex); } @@ -34,20 +37,20 @@ public boolean containsVertex(E vertex) { } public List getTargetVertices(E vertex) { - return list.get(vertex); + return new ArrayList<>(list.get(vertex)); } private boolean isNotDuplicate(E vertex, E edge) { return !list.get(vertex).contains(edge); } - public List removeVertex(E vertex) { + public SequencedSet removeVertex(E vertex) { if (!containsVertex(vertex)) return null; - if (biDirectional) removeReferences(vertex, list.get(vertex)); + if (isBidirectional) removeReferences(vertex, list.get(vertex)); return list.remove(vertex); } - private void removeReferences(E vertex, List refList) { + private void removeReferences(E vertex, SequencedSet refList) { for (E reference : refList) list.get(reference).remove(vertex); } diff --git a/src/main/java/bomb/tools/data/structures/queue/BufferedQueue.java b/src/main/java/bomb/tools/data/structures/queue/BufferedQueue.java index 02db57c9..2fde5ccc 100644 --- a/src/main/java/bomb/tools/data/structures/queue/BufferedQueue.java +++ b/src/main/java/bomb/tools/data/structures/queue/BufferedQueue.java @@ -1,5 +1,6 @@ package bomb.tools.data.structures.queue; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import java.lang.ref.WeakReference; @@ -14,7 +15,7 @@ import java.util.stream.Stream; @SuppressWarnings({"unchecked", "NullableProblems"}) -public class BufferedQueue implements List, Iterable, RandomAccess { +public final class BufferedQueue implements List, Iterable, RandomAccess { private final int capacity; private final ArrayDeque dataDeque; @@ -39,6 +40,18 @@ public E get(int index) throws ArrayIndexOutOfBoundsException { throw new ArrayIndexOutOfBoundsException(); } + @Contract(pure = true) + @Override + public @NotNull E getFirst() { + return dataDeque.getFirst(); + } + + @Contract(pure = true) + @Override + public @NotNull E getLast() { + return dataDeque.getLast(); + } + public int size() { return dataDeque.size(); } diff --git a/src/main/java/bomb/tools/data/structures/ring/ArrayRing.java b/src/main/java/bomb/tools/data/structures/ring/ArrayRing.java index 2943632b..4c269c9b 100644 --- a/src/main/java/bomb/tools/data/structures/ring/ArrayRing.java +++ b/src/main/java/bomb/tools/data/structures/ring/ArrayRing.java @@ -10,7 +10,7 @@ import static java.util.Arrays.asList; -public class ArrayRing implements Iterable { +public final class ArrayRing implements Iterable { private final List internalStructure; private int headIndex; @@ -29,7 +29,7 @@ public ArrayRing(E @NotNull ... elements) { } public ArrayRing(@NotNull Collection c) { - if (c.size() < 1) + if (c.isEmpty()) throw new IllegalArgumentException(); internalStructure = c instanceof ArrayList ? (List) c : @@ -87,7 +87,7 @@ public void rotateCounterClockwise(int rotations) { } @Override - public Iterator iterator() { + public @NotNull Iterator iterator() { return headIndex == 0 ? internalStructure.iterator() : reorderList().iterator(); diff --git a/src/main/java/bomb/tools/data/structures/trie/Trie.java b/src/main/java/bomb/tools/data/structures/trie/Trie.java index 075e4f92..d2051b27 100644 --- a/src/main/java/bomb/tools/data/structures/trie/Trie.java +++ b/src/main/java/bomb/tools/data/structures/trie/Trie.java @@ -7,11 +7,11 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; +import java.util.SequencedMap; import java.util.Set; import java.util.TreeMap; -public class Trie { +public final class Trie { private final TrieNode root; public Trie() { @@ -117,8 +117,8 @@ public String toString() { return getWordsStartingWith("").toString(); } - private static class TrieNode { - private final Map children; + private static final class TrieNode { + private final SequencedMap children; private boolean isEndOfWord; public TrieNode() { @@ -151,7 +151,7 @@ public boolean hasNoChild() { } public Character firstChild() { - return children.keySet().iterator().next(); + return children.sequencedKeySet().getFirst(); } public void setEndOfWord(boolean isEndOfWord) { diff --git a/src/main/java/bomb/tools/filter/Regex.java b/src/main/java/bomb/tools/filter/Regex.java index 7f66deaa..b672d2cc 100644 --- a/src/main/java/bomb/tools/filter/Regex.java +++ b/src/main/java/bomb/tools/filter/Regex.java @@ -1,6 +1,7 @@ package bomb.tools.filter; import org.intellij.lang.annotations.Language; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -126,8 +127,9 @@ public void reset() { textMatcher.reset(); } + @Contract(pure = true) @Override - public String toString() { + public @NotNull String toString() { return "Regex: " + regPattern.pattern(); } @@ -136,11 +138,12 @@ public Stream stream() { } @Override - public Iterator iterator() { + public @NotNull Iterator iterator() { return RESULT_STREAM.apply(textMatcher).iterator(); } - public Regex appendToPattern(@Language("regexp") @NotNull String pattern) { + @Contract("_ -> new") + public @NotNull Regex appendToPattern(@Language("regexp") @NotNull String pattern) { return new Regex(this.regPattern.pattern() + pattern, regPattern.flags()); } diff --git a/src/main/java/bomb/tools/note/NoteController.java b/src/main/java/bomb/tools/note/NoteController.java index 2f4b851b..37578d0b 100644 --- a/src/main/java/bomb/tools/note/NoteController.java +++ b/src/main/java/bomb/tools/note/NoteController.java @@ -33,8 +33,9 @@ private void addNoteWindow() { @Override public void reset() { - for (int i = extraNotes.size() - 1; i >= 0; i--) - extraNotes.get(i).close(); + for (var notePageController : extraNotes.reversed()) { + notePageController.close(); + } extraNotes.clear(); FacadeFX.clearMultipleTextFields(firstNote, secondNote, thirdNote, fourthNote, fifthNote); } diff --git a/src/main/java/bomb/tools/pattern/observer/ObserverHub.java b/src/main/java/bomb/tools/pattern/observer/ObserverHub.java index c4028c29..518b5c8d 100644 --- a/src/main/java/bomb/tools/pattern/observer/ObserverHub.java +++ b/src/main/java/bomb/tools/pattern/observer/ObserverHub.java @@ -1,24 +1,58 @@ package bomb.tools.pattern.observer; +import bomb.annotation.DisplayComponent; +import bomb.modules.ab.blind.alley.BlindAlley; +import bomb.modules.s.souvenir.Souvenir; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.EnumMap; +import java.util.Map; + +import static bomb.tools.pattern.observer.ObserverHub.ObserverIndex.BLIND_ALLEY_PANE; +import static bomb.tools.pattern.observer.ObserverHub.ObserverIndex.SOUVENIR_PANE; public final class ObserverHub { public enum ObserverIndex { FORGET_ME_NOT_TOGGLE, SOUVENIR_TOGGLE, BLIND_ALLEY_PANE, SOUVENIR_PANE, RESET } + private static final Logger LOG = LoggerFactory.getLogger(ObserverHub.class); private static final EnumMap OBSERVER_MAP = new EnumMap<>(ObserverIndex.class); + private static final Map BUTTON_NAME_MAP = Map.of( + extractButtonLinkerName(BlindAlley.class), BLIND_ALLEY_PANE, + extractButtonLinkerName(Souvenir.class), SOUVENIR_PANE + ); + + private static String extractButtonLinkerName(Class clazz) { + return clazz.getAnnotation(DisplayComponent.class) + .buttonLinkerName(); + } private ObserverHub() { } public static void addObserver(ObserverIndex index, Observer observer) { + if (OBSERVER_MAP.containsKey(index)) { + LOG.warn("{} value will be overwritten", index); + } OBSERVER_MAP.put(index, observer); } public static void updateAtIndex(@NotNull ObserverIndex index) { OBSERVER_MAP.get(index).update(); } + + public static void scanButtonName(@NotNull String buttonName) { + if (BUTTON_NAME_MAP.containsKey(buttonName)) { + updateAtIndex(BUTTON_NAME_MAP.get(buttonName)); + } + } + + public static void ensureMapIsPopulated() { + if (ObserverIndex.values().length != OBSERVER_MAP.size()) { + LOG.warn("Observers not fully populated: {}", OBSERVER_MAP); + } + } } diff --git a/src/main/java/bomb/tools/pattern/observer/ResetObserver.java b/src/main/java/bomb/tools/pattern/observer/ResetObserver.java index d37193d3..6e8beb32 100644 --- a/src/main/java/bomb/tools/pattern/observer/ResetObserver.java +++ b/src/main/java/bomb/tools/pattern/observer/ResetObserver.java @@ -3,11 +3,14 @@ import bomb.abstractions.Resettable; import javafx.fxml.FXMLLoader; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; public final class ResetObserver implements Observer { + private static final Logger LOG = LoggerFactory.getLogger(ResetObserver.class); private final List controllerList; public ResetObserver() { @@ -19,6 +22,7 @@ public void addController(@NotNull FXMLLoader loader) { Object controller = loader.getController(); if (controller == null) return; controllerList.add((Resettable) controller); + LOG.debug("View of ResetObserver: {}", controllerList); } @Override diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 9a4e3da3..0588c4fb 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -5,6 +5,7 @@ requires com.opencsv; requires com.jfoenix; requires org.jgrapht.core; + requires org.slf4j; requires javatuples; requires MaterialFX; requires org.jetbrains.annotations; diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 00000000..f34bcbcd --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,29 @@ + + + + + + %d{yyyy-MM-dd | HH:mm:ss} [%thread] %p | %logger{25} %m%n + utf8 + + + + + logs/gradle-centurion.log + + logs/gradle-centurion-%d{yyyy-MM-dd+HH:mm:ss}.log + 7 + + + %d{yyyy-MM-dd | HH:mm:ss} %p [%t] %c{2} - %m%n + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/bomb/modules/ab/bitwise/BitwiseTest.java b/src/test/java/bomb/modules/ab/bitwise/BitwiseTest.java index 9161d1f7..1f4248d5 100644 --- a/src/test/java/bomb/modules/ab/bitwise/BitwiseTest.java +++ b/src/test/java/bomb/modules/ab/bitwise/BitwiseTest.java @@ -88,19 +88,19 @@ public void maximumConditionTest(String expected, LogicOperator operation) { assertEquals(Bitwise.getByte(operation), expected); } - private void setEssentialTrueConditions() { + private static void setEssentialTrueConditions() { Widget.setSerialCode(LAST_DIGIT_ODD); Widget.setStartTime(DEFAULT_START_TIME); Widget.setNumModules(6); } - private void setEssentialFalseConditions() { + private static void setEssentialFalseConditions() { Widget.setSerialCode(LAST_DIGIT_EVEN); Widget.setStartTime(DEFAULT_START_TIME); Widget.setNumModules(1); } - private void maximizeConditions() { + private static void maximizeConditions() { Widget.setPortValue(PARALLEL, 3); Widget.setIndicator(ON, BOB); Widget.setIndicator(ON, NSA); diff --git a/src/test/java/bomb/modules/il/laundry/LaundryTest.java b/src/test/java/bomb/modules/il/laundry/LaundryTest.java index aa882921..a507e3f2 100644 --- a/src/test/java/bomb/modules/il/laundry/LaundryTest.java +++ b/src/test/java/bomb/modules/il/laundry/LaundryTest.java @@ -22,7 +22,7 @@ public void methodSetup() { } @DataProvider - public Object[][] exceptionProvider() { + public Object[][] illegalArgExceptionProvider() { ConditionSetter setSerial = () -> Widget.setSerialCode("ajwf45"); return new Object[][]{ {EMPTY_SETTER, "1", "1"}, {setSerial, "", "1"}, {EMPTY_SETTER, "1", ""}, @@ -30,8 +30,23 @@ public Object[][] exceptionProvider() { }; } - @Test(dataProvider = "exceptionProvider", expectedExceptions = IllegalArgumentException.class) - public void exceptionTest(ConditionSetter setter, String solved, String needy) { + @Test(dataProvider = "illegalArgExceptionProvider", expectedExceptions = IllegalArgumentException.class) + public void illegalArgExceptionTest(ConditionSetter setter, String solved, String needy) { + setter.setCondition(); + Laundry.clean(solved, needy); + } + + @DataProvider + public Object[][] numberFormatExceptionProvider() { + ConditionSetter setSerial = () -> Widget.setSerialCode("ajwf45"); + return new Object[][]{ + {setSerial, "abc", "1"}, + {setSerial, "1", "abc"} + }; + } + + @Test(dataProvider = "numberFormatExceptionProvider", expectedExceptions = NumberFormatException.class) + public void numberFormatExceptionTest(ConditionSetter setter, String solved, String needy) { setter.setCondition(); Laundry.clean(solved, needy); } diff --git a/src/test/java/bomb/modules/t/the/bulb/TheBulbTest.java b/src/test/java/bomb/modules/t/the/bulb/TheBulbTest.java index f3edf8c8..c708ccd9 100644 --- a/src/test/java/bomb/modules/t/the/bulb/TheBulbTest.java +++ b/src/test/java/bomb/modules/t/the/bulb/TheBulbTest.java @@ -19,17 +19,17 @@ import static bomb.enumerations.Indicator.IND; import static bomb.enumerations.Indicator.NSA; import static bomb.enumerations.Indicator.SIG; -import static bomb.modules.t.the.bulb.Bulb.Color.BLUE; -import static bomb.modules.t.the.bulb.Bulb.Color.GREEN; -import static bomb.modules.t.the.bulb.Bulb.Color.PURPLE; -import static bomb.modules.t.the.bulb.Bulb.Color.WHITE; -import static bomb.modules.t.the.bulb.Bulb.Color.YELLOW; -import static bomb.modules.t.the.bulb.Bulb.Light.OFF; -import static bomb.modules.t.the.bulb.Bulb.Light.ON; -import static bomb.modules.t.the.bulb.Bulb.Opacity.OPAQUE; -import static bomb.modules.t.the.bulb.Bulb.Opacity.TRANSLUCENT; -import static bomb.modules.t.the.bulb.Bulb.Position.SCREWED; -import static bomb.modules.t.the.bulb.Bulb.Position.UNSCREWED; +import static bomb.modules.t.the.bulb.BulbModel.Color.BLUE; +import static bomb.modules.t.the.bulb.BulbModel.Color.GREEN; +import static bomb.modules.t.the.bulb.BulbModel.Color.PURPLE; +import static bomb.modules.t.the.bulb.BulbModel.Color.WHITE; +import static bomb.modules.t.the.bulb.BulbModel.Color.YELLOW; +import static bomb.modules.t.the.bulb.BulbModel.Light.OFF; +import static bomb.modules.t.the.bulb.BulbModel.Light.ON; +import static bomb.modules.t.the.bulb.BulbModel.Opacity.OPAQUE; +import static bomb.modules.t.the.bulb.BulbModel.Opacity.TRANSLUCENT; +import static bomb.modules.t.the.bulb.BulbModel.Position.SCREWED; +import static bomb.modules.t.the.bulb.BulbModel.Position.UNSCREWED; import static bomb.modules.t.the.bulb.TheBulb.PRESS_I; import static bomb.modules.t.the.bulb.TheBulb.PRESS_O; import static bomb.modules.t.the.bulb.TheBulb.SCREW; @@ -51,15 +51,15 @@ public Object[][] exceptionTestProvider() { } @Test(dataProvider = "exceptionTestProvider", expectedExceptions = IllegalArgumentException.class) - public void exceptionTest(Bulb.Position position, Bulb.Light inputLight, - Bulb.Color inputColor, Bulb.Opacity inputOpacity) { - Bulb testBulb = new Bulb(); - testBulb.setPosition(position); - testBulb.setOpacity(inputOpacity); - testBulb.setLight(inputLight); - testBulb.setColor(inputColor); - - TheBulb.solve(testBulb); + public void exceptionTest(BulbModel.Position position, BulbModel.Light inputLight, + BulbModel.Color inputColor, BulbModel.Opacity inputOpacity) { + BulbModel testBulbModel = new BulbModel(); + testBulbModel.setPosition(position); + testBulbModel.setOpacity(inputOpacity); + testBulbModel.setLight(inputLight); + testBulbModel.setColor(inputColor); + + TheBulb.solve(testBulbModel); } @DataProvider @@ -87,20 +87,20 @@ public Object[][] trainingVideoTestProvider() { } @Test(dataProvider = "trainingVideoTestProvider") - public void trainingVideoTest(ConditionSetter bombConditions, Bulb.Light inputLight, - Bulb.Color inputColor, Bulb.Opacity inputOpacity, + public void trainingVideoTest(ConditionSetter bombConditions, BulbModel.Light inputLight, + BulbModel.Color inputColor, BulbModel.Opacity inputOpacity, String[] expectedResults) { bombConditions.setCondition(); - Bulb testBulb = new Bulb(); - testBulb.setPosition(SCREWED); - testBulb.setOpacity(inputOpacity); - testBulb.setLight(inputLight); - testBulb.setColor(inputColor); + BulbModel testBulbModel = new BulbModel(); + testBulbModel.setPosition(SCREWED); + testBulbModel.setOpacity(inputOpacity); + testBulbModel.setLight(inputLight); + testBulbModel.setColor(inputColor); List convertedResults = Arrays.asList(expectedResults); - assertEquals(TheBulb.solve(testBulb), convertedResults); + assertEquals(TheBulb.solve(testBulbModel), convertedResults); } @DataProvider @@ -133,20 +133,20 @@ public Object[][] writtenTestProvider() { } @Test(dataProvider = "writtenTestProvider") - public void writtenTest(ConditionSetter bombConditions, Bulb.Light inputLight, - Bulb.Color inputColor, Bulb.Opacity inputOpacity, + public void writtenTest(ConditionSetter bombConditions, BulbModel.Light inputLight, + BulbModel.Color inputColor, BulbModel.Opacity inputOpacity, String[] expectedResults) { bombConditions.setCondition(); - Bulb testBulb = new Bulb(); - testBulb.setPosition(SCREWED); - testBulb.setOpacity(inputOpacity); - testBulb.setLight(inputLight); - testBulb.setColor(inputColor); + BulbModel testBulbModel = new BulbModel(); + testBulbModel.setPosition(SCREWED); + testBulbModel.setOpacity(inputOpacity); + testBulbModel.setLight(inputLight); + testBulbModel.setColor(inputColor); List convertedResults = Arrays.asList(expectedResults); - assertEquals(TheBulb.solve(testBulb), convertedResults); + assertEquals(TheBulb.solve(testBulbModel), convertedResults); } @AfterClass