diff --git a/pom.xml b/pom.xml index ea01b49..dfc4f36 100644 --- a/pom.xml +++ b/pom.xml @@ -9,11 +9,11 @@ ${java.version} ${java.version} UTF-8 - 21 - 5.8.2 + 22.0.1 + 5.10.2 10.17.1.0 - 1.18.30 - 6.4.2.Final + 1.18.32 + 6.5.2.Final @@ -21,7 +21,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.12.1 + 3.13.0 ${java.version} ${java.version} @@ -55,10 +55,11 @@ ${javafx.version} - com.jfoenix - jfoenix - 9.0.10 + org.controlsfx + controlsfx + 11.2.1 + eu.mihosoft.monacofx monacofx @@ -93,14 +94,14 @@ com.sun.xml.bind - jaxb-impl - 3.0.2 + jaxb-core + 4.0.5 org.apache.logging.log4j log4j-slf4j2-impl - 2.22.1 + 2.23.1 org.projectlombok @@ -111,13 +112,13 @@ org.mockito mockito-core - 4.3.1 + 5.12.0 test org.mockito mockito-junit-jupiter - 4.3.1 + 5.12.0 test diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/App.java b/src/main/java/com/JayPi4c/RobbiSimulator/App.java index f0122cc..721c7df 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/App.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/App.java @@ -1,16 +1,11 @@ package com.JayPi4c.RobbiSimulator; -import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; - -import java.util.ResourceBundle; - import com.JayPi4c.RobbiSimulator.controller.program.ProgramController; import com.JayPi4c.RobbiSimulator.controller.tutor.TutorController; import com.JayPi4c.RobbiSimulator.utils.AlertHelper; import com.JayPi4c.RobbiSimulator.utils.HibernateUtils; import com.JayPi4c.RobbiSimulator.utils.I18nUtils; import com.JayPi4c.RobbiSimulator.utils.PropertiesLoader; - import javafx.application.Application; import javafx.application.Platform; import javafx.scene.control.Alert.AlertType; @@ -18,100 +13,101 @@ import lombok.extern.slf4j.Slf4j; import javax.swing.*; +import java.util.ResourceBundle; + +import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; /** - * - * Hauptklasse des Robbi Simulators.
- * Javaversion: 17
- * - * @author Jonas Pohl + * Application entry point.
+ * Java version: 21 * + * @author Jonas Pohl */ @Slf4j public class App extends Application { - private static final String INIT_ERROR_MESSAGE = "Init.error.message"; - private static final String INIT_ERROR_TITLE = "Init.error.title"; - private static final String INIT_ERROR_HEADER = "Init.error.header"; - - /** - * Application entry point - * - * @param args the arguments from the command line - */ - public static void main(String[] args) { - try { - launch(args); - } catch (Exception e) { - e.printStackTrace(); - JOptionPane.showMessageDialog(null, """ - Failed to load application. - Further information can be obtained running the - application in the command line. - Please consider submitting your error. - """); - } - } - - @Override - public void init() { - logger.info("Initialize application"); - - logger.debug("Initializing properties..."); - if (PropertiesLoader.initialize()) { - logger.debug("Loaded properties successfully"); - } else - logger.debug("Failed to load properties."); - - I18nUtils.setBundle(ResourceBundle.getBundle("lang.messages", PropertiesLoader.getLocale())); - - logger.debug("Loading Program Controller"); - if (!ProgramController.initialize()) { - logger.error("Failed to load Program Controller"); - AlertHelper.showAlertAndWait(AlertType.ERROR, i18n(INIT_ERROR_MESSAGE), null, null, i18n(INIT_ERROR_TITLE), - i18n(INIT_ERROR_HEADER)); - Platform.exit(); - } - logger.debug("loading Program Controller successfully"); - - if (PropertiesLoader.isTutor()) { - logger.debug("Starting Tutor RMI server"); - if (TutorController.initialize()) - logger.debug("RMI server started"); - else - logger.debug("Failed to initialize RMI server."); - } - } - - @Override - public void start(Stage primaryStage) { - logger.info("Starting application"); - logger.debug("Creating scene"); - ProgramController.createAndShow(ProgramController.DEFAULT_ROBBI_FILE_NAME); - logger.debug("Scene creation done"); - } - - @Override - public void stop() { - - logger.debug("Shutting down database connection"); - HibernateUtils.shutdown(); - - if (PropertiesLoader.isTutor()) { - logger.debug("Stopping Tutor-Server."); - if (TutorController.shutdown()) - logger.debug("Tutor RMI server stopped successfully."); - else - logger.debug("Failed to shutdown Tutor RMI server"); - } - - logger.debug("saving properties..."); - if (PropertiesLoader.finish()) - logger.debug("Properties saved"); - else - logger.debug("Failed to save properties"); - - logger.info("Quitting application"); - } + private static final String INIT_ERROR_MESSAGE = "Init.error.message"; + private static final String INIT_ERROR_TITLE = "Init.error.title"; + private static final String INIT_ERROR_HEADER = "Init.error.header"; + + /** + * Application entry point + * + * @param args the arguments from the command line + */ + public static void main(String[] args) { + try { + launch(args); + } catch (Exception e) { + logger.error("Failed to load application", e); + JOptionPane.showMessageDialog(null, """ + Failed to load application. + Further information can be obtained running the + application in the command line. + Please consider submitting your error. + """); + } + } + + @Override + public void init() { + logger.info("Initialize application"); + + logger.debug("Initializing properties..."); + if (PropertiesLoader.initialize()) { + logger.debug("Loaded properties successfully"); + } else + logger.debug("Failed to load properties."); + + I18nUtils.setBundle(ResourceBundle.getBundle("lang.messages", PropertiesLoader.getLocale())); + + logger.debug("Loading Program Controller"); + if (!ProgramController.initialize()) { + logger.error("Failed to load Program Controller"); + AlertHelper.showAlertAndWait(AlertType.ERROR, i18n(INIT_ERROR_MESSAGE), null, null, i18n(INIT_ERROR_TITLE), + i18n(INIT_ERROR_HEADER)); + Platform.exit(); + } + logger.debug("loading Program Controller successfully"); + + if (PropertiesLoader.isTutor()) { + logger.debug("Starting Tutor RMI server"); + if (TutorController.initialize()) + logger.debug("RMI server started"); + else + logger.debug("Failed to initialize RMI server."); + } + } + + @Override + public void start(Stage primaryStage) { + logger.info("Starting application"); + logger.debug("Creating scene"); + ProgramController.createAndShow(ProgramController.DEFAULT_ROBBI_FILE_NAME); + logger.debug("Scene creation done"); + } + + @Override + public void stop() { + + logger.debug("Shutting down database connection"); + HibernateUtils.shutdown(); + + if (PropertiesLoader.isTutor()) { + logger.debug("Stopping Tutor-Server."); + if (TutorController.shutdown()) + logger.debug("Tutor RMI server stopped successfully."); + else + logger.debug("Failed to shutdown Tutor RMI server"); + } + + logger.debug("saving properties..."); + if (PropertiesLoader.finish()) + logger.debug("Properties saved"); + else + logger.debug("Failed to save properties"); + + logger.info("Quitting application"); + } } \ No newline at end of file diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/ButtonState.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/ButtonState.java index c3c079e..8590463 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/ButtonState.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/ButtonState.java @@ -6,69 +6,68 @@ /** * This class stores the state of the currently selected territory button state. * The state should be modified by the editor tool buttons. - * + *

* If the TerritoryEventHandler does register a click, the eventHandler gets the * current state from this class - * - * @author Jonas Pohl * + * @author Jonas Pohl */ +@Getter +@Setter public class ButtonState { - /** - * Constant for the ButtonState NONE. - */ - public static final int NONE = -1; - /** - * Constant for the ButtonState ROBBI. - */ - public static final int ROBBI = 0; + /** + * Constant for the ButtonState NONE. + */ + public static final int NONE = -1; + /** + * Constant for the ButtonState ROBBI. + */ + public static final int ROBBI = 0; - /** - * Constant for the ButtonState HOLLOW. - */ - public static final int HOLLOW = 1; - /** - * Constant for the ButtonState PILE_OF_SCRAP. - */ - public static final int PILE_OF_SCRAP = 2; - /** - * Constant for the ButtonState STOCKPILE. - */ - public static final int STOCKPILE = 3; - /** - * Constant for the ButtonState ACCU. - */ - public static final int ACCU = 4; - /** - * Constant for the ButtonState SCREW. - */ - public static final int SCREW = 5; - /** - * Constant for the ButtonState NUT. - */ - public static final int NUT = 6; - /** - * Constant for the ButtonState CLEAR. - */ - public static final int CLEAR = 7; + /** + * Constant for the ButtonState HOLLOW. + */ + public static final int HOLLOW = 1; + /** + * Constant for the ButtonState PILE_OF_SCRAP. + */ + public static final int PILE_OF_SCRAP = 2; + /** + * Constant for the ButtonState STOCKPILE. + */ + public static final int STOCKPILE = 3; + /** + * Constant for the ButtonState ACCU. + */ + public static final int ACCU = 4; + /** + * Constant for the ButtonState SCREW. + */ + public static final int SCREW = 5; + /** + * Constant for the ButtonState NUT. + */ + public static final int NUT = 6; + /** + * Constant for the ButtonState CLEAR. + */ + public static final int CLEAR = 7; - @Getter - @Setter - private int selected; + private int selected; - /** - * Creates a new ButtonState and sets the selected state to NONE - */ - public ButtonState() { - selected = NONE; - } + /** + * Creates a new ButtonState and sets the selected state to NONE + */ + public ButtonState() { + selected = NONE; + } - /** - * reset the currently selected state to NONE - */ - public void deselect() { - selected = NONE; - } + /** + * reset the currently selected state to NONE + */ + public void deselect() { + selected = NONE; + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/ChangeTerritorySizeHandler.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/ChangeTerritorySizeHandler.java index 2e2043e..1ac8253 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/ChangeTerritorySizeHandler.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/ChangeTerritorySizeHandler.java @@ -1,108 +1,102 @@ package com.JayPi4c.RobbiSimulator.controller; -import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; - -import java.util.Optional; - import com.JayPi4c.RobbiSimulator.model.Dimension; import com.JayPi4c.RobbiSimulator.model.Territory; - import javafx.application.Platform; import javafx.event.ActionEvent; import javafx.event.EventHandler; -import javafx.scene.control.ButtonType; -import javafx.scene.control.Dialog; -import javafx.scene.control.DialogPane; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; +import javafx.scene.control.*; import javafx.scene.layout.GridPane; import javafx.stage.Modality; import javafx.stage.Window; +import java.util.Optional; + +import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; + /** * This EventHandler combines all the code needed to change the territory size - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public class ChangeTerritorySizeHandler implements EventHandler { - private Territory territory; + private final Territory territory; - private Dialog dialog; - private Window parent; + private Dialog dialog; + private final Window parent; - // language keys - private static final String CHANGESIZE_DIALOG_TITLE = "ChangeSize.dialog.title"; - private static final String CHANGESIZE_DIALOG_HEADER = "ChangeSize.dialog.header"; - private static final String CHANGESIZE_DIALOG_COLS = "ChangeSize.dialog.cols"; - private static final String CHANGESIZE_DIALOG_ROWS = "ChangeSize.dialog.rows"; + // language keys + private static final String CHANGESIZE_DIALOG_TITLE = "ChangeSize.dialog.title"; + private static final String CHANGESIZE_DIALOG_HEADER = "ChangeSize.dialog.header"; + private static final String CHANGESIZE_DIALOG_COLS = "ChangeSize.dialog.cols"; + private static final String CHANGESIZE_DIALOG_ROWS = "ChangeSize.dialog.rows"; - /** - * Creates a new ChangeTerritorySizeHandler and sets up a new Dialog, which can - * be invoked in the handle method - * - * @param territory The territory, which size should be changed - * @param parent The parent window in order to set the dialog relative to the - * current window - */ - public ChangeTerritorySizeHandler(Window parent, Territory territory) { - this.territory = territory; - this.parent = parent; - } + /** + * Creates a new ChangeTerritorySizeHandler and sets up a new Dialog, which can + * be invoked in the handle method + * + * @param territory The territory, which size should be changed + * @param parent The parent window in order to set the dialog relative to the + * current window + */ + public ChangeTerritorySizeHandler(Window parent, Territory territory) { + this.territory = territory; + this.parent = parent; + } - /** - * Creates a new Dialog to handle the size change. The Dialog will be newly - * created every time in order to have the correct language at every moment. - */ - private void createDialog() { - dialog = new Dialog<>(); - dialog.setTitle(i18n(CHANGESIZE_DIALOG_TITLE)); - dialog.setHeaderText(i18n(CHANGESIZE_DIALOG_HEADER)); - DialogPane dialogPane = dialog.getDialogPane(); - dialogPane.getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); - TextField rowField = new TextField(); - rowField.textProperty().addListener((observeable, oldVal, newVal) -> dialog.getDialogPane() - .lookupButton(ButtonType.OK).setDisable(newVal.isEmpty() || !isValid(newVal))); - TextField colField = new TextField(); - colField.textProperty().addListener((observable, oldVal, newVal) -> dialog.getDialogPane() - .lookupButton(ButtonType.OK).setDisable(newVal.isEmpty() || !isValid(newVal))); + /** + * Creates a new Dialog to handle the size change. The Dialog will be newly + * created every time in order to have the correct language at every moment. + */ + private void createDialog() { + dialog = new Dialog<>(); + dialog.setTitle(i18n(CHANGESIZE_DIALOG_TITLE)); + dialog.setHeaderText(i18n(CHANGESIZE_DIALOG_HEADER)); + DialogPane dialogPane = dialog.getDialogPane(); + dialogPane.getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); + TextField rowField = new TextField(); + rowField.textProperty().addListener((observeable, oldVal, newVal) -> dialog.getDialogPane() + .lookupButton(ButtonType.OK).setDisable(newVal.isEmpty() || !isValid(newVal))); + TextField colField = new TextField(); + colField.textProperty().addListener((observable, oldVal, newVal) -> dialog.getDialogPane() + .lookupButton(ButtonType.OK).setDisable(newVal.isEmpty() || !isValid(newVal))); - GridPane grid = new GridPane(); - grid.addRow(0, new Label(i18n(CHANGESIZE_DIALOG_COLS)), colField); - grid.addRow(1, new Label(i18n(CHANGESIZE_DIALOG_ROWS)), rowField); - grid.setHgap(20); - dialogPane.setContent(grid); - rowField.setText(Integer.toString(territory.getNumRows())); - colField.setText(Integer.toString(territory.getNumCols())); - Platform.runLater(colField::requestFocus); - dialog.setResultConverter(button -> button == ButtonType.OK - ? new Dimension(Integer.parseInt(colField.getText()), Integer.parseInt(rowField.getText())) - : null); - dialog.initModality(Modality.WINDOW_MODAL); - dialog.initOwner(parent); - } + GridPane grid = new GridPane(); + grid.addRow(0, new Label(i18n(CHANGESIZE_DIALOG_COLS)), colField); + grid.addRow(1, new Label(i18n(CHANGESIZE_DIALOG_ROWS)), rowField); + grid.setHgap(20); + dialogPane.setContent(grid); + rowField.setText(Integer.toString(territory.getNumRows())); + colField.setText(Integer.toString(territory.getNumCols())); + Platform.runLater(colField::requestFocus); + dialog.setResultConverter(button -> button == ButtonType.OK + ? new Dimension(Integer.parseInt(colField.getText()), Integer.parseInt(rowField.getText())) + : null); + dialog.initModality(Modality.WINDOW_MODAL); + dialog.initOwner(parent); + } - /** - * Checks if the given String is between 1 and 100. - * - * @param s the String which represents the user-input - * @return true if the s is > 0 and <= 100, false otherwise - */ - private boolean isValid(String s) { - try { - int val = Integer.parseInt(s); - return val > 0 && val <= 100; - } catch (NumberFormatException e) { - return false; - } - } + /** + * Checks if the given String is between 1 and 100. + * + * @param s the String which represents the user-input + * @return true if the s is > 0 and <= 100, false otherwise + */ + private boolean isValid(String s) { + try { + int val = Integer.parseInt(s); + return val > 0 && val <= 100; + } catch (NumberFormatException e) { + return false; + } + } - @Override - public void handle(ActionEvent event) { - createDialog(); - Optional optionalDimension = dialog.showAndWait(); - optionalDimension.ifPresent(result -> territory.changeSize(result.cols(), result.rows())); - } + @Override + public void handle(ActionEvent event) { + createDialog(); + Optional optionalDimension = dialog.showAndWait(); + optionalDimension.ifPresent(result -> territory.changeSize(result.cols(), result.rows())); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/LanguageController.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/LanguageController.java index 48ca825..086e76e 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/LanguageController.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/LanguageController.java @@ -1,266 +1,253 @@ package com.JayPi4c.RobbiSimulator.controller; -import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.createBinding; -import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.createTooltip; -import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; - -import java.util.Locale; - import com.JayPi4c.RobbiSimulator.utils.I18nUtils; import com.JayPi4c.RobbiSimulator.utils.PropertiesLoader; import com.JayPi4c.RobbiSimulator.view.MainStage; import com.JayPi4c.RobbiSimulator.view.MenuBar; import com.JayPi4c.RobbiSimulator.view.Toolbar; +import java.util.Locale; + +import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.*; + /** * Controller to handle the change of language. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public class LanguageController { - private MainStage stage; - - // language keys - private static final String LANGUAGE_CHANGED = "language.changed"; - - private static final String MENU_EDITOR_NEW = "Menu.editor.new"; - private static final String MENU_EDITOR_SAVE = "Menu.editor.save"; - private static final String MENU_EDITOR_OPEN = "Menu.editor.open"; - private static final String MENU_EDITOR_FORMAT = "Menu.editor.format"; - private static final String MENU_EDITOR_COMPILE = "Menu.editor.compile"; - private static final String MENU_EDITOR_PRINT = "Menu.editor.print"; - private static final String MENU_EDITOR_QUIT = "Menu.editor.quit"; - private static final String MENU_EDITOR = "Menu.editor"; - - private static final String MENU_TERRITORY_SAVE_XML = "Menu.territory.save.xml"; - private static final String MENU_TERRITORY_SAVE_JAXB = "Menu.territory.save.jaxb"; - private static final String MENU_TERRITORY_SAVE_SERIALIZE = "Menu.territory.save.serialize"; - private static final String MENU_TERRITORY_SAVE = "Menu.territory.save"; - private static final String MENU_TERRITORY_LOAD_XML = "Menu.territory.load.xml"; - private static final String MENU_TERRITORY_LOAD_JAXB = "Menu.territory.load.jaxb"; - private static final String MENU_TERRITORY_LOAD_DESERIALIZE = "Menu.territory.load.deserialize"; - private static final String MENU_TERRITORY_LOAD = "Menu.territory.load"; - private static final String MENU_TERRITORY_SAVEASPIC_PNG = "Menu.territory.saveAsPic.png"; - private static final String MENU_TERRITORY_SAVEASPIC_GIF = "Menu.territory.saveAsPic.gif"; - private static final String MENU_TERRITORY_SAVEASPIC = "Menu.territory.saveAsPic"; - private static final String MENU_TERRITORY_PRINT = "Menu.territory.print"; - private static final String MENU_TERRITORY_SIZE = "Menu.territory.size"; - private static final String MENU_TERRITORY_PLACE_ROBBI = "Menu.territory.place.robbi"; - private static final String MENU_TERRITORY_PLACE_HOLLOW = "Menu.territory.place.hollow"; - private static final String MENU_TERRITORY_PLACE_PILEOFSCRAP = "Menu.territory.place.pileOfScrap"; - private static final String MENU_TERRITORY_PLACE_STOCKPILE = "Menu.territory.place.stockpile"; - private static final String MENU_TERRITORY_PLACE_ACCU = "Menu.territory.place.accu"; - private static final String MENU_TERRITORY_PLACE_SCREW = "Menu.territory.place.screw"; - private static final String MENU_TERRITORY_PLACE_NUT = "Menu.territory.place.nut"; - private static final String MENU_TERRITORY_DELETE = "Menu.territory.delete"; - private static final String MENU_TERRITORY = "Menu.territory"; - - private static final String MENU_ROBBI_ITEMPRESENT = "Menu.robbi.itemPresent"; - private static final String MENU_ROBBI_ISSTOCKPILE = "Menu.robbi.isStockpile"; - private static final String MENU_ROBBI_HOLLOWAHEAD = "Menu.robbi.hollowAhead"; - private static final String MENU_ROBBI_PILEOFSCRAPAHEAD = "Menu.robbi.pileOfScrapAhead"; - private static final String MENU_ROBBI_ISBAGFULL = "Menu.robbi.isBagFull"; - private static final String MENU_ROBBI_PUSHPILEOFSCRAP = "Menu.robbi.pushPileOfScrap"; - private static final String MENU_ROBBI_MOVE = "Menu.robbi.move"; - private static final String MENU_ROBBI_TURNLEFT = "Menu.robbi.turnLeft"; - private static final String MENU_ROBBI_PUT = "Menu.robbi.put"; - private static final String MENU_ROBBI_TAKE = "Menu.robbi.take"; - private static final String MENU_ROBBI = "Menu.robbi"; - private static final String MENU_SIMULATION_RESET = "Menu.simulation.reset"; - private static final String MENU_SIMULATION_START = "Menu.simulation.start"; - private static final String MENU_SIMULATION_PAUSE = "Menu.simulation.pause"; - private static final String MENU_SIMULATION_STOP = "Menu.simulation.stop"; - private static final String MENU_SIMULATION = "Menu.simulation"; - private static final String MENU_WINDOW_LANGUAGE = "Menu.window.language"; - private static final String MENU_WINDOW_LANGUAGE_ENGLISH = "Menu.window.language.english"; - private static final String MENU_WINDOW_LANGUAGE_GERMAN = "Menu.window.language.german"; - private static final String MENU_WINDOW_CHANGECURSOR = "Menu.window.changeCursor"; - private static final String MENU_WINDOW = "Menu.window"; - private static final String MENU_WINDOW_DARKMODE = "Menu.window.darkmode"; - private static final String MENU_WINDOW_ENABLESOUNDS = "Menu.window.enableSounds"; - private static final String MENU_WINDOW_LIBRARIES = "Menu.window.libraries"; - private static final String MENU_WINDOW_INFO = "Menu.window.info"; - - private static final String TOOLBAR_CONTROL_NEW = "Toolbar.control.new"; - private static final String TOOLBAR_CONTROL_LOAD = "Toolbar.control.load"; - private static final String TOOLBAR_CONTROL_SAVE = "Toolbar.control.save"; - private static final String TOOLBAR_CONTROL_COMPILE = "Toolbar.control.compile"; - - private static final String TOOLBAR_TERRITORY_SIZE = "Toolbar.territory.size"; - private static final String TOOLBAR_TERRITORY_PLACEROBBI = "Toolbar.territory.placeRobbi"; - private static final String TOOLBAR_TERRITORY_PLACEHOLLOW = "Toolbar.territory.placeHollow"; - private static final String TOOLBAR_TERRITORY_PLACEPILEOFSCRAP = "Toolbar.territory.placePileOfScrap"; - private static final String TOOLBAR_TERRITORY_PLACESTOCKPILE = "Toolbar.territory.placeStockpile"; - private static final String TOOLBAR_TERRITORY_PLACEACCU = "Toolbar.territory.placeAccu"; - private static final String TOOLBAR_TERRITORY_PLACESCREW = "Toolbar.territory.placeScrew"; - private static final String TOOLBAR_TERRITORY_PLACENUT = "Toolbar.territory.placeNut"; - private static final String TOOLBAR_TERRITORY_DELETE = "Toolbar.territory.delete"; - - private static final String TOOLBAR_ROBBI_TURNLEFT = "Toolbar.robbi.turnLeft"; - private static final String TOOLBAR_ROBBI_MOVE = "Toolbar.robbi.move"; - private static final String TOOLBAR_ROBBI_PUT = "Toolbar.robbi.put"; - private static final String TOOLBAR_ROBBI_TAKE = "Toolbar.robbi.take"; - - private static final String TOOLBAR_ACTION_RESET = "Toolbar.action.reset"; - private static final String TOOLBAR_ACTION_START = "Toolbar.action.start"; - private static final String TOOLBAR_ACTION_PAUSE = "Toolbar.action.pause"; - private static final String TOOLBAR_ACTION_STOP = "Toolbar.action.stop"; - private static final String TOOLBAR_ACTION_SPEED = "Toolbar.action.speed"; - - private static final String MENU_EXAMPLES = "Menu.examples"; - private static final String MENU_EXAMPLES_LOAD = "Menu.examples.load"; - private static final String MENU_EXAMPLES_SAVE = "Menu.examples.save"; - private static final String MENU_TUTOR_LOADREQUEST = "Menu.tutor.loadRequest"; - private static final String MENU_TUTOR_SAVEANSWER = "Menu.tutor.saveAnswer"; - private static final String MENU_TUTOR_SENDREQUEST = "Menu.tutor.sendRequest"; - private static final String MENU_TUTOR_RECEIVEANSWER = "Menu.tutor.receiveAnswer"; - private static final String MENU_TUTOR = "Menu.tutor"; - - private static final String MAIN_TITLE = "Main.title"; - - /** - * Constructor to create a new LanguageController. Sets the actions to the - * languageSelection and binds text to all graphical elements. - * - * @param mainStage the stage, this controller is for - */ - public LanguageController(MainStage mainStage) { - this.stage = mainStage; - - MenuBar menubar = mainStage.getMenubar(); - - menubar.getGermanLanguageMenuItem().setOnAction(e -> { - I18nUtils.setLocale(Locale.GERMANY); - updateTitle(); - mainStage.getSnackbarController().showMessage(LANGUAGE_CHANGED); - }); - menubar.getEnglishLanguageMenuItem().setOnAction(e -> { - I18nUtils.setLocale(Locale.UK); - updateTitle(); - mainStage.getSnackbarController().showMessage(LANGUAGE_CHANGED); - }); - - // text bindings - menubar.getNewEditorMenuItem().textProperty().bind(createBinding(MENU_EDITOR_NEW)); - menubar.getSaveEditorMenuItem().textProperty().bind(createBinding(MENU_EDITOR_SAVE)); - menubar.getOpenEditorMenuItem().textProperty().bind(createBinding(MENU_EDITOR_OPEN)); - menubar.getFormatSourceCodeMenuItem().textProperty().bind(createBinding(MENU_EDITOR_FORMAT)); - menubar.getCompileEditorMenuItem().textProperty().bind(createBinding(MENU_EDITOR_COMPILE)); - menubar.getPrintEditorMenuItem().textProperty().bind(createBinding(MENU_EDITOR_PRINT)); - menubar.getQuitEditorMenuItem().textProperty().bind(createBinding(MENU_EDITOR_QUIT)); - menubar.getEditorMenu().textProperty().bind(createBinding(MENU_EDITOR)); - - // territory Menu - menubar.getSaveXMLTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_SAVE_XML)); - menubar.getSaveJAXBTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_SAVE_JAXB)); - menubar.getSaveSerialTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_SAVE_SERIALIZE)); - menubar.getSaveTerritoryMenu().textProperty().bind(createBinding(MENU_TERRITORY_SAVE)); - menubar.getLoadXMLTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_LOAD_XML)); - menubar.getLoadJAXBTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_LOAD_JAXB)); - menubar.getLoadSerialTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_LOAD_DESERIALIZE)); - menubar.getLoadTerritoryMenu().textProperty().bind(createBinding(MENU_TERRITORY_LOAD)); - menubar.getSaveAsPNGMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_SAVEASPIC_PNG)); - menubar.getSaveAsGifMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_SAVEASPIC_GIF)); - menubar.getSaveAsPicMenu().textProperty().bind(createBinding(MENU_TERRITORY_SAVEASPIC)); - menubar.getPrintTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_PRINT)); - - menubar.getChangeSizeTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_SIZE)); - menubar.getPlaceRobbiTerritoryRadioMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_PLACE_ROBBI)); - menubar.getPlaceHollowTerritoryRadioMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_PLACE_HOLLOW)); - menubar.getPlacePileOfScrapTerritoryRadioMenuItem().textProperty() - .bind(createBinding(MENU_TERRITORY_PLACE_PILEOFSCRAP)); - menubar.getPlaceStockpileTerritoryRadioMenuItem().textProperty() - .bind(createBinding(MENU_TERRITORY_PLACE_STOCKPILE)); - menubar.getPlaceAccuTerritoryRadioMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_PLACE_ACCU)); - menubar.getPlaceScrewTerritoryRadioMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_PLACE_SCREW)); - menubar.getPlaceNutTerritoryRadioMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_PLACE_NUT)); - menubar.getDeleteFieldRadioMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_DELETE)); - menubar.getTerritoryMenu().textProperty().bind(createBinding(MENU_TERRITORY)); - - // robbi Menu - menubar.getItemPresentMenuItem().textProperty().bind(createBinding(MENU_ROBBI_ITEMPRESENT)); - menubar.getIsStockpileMenuItem().textProperty().bind(createBinding(MENU_ROBBI_ISSTOCKPILE)); - menubar.getHollowAheadMenuItem().textProperty().bind(createBinding(MENU_ROBBI_HOLLOWAHEAD)); - menubar.getPileOfScrapAheadMenuItem().textProperty().bind(createBinding(MENU_ROBBI_PILEOFSCRAPAHEAD)); - menubar.getIsBagFullMenuItem().textProperty().bind(createBinding(MENU_ROBBI_ISBAGFULL)); - menubar.getPushPileOfScrapMenuItem().textProperty().bind(createBinding(MENU_ROBBI_PUSHPILEOFSCRAP)); - menubar.getMoveMenuItem().textProperty().bind(createBinding(MENU_ROBBI_MOVE)); - menubar.getTurnLeftMenuItem().textProperty().bind(createBinding(MENU_ROBBI_TURNLEFT)); - menubar.getPutMenuItem().textProperty().bind(createBinding(MENU_ROBBI_PUT)); - menubar.getTakeMenuItem().textProperty().bind(createBinding(MENU_ROBBI_TAKE)); - menubar.getRobbiMenu().textProperty().bind(createBinding(MENU_ROBBI)); - - // simulation Menu - menubar.getResetMenuItem().textProperty().bind(createBinding(MENU_SIMULATION_RESET)); - menubar.getStartMenuItem().textProperty().bind(createBinding(MENU_SIMULATION_START)); - menubar.getPauseMenuItem().textProperty().bind(createBinding(MENU_SIMULATION_PAUSE)); - menubar.getStopMenuItem().textProperty().bind(createBinding(MENU_SIMULATION_STOP)); - menubar.getSimulationMenu().textProperty().bind(createBinding(MENU_SIMULATION)); - - // window Menu - menubar.getLanguageMenu().textProperty().bind(createBinding(MENU_WINDOW_LANGUAGE)); - menubar.getEnglishLanguageMenuItem().textProperty().bind(createBinding(MENU_WINDOW_LANGUAGE_ENGLISH)); - menubar.getGermanLanguageMenuItem().textProperty().bind(createBinding(MENU_WINDOW_LANGUAGE_GERMAN)); - menubar.getChangeCursorMenuItem().textProperty().bind(createBinding(MENU_WINDOW_CHANGECURSOR)); - menubar.getWindowMenu().textProperty().bind(createBinding(MENU_WINDOW)); - menubar.getDarkModeMenuItem().textProperty().bind(createBinding(MENU_WINDOW_DARKMODE)); - menubar.getEnableSoundsMenuItem().textProperty().bind(createBinding(MENU_WINDOW_ENABLESOUNDS)); - menubar.getLibraryMenuItem().textProperty().bind(createBinding(MENU_WINDOW_LIBRARIES)); - menubar.getInfoMenuItem().textProperty().bind(createBinding(MENU_WINDOW_INFO)); - - Toolbar toolbar = mainStage.getToolbar(); - // Tool bar - - toolbar.getNewButtonToolbar().setTooltip(createTooltip(TOOLBAR_CONTROL_NEW)); - toolbar.getLoadButtonToolbar().setTooltip(createTooltip(TOOLBAR_CONTROL_LOAD)); - toolbar.getSaveButtonToolbar().setTooltip(createTooltip(TOOLBAR_CONTROL_SAVE)); - toolbar.getCompileButtonToolbar().setTooltip(createTooltip(TOOLBAR_CONTROL_COMPILE)); - - toolbar.getChangeSizeButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_SIZE)); - toolbar.getPlaceRobbiToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_PLACEROBBI)); - toolbar.getPlaceHollowToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_PLACEHOLLOW)); - toolbar.getPlacePileOfScrapToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_PLACEPILEOFSCRAP)); - toolbar.getPlaceStockpileToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_PLACESTOCKPILE)); - toolbar.getPlaceAccuToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_PLACEACCU)); - toolbar.getPlaceScrewToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_PLACESCREW)); - toolbar.getPlaceNutToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_PLACENUT)); - toolbar.getDeleteFieldToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_DELETE)); - - toolbar.getRobbiTurnLeftButtonToolbar().setTooltip(createTooltip(TOOLBAR_ROBBI_TURNLEFT)); - toolbar.getRobbiMoveButtonToolbar().setTooltip(createTooltip(TOOLBAR_ROBBI_MOVE)); - toolbar.getRobbiPutButtonToolbar().setTooltip(createTooltip(TOOLBAR_ROBBI_PUT)); - toolbar.getRobbiTakeButtonToolbar().setTooltip(createTooltip(TOOLBAR_ROBBI_TAKE)); - - toolbar.getResetButtonToolbar().setTooltip(createTooltip(TOOLBAR_ACTION_RESET)); - toolbar.getStartToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_ACTION_START)); - toolbar.getPauseToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_ACTION_PAUSE)); - toolbar.getStopToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_ACTION_STOP)); - toolbar.getSpeedSliderToolbar().setTooltip(createTooltip(TOOLBAR_ACTION_SPEED)); - - menubar.getExamplesMenu().textProperty().bind(createBinding(MENU_EXAMPLES)); - menubar.getLoadExampleMenuItem().textProperty().bind(createBinding(MENU_EXAMPLES_LOAD)); - menubar.getSaveExampleMenuItem().textProperty().bind(createBinding(MENU_EXAMPLES_SAVE)); - - if (PropertiesLoader.isTutor()) { - menubar.getLoadRequestMenuItem().textProperty().bind(createBinding(MENU_TUTOR_LOADREQUEST)); - menubar.getSaveAnswerMenuItem().textProperty().bind(createBinding(MENU_TUTOR_SAVEANSWER)); - } else { - menubar.getSendRequestMenuItem().textProperty().bind(createBinding(MENU_TUTOR_SENDREQUEST)); - menubar.getReceiveAnswerMenuItem().textProperty().bind(createBinding(MENU_TUTOR_RECEIVEANSWER)); - } - menubar.getTutorMenu().textProperty().bind(createBinding(MENU_TUTOR)); - } - - /** - * needed since the star (*) would mess with the binding property - * - * TODO use formatted String - */ - public void updateTitle() { - stage.setTitle( - i18n(MAIN_TITLE) + ": " + stage.getProgram().getName() + (stage.getProgram().isEdited() ? "*" : "")); - } + // language keys + private static final String LANGUAGE_CHANGED = "language.changed"; + private static final String MENU_EDITOR_NEW = "Menu.editor.new"; + private static final String MENU_EDITOR_SAVE = "Menu.editor.save"; + private static final String MENU_EDITOR_OPEN = "Menu.editor.open"; + private static final String MENU_EDITOR_FORMAT = "Menu.editor.format"; + private static final String MENU_EDITOR_COMPILE = "Menu.editor.compile"; + private static final String MENU_EDITOR_PRINT = "Menu.editor.print"; + private static final String MENU_EDITOR_QUIT = "Menu.editor.quit"; + private static final String MENU_EDITOR = "Menu.editor"; + private static final String MENU_TERRITORY_SAVE_XML = "Menu.territory.save.xml"; + private static final String MENU_TERRITORY_SAVE_JAXB = "Menu.territory.save.jaxb"; + private static final String MENU_TERRITORY_SAVE_SERIALIZE = "Menu.territory.save.serialize"; + private static final String MENU_TERRITORY_SAVE = "Menu.territory.save"; + private static final String MENU_TERRITORY_LOAD_XML = "Menu.territory.load.xml"; + private static final String MENU_TERRITORY_LOAD_JAXB = "Menu.territory.load.jaxb"; + private static final String MENU_TERRITORY_LOAD_DESERIALIZE = "Menu.territory.load.deserialize"; + private static final String MENU_TERRITORY_LOAD = "Menu.territory.load"; + private static final String MENU_TERRITORY_SAVEASPIC_PNG = "Menu.territory.saveAsPic.png"; + private static final String MENU_TERRITORY_SAVEASPIC_GIF = "Menu.territory.saveAsPic.gif"; + private static final String MENU_TERRITORY_SAVEASPIC = "Menu.territory.saveAsPic"; + private static final String MENU_TERRITORY_PRINT = "Menu.territory.print"; + private static final String MENU_TERRITORY_SIZE = "Menu.territory.size"; + private static final String MENU_TERRITORY_PLACE_ROBBI = "Menu.territory.place.robbi"; + private static final String MENU_TERRITORY_PLACE_HOLLOW = "Menu.territory.place.hollow"; + private static final String MENU_TERRITORY_PLACE_PILEOFSCRAP = "Menu.territory.place.pileOfScrap"; + private static final String MENU_TERRITORY_PLACE_STOCKPILE = "Menu.territory.place.stockpile"; + private static final String MENU_TERRITORY_PLACE_ACCU = "Menu.territory.place.accu"; + private static final String MENU_TERRITORY_PLACE_SCREW = "Menu.territory.place.screw"; + private static final String MENU_TERRITORY_PLACE_NUT = "Menu.territory.place.nut"; + private static final String MENU_TERRITORY_DELETE = "Menu.territory.delete"; + private static final String MENU_TERRITORY = "Menu.territory"; + private static final String MENU_ROBBI_ITEMPRESENT = "Menu.robbi.itemPresent"; + private static final String MENU_ROBBI_ISSTOCKPILE = "Menu.robbi.isStockpile"; + private static final String MENU_ROBBI_HOLLOWAHEAD = "Menu.robbi.hollowAhead"; + private static final String MENU_ROBBI_PILEOFSCRAPAHEAD = "Menu.robbi.pileOfScrapAhead"; + private static final String MENU_ROBBI_ISBAGFULL = "Menu.robbi.isBagFull"; + private static final String MENU_ROBBI_PUSHPILEOFSCRAP = "Menu.robbi.pushPileOfScrap"; + private static final String MENU_ROBBI_MOVE = "Menu.robbi.move"; + private static final String MENU_ROBBI_TURNLEFT = "Menu.robbi.turnLeft"; + private static final String MENU_ROBBI_PUT = "Menu.robbi.put"; + private static final String MENU_ROBBI_TAKE = "Menu.robbi.take"; + private static final String MENU_ROBBI = "Menu.robbi"; + private static final String MENU_SIMULATION_RESET = "Menu.simulation.reset"; + private static final String MENU_SIMULATION_START = "Menu.simulation.start"; + private static final String MENU_SIMULATION_PAUSE = "Menu.simulation.pause"; + private static final String MENU_SIMULATION_STOP = "Menu.simulation.stop"; + private static final String MENU_SIMULATION = "Menu.simulation"; + private static final String MENU_WINDOW_LANGUAGE = "Menu.window.language"; + private static final String MENU_WINDOW_LANGUAGE_ENGLISH = "Menu.window.language.english"; + private static final String MENU_WINDOW_LANGUAGE_GERMAN = "Menu.window.language.german"; + private static final String MENU_WINDOW_CHANGECURSOR = "Menu.window.changeCursor"; + private static final String MENU_WINDOW = "Menu.window"; + private static final String MENU_WINDOW_DARKMODE = "Menu.window.darkmode"; + private static final String MENU_WINDOW_ENABLESOUNDS = "Menu.window.enableSounds"; + private static final String MENU_WINDOW_LIBRARIES = "Menu.window.libraries"; + private static final String MENU_WINDOW_INFO = "Menu.window.info"; + private static final String TOOLBAR_CONTROL_NEW = "Toolbar.control.new"; + private static final String TOOLBAR_CONTROL_LOAD = "Toolbar.control.load"; + private static final String TOOLBAR_CONTROL_SAVE = "Toolbar.control.save"; + private static final String TOOLBAR_CONTROL_COMPILE = "Toolbar.control.compile"; + private static final String TOOLBAR_TERRITORY_SIZE = "Toolbar.territory.size"; + private static final String TOOLBAR_TERRITORY_PLACEROBBI = "Toolbar.territory.placeRobbi"; + private static final String TOOLBAR_TERRITORY_PLACEHOLLOW = "Toolbar.territory.placeHollow"; + private static final String TOOLBAR_TERRITORY_PLACEPILEOFSCRAP = "Toolbar.territory.placePileOfScrap"; + private static final String TOOLBAR_TERRITORY_PLACESTOCKPILE = "Toolbar.territory.placeStockpile"; + private static final String TOOLBAR_TERRITORY_PLACEACCU = "Toolbar.territory.placeAccu"; + private static final String TOOLBAR_TERRITORY_PLACESCREW = "Toolbar.territory.placeScrew"; + private static final String TOOLBAR_TERRITORY_PLACENUT = "Toolbar.territory.placeNut"; + private static final String TOOLBAR_TERRITORY_DELETE = "Toolbar.territory.delete"; + private static final String TOOLBAR_ROBBI_TURNLEFT = "Toolbar.robbi.turnLeft"; + private static final String TOOLBAR_ROBBI_MOVE = "Toolbar.robbi.move"; + private static final String TOOLBAR_ROBBI_PUT = "Toolbar.robbi.put"; + private static final String TOOLBAR_ROBBI_TAKE = "Toolbar.robbi.take"; + private static final String TOOLBAR_ACTION_RESET = "Toolbar.action.reset"; + private static final String TOOLBAR_ACTION_START = "Toolbar.action.start"; + private static final String TOOLBAR_ACTION_PAUSE = "Toolbar.action.pause"; + private static final String TOOLBAR_ACTION_STOP = "Toolbar.action.stop"; + private static final String TOOLBAR_ACTION_SPEED = "Toolbar.action.speed"; + private static final String MENU_EXAMPLES = "Menu.examples"; + private static final String MENU_EXAMPLES_LOAD = "Menu.examples.load"; + private static final String MENU_EXAMPLES_SAVE = "Menu.examples.save"; + private static final String MENU_TUTOR_LOADREQUEST = "Menu.tutor.loadRequest"; + private static final String MENU_TUTOR_SAVEANSWER = "Menu.tutor.saveAnswer"; + private static final String MENU_TUTOR_SENDREQUEST = "Menu.tutor.sendRequest"; + private static final String MENU_TUTOR_RECEIVEANSWER = "Menu.tutor.receiveAnswer"; + private static final String MENU_TUTOR = "Menu.tutor"; + private static final String MAIN_TITLE = "Main.title"; + private final MainStage stage; + + /** + * Constructor to create a new LanguageController. Sets the actions to the + * languageSelection and binds text to all graphical elements. + * + * @param mainStage the stage, this controller is for + */ + public LanguageController(MainStage mainStage) { + this.stage = mainStage; + + MenuBar menubar = mainStage.getMenubar(); + + menubar.getGermanLanguageMenuItem().setOnAction(e -> { + I18nUtils.setLocale(Locale.GERMANY); + updateTitle(); + mainStage.getNotificationController().showMessage(3000, LANGUAGE_CHANGED); + }); + menubar.getEnglishLanguageMenuItem().setOnAction(e -> { + I18nUtils.setLocale(Locale.UK); + updateTitle(); + mainStage.getNotificationController().showMessage(3000, LANGUAGE_CHANGED); + }); + + // text bindings + menubar.getNewEditorMenuItem().textProperty().bind(createBinding(MENU_EDITOR_NEW)); + menubar.getSaveEditorMenuItem().textProperty().bind(createBinding(MENU_EDITOR_SAVE)); + menubar.getOpenEditorMenuItem().textProperty().bind(createBinding(MENU_EDITOR_OPEN)); + menubar.getFormatSourceCodeMenuItem().textProperty().bind(createBinding(MENU_EDITOR_FORMAT)); + menubar.getCompileEditorMenuItem().textProperty().bind(createBinding(MENU_EDITOR_COMPILE)); + menubar.getPrintEditorMenuItem().textProperty().bind(createBinding(MENU_EDITOR_PRINT)); + menubar.getQuitEditorMenuItem().textProperty().bind(createBinding(MENU_EDITOR_QUIT)); + menubar.getEditorMenu().textProperty().bind(createBinding(MENU_EDITOR)); + + // territory Menu + menubar.getSaveXMLTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_SAVE_XML)); + menubar.getSaveJAXBTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_SAVE_JAXB)); + menubar.getSaveSerialTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_SAVE_SERIALIZE)); + menubar.getSaveTerritoryMenu().textProperty().bind(createBinding(MENU_TERRITORY_SAVE)); + menubar.getLoadXMLTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_LOAD_XML)); + menubar.getLoadJAXBTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_LOAD_JAXB)); + menubar.getLoadSerialTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_LOAD_DESERIALIZE)); + menubar.getLoadTerritoryMenu().textProperty().bind(createBinding(MENU_TERRITORY_LOAD)); + menubar.getSaveAsPNGMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_SAVEASPIC_PNG)); + menubar.getSaveAsGifMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_SAVEASPIC_GIF)); + menubar.getSaveAsPicMenu().textProperty().bind(createBinding(MENU_TERRITORY_SAVEASPIC)); + menubar.getPrintTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_PRINT)); + + menubar.getChangeSizeTerritoryMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_SIZE)); + menubar.getPlaceRobbiTerritoryRadioMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_PLACE_ROBBI)); + menubar.getPlaceHollowTerritoryRadioMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_PLACE_HOLLOW)); + menubar.getPlacePileOfScrapTerritoryRadioMenuItem().textProperty() + .bind(createBinding(MENU_TERRITORY_PLACE_PILEOFSCRAP)); + menubar.getPlaceStockpileTerritoryRadioMenuItem().textProperty() + .bind(createBinding(MENU_TERRITORY_PLACE_STOCKPILE)); + menubar.getPlaceAccuTerritoryRadioMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_PLACE_ACCU)); + menubar.getPlaceScrewTerritoryRadioMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_PLACE_SCREW)); + menubar.getPlaceNutTerritoryRadioMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_PLACE_NUT)); + menubar.getDeleteFieldRadioMenuItem().textProperty().bind(createBinding(MENU_TERRITORY_DELETE)); + menubar.getTerritoryMenu().textProperty().bind(createBinding(MENU_TERRITORY)); + + // robbi Menu + menubar.getItemPresentMenuItem().textProperty().bind(createBinding(MENU_ROBBI_ITEMPRESENT)); + menubar.getIsStockpileMenuItem().textProperty().bind(createBinding(MENU_ROBBI_ISSTOCKPILE)); + menubar.getHollowAheadMenuItem().textProperty().bind(createBinding(MENU_ROBBI_HOLLOWAHEAD)); + menubar.getPileOfScrapAheadMenuItem().textProperty().bind(createBinding(MENU_ROBBI_PILEOFSCRAPAHEAD)); + menubar.getIsBagFullMenuItem().textProperty().bind(createBinding(MENU_ROBBI_ISBAGFULL)); + menubar.getPushPileOfScrapMenuItem().textProperty().bind(createBinding(MENU_ROBBI_PUSHPILEOFSCRAP)); + menubar.getMoveMenuItem().textProperty().bind(createBinding(MENU_ROBBI_MOVE)); + menubar.getTurnLeftMenuItem().textProperty().bind(createBinding(MENU_ROBBI_TURNLEFT)); + menubar.getPutMenuItem().textProperty().bind(createBinding(MENU_ROBBI_PUT)); + menubar.getTakeMenuItem().textProperty().bind(createBinding(MENU_ROBBI_TAKE)); + menubar.getRobbiMenu().textProperty().bind(createBinding(MENU_ROBBI)); + + // simulation Menu + menubar.getResetMenuItem().textProperty().bind(createBinding(MENU_SIMULATION_RESET)); + menubar.getStartMenuItem().textProperty().bind(createBinding(MENU_SIMULATION_START)); + menubar.getPauseMenuItem().textProperty().bind(createBinding(MENU_SIMULATION_PAUSE)); + menubar.getStopMenuItem().textProperty().bind(createBinding(MENU_SIMULATION_STOP)); + menubar.getSimulationMenu().textProperty().bind(createBinding(MENU_SIMULATION)); + + // window Menu + menubar.getLanguageMenu().textProperty().bind(createBinding(MENU_WINDOW_LANGUAGE)); + menubar.getEnglishLanguageMenuItem().textProperty().bind(createBinding(MENU_WINDOW_LANGUAGE_ENGLISH)); + menubar.getGermanLanguageMenuItem().textProperty().bind(createBinding(MENU_WINDOW_LANGUAGE_GERMAN)); + menubar.getChangeCursorMenuItem().textProperty().bind(createBinding(MENU_WINDOW_CHANGECURSOR)); + menubar.getWindowMenu().textProperty().bind(createBinding(MENU_WINDOW)); + menubar.getDarkModeMenuItem().textProperty().bind(createBinding(MENU_WINDOW_DARKMODE)); + menubar.getEnableSoundsMenuItem().textProperty().bind(createBinding(MENU_WINDOW_ENABLESOUNDS)); + menubar.getLibraryMenuItem().textProperty().bind(createBinding(MENU_WINDOW_LIBRARIES)); + menubar.getInfoMenuItem().textProperty().bind(createBinding(MENU_WINDOW_INFO)); + + Toolbar toolbar = mainStage.getToolbar(); + // Tool bar + + toolbar.getNewButtonToolbar().setTooltip(createTooltip(TOOLBAR_CONTROL_NEW)); + toolbar.getLoadButtonToolbar().setTooltip(createTooltip(TOOLBAR_CONTROL_LOAD)); + toolbar.getSaveButtonToolbar().setTooltip(createTooltip(TOOLBAR_CONTROL_SAVE)); + toolbar.getCompileButtonToolbar().setTooltip(createTooltip(TOOLBAR_CONTROL_COMPILE)); + + toolbar.getChangeSizeButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_SIZE)); + toolbar.getPlaceRobbiToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_PLACEROBBI)); + toolbar.getPlaceHollowToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_PLACEHOLLOW)); + toolbar.getPlacePileOfScrapToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_PLACEPILEOFSCRAP)); + toolbar.getPlaceStockpileToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_PLACESTOCKPILE)); + toolbar.getPlaceAccuToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_PLACEACCU)); + toolbar.getPlaceScrewToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_PLACESCREW)); + toolbar.getPlaceNutToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_PLACENUT)); + toolbar.getDeleteFieldToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_TERRITORY_DELETE)); + + toolbar.getRobbiTurnLeftButtonToolbar().setTooltip(createTooltip(TOOLBAR_ROBBI_TURNLEFT)); + toolbar.getRobbiMoveButtonToolbar().setTooltip(createTooltip(TOOLBAR_ROBBI_MOVE)); + toolbar.getRobbiPutButtonToolbar().setTooltip(createTooltip(TOOLBAR_ROBBI_PUT)); + toolbar.getRobbiTakeButtonToolbar().setTooltip(createTooltip(TOOLBAR_ROBBI_TAKE)); + + toolbar.getResetButtonToolbar().setTooltip(createTooltip(TOOLBAR_ACTION_RESET)); + toolbar.getStartToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_ACTION_START)); + toolbar.getPauseToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_ACTION_PAUSE)); + toolbar.getStopToggleButtonToolbar().setTooltip(createTooltip(TOOLBAR_ACTION_STOP)); + toolbar.getSpeedSliderToolbar().setTooltip(createTooltip(TOOLBAR_ACTION_SPEED)); + + menubar.getExamplesMenu().textProperty().bind(createBinding(MENU_EXAMPLES)); + menubar.getLoadExampleMenuItem().textProperty().bind(createBinding(MENU_EXAMPLES_LOAD)); + menubar.getSaveExampleMenuItem().textProperty().bind(createBinding(MENU_EXAMPLES_SAVE)); + + if (PropertiesLoader.isTutor()) { + menubar.getLoadRequestMenuItem().textProperty().bind(createBinding(MENU_TUTOR_LOADREQUEST)); + menubar.getSaveAnswerMenuItem().textProperty().bind(createBinding(MENU_TUTOR_SAVEANSWER)); + } else { + menubar.getSendRequestMenuItem().textProperty().bind(createBinding(MENU_TUTOR_SENDREQUEST)); + menubar.getReceiveAnswerMenuItem().textProperty().bind(createBinding(MENU_TUTOR_RECEIVEANSWER)); + } + menubar.getTutorMenu().textProperty().bind(createBinding(MENU_TUTOR)); + } + + /** + * needed since the star (*) would mess with the binding property + *

+ * TODO use formatted String + */ + public void updateTitle() { + stage.setTitle( + i18n(MAIN_TITLE) + ": " + stage.getProgram().getName() + (stage.getProgram().isEdited() ? "*" : "")); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/MainStageController.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/MainStageController.java index 082cbe0..4556a9d 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/MainStageController.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/MainStageController.java @@ -1,36 +1,13 @@ package com.JayPi4c.RobbiSimulator.controller; -import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; - -import javax.imageio.ImageIO; - -import org.apache.logging.log4j.Logger; -import org.hibernate.Version; - import com.JayPi4c.RobbiSimulator.controller.program.Program; import com.JayPi4c.RobbiSimulator.controller.program.ProgramController; -import com.JayPi4c.RobbiSimulator.model.BagIsEmptyException; -import com.JayPi4c.RobbiSimulator.model.BagIsFullException; -import com.JayPi4c.RobbiSimulator.model.HollowAheadException; -import com.JayPi4c.RobbiSimulator.model.NoItemException; -import com.JayPi4c.RobbiSimulator.model.NoPileOfScrapAheadException; -import com.JayPi4c.RobbiSimulator.model.TileBlockedException; -import com.JayPi4c.RobbiSimulator.model.TileIsFullException; -import com.JayPi4c.RobbiSimulator.utils.AlertHelper; -import com.JayPi4c.RobbiSimulator.utils.Observable; -import com.JayPi4c.RobbiSimulator.utils.Observer; -import com.JayPi4c.RobbiSimulator.utils.SceneManager; -import com.JayPi4c.RobbiSimulator.utils.SoundManager; +import com.JayPi4c.RobbiSimulator.model.*; +import com.JayPi4c.RobbiSimulator.utils.*; import com.JayPi4c.RobbiSimulator.view.MainStage; import com.JayPi4c.RobbiSimulator.view.MenuBar; import com.JayPi4c.RobbiSimulator.view.TerritoryPanel; import com.JayPi4c.RobbiSimulator.view.Toolbar; -import com.jfoenix.utils.JFXUtilities; - import eu.mihosoft.monacofx.MonacoFX; import jakarta.xml.bind.JAXBContext; import javafx.embed.swing.SwingFXUtils; @@ -53,430 +30,434 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.apache.logging.log4j.Logger; +import org.hibernate.Version; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; /** * This controller contains all the settings for the mainStage - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Slf4j public class MainStageController implements Observer { - private ButtonState buttonState; - - private MainStage mainStage; - - @Setter - private boolean changeCursor = false; - @Getter - private boolean soundsEnabled = false; - - private RadioMenuItem selectedRadioMenuItem = null; - - /** - * Creates a new MainStageController and sets all the actions for the buttons in - * the mainStage - * - * @param mainStage the mainStage for this controller - * @param buttonState the buttonState for this controller - */ - public MainStageController(MainStage mainStage, ButtonState buttonState) { - this.mainStage = mainStage; - this.buttonState = buttonState; - this.mainStage.getProgram().addObserver(this); - - mainStage.setTitle(i18n("Main.title") + ": " + mainStage.getProgram().getName()); - - mainStage.setOnCloseRequest(e -> { - mainStage.getProgram().save(mainStage.getTextArea().getEditor().getDocument().getText()); - ProgramController.close(mainStage.getProgram().getName()); - }); - - MenuBar menuBar = mainStage.getMenubar(); - - // editor (menuBar) - menuBar.getNewEditorMenuItem().setOnAction(e -> ProgramController.createAndShow(mainStage)); - menuBar.getOpenEditorMenuItem().setOnAction(e -> ProgramController.openProgram(mainStage)); - menuBar.getSaveEditorMenuItem().setOnAction(e -> { - mainStage.getProgram().save(mainStage.getTextArea().getEditor().getDocument().getText()); - mainStage.setTitle(getTitle(mainStage.getProgram())); - }); - - menuBar.getFormatSourceCodeMenuItem().setOnAction(e -> { - mainStage.getSnackbarController().showMessage("not.implemented"); - // For this the following PR should first be merged: - // https://github.com/miho/MonacoFX/pull/25 - }); - - menuBar.getCompileEditorMenuItem().setOnAction(e -> { - mainStage.getSimulationController().stopSimulation(); - Program program = mainStage.getProgram(); - program.save(mainStage.getTextArea().getEditor().getDocument().getText()); - mainStage.setTitle(getTitle(program)); - ProgramController.compile(program, mainStage); - }); - // TODO print editor content - menuBar.getPrintEditorMenuItem() - .setOnAction(e -> mainStage.getSnackbarController().showMessage("not.implemented")); - menuBar.getQuitEditorMenuItem().setOnAction(e -> { - Program program = mainStage.getProgram(); - logger.info("exiting {}", program.getName()); - program.save(mainStage.getTextArea().getEditor().getDocument().getText()); - ProgramController.close(program.getName()); - mainStage.close(); - }); - // Territory (menuBar) - // save -> TerritorySaveController - menuBar.getSaveAsPNGMenuItem().setOnAction(e -> { - String extension = ".png"; - File file = getFile(i18n("Menu.territory.saveAsPic.png.description"), extension); - if (file == null) - return; - - if (!saveAsImage(file, extension)) { - mainStage.getSnackbarController().showMessage("Menu.territory.saveAsPic.error"); - } - }); - menuBar.getSaveAsGifMenuItem().setOnAction(e -> { - String extension = ".gif"; - File file = getFile(i18n("Menu.territory.saveAsPic.gif.description"), extension); - if (file == null) - return; - - if (!saveAsImage(file, extension)) { - mainStage.getSnackbarController().showMessage("Menu.territory.saveAsPic.error"); - } - }); - menuBar.getPrintTerritoryMenuItem().setOnAction(e -> printTerritory()); - menuBar.getChangeSizeTerritoryMenuItem() - .setOnAction(new ChangeTerritorySizeHandler(mainStage, mainStage.getTerritory())); - - menuBar.getPlaceRobbiTerritoryRadioMenuItem() - .setOnAction(getRadioHandler(MainStage.menuRobbiImage, ButtonState.ROBBI)); - menuBar.getPlaceHollowTerritoryRadioMenuItem() - .setOnAction(getRadioHandler(MainStage.menuHollowImage, ButtonState.HOLLOW)); - menuBar.getPlacePileOfScrapTerritoryRadioMenuItem() - .setOnAction(getRadioHandler(MainStage.menuPileOfScrapImage, ButtonState.PILE_OF_SCRAP)); - menuBar.getPlaceStockpileTerritoryRadioMenuItem() - .setOnAction(getRadioHandler(MainStage.menuStockpileImage, ButtonState.STOCKPILE)); - menuBar.getPlaceAccuTerritoryRadioMenuItem() - .setOnAction(getRadioHandler(MainStage.menuAccuImage, ButtonState.ACCU)); - menuBar.getPlaceScrewTerritoryRadioMenuItem() - .setOnAction(getRadioHandler(MainStage.menuScrewImage, ButtonState.SCREW)); - menuBar.getPlaceNutTerritoryRadioMenuItem() - .setOnAction(getRadioHandler(MainStage.menuNutImage, ButtonState.NUT)); - menuBar.getDeleteFieldRadioMenuItem() - .setOnAction(getRadioHandler(MainStage.menuDeleteImage, ButtonState.CLEAR)); - // Robbi (menuBar) - menuBar.getMoveMenuItem().setOnAction(e -> { - try { - mainStage.getTerritory().getRobbi().vor(); - } catch (HollowAheadException ex) { - mainStage.getSnackbarController().showMessage(ex.getMessage()); - } - }); - menuBar.getTurnLeftMenuItem().setOnAction(e -> mainStage.getTerritory().getRobbi().linksUm()); - menuBar.getPutMenuItem().setOnAction(e -> { - try { - mainStage.getTerritory().getRobbi().legeAb(); - } catch (BagIsEmptyException | TileIsFullException ex) { - mainStage.getSnackbarController().showMessage(ex.getMessage()); - } - }); - menuBar.getTakeMenuItem().setOnAction(e -> { - try { - mainStage.getTerritory().getRobbi().nehmeAuf(); - } catch (NoItemException | BagIsFullException ex) { - mainStage.getSnackbarController().showMessage(ex.getMessage()); - } - }); - menuBar.getPushPileOfScrapMenuItem().setOnAction(e -> { - try { - mainStage.getTerritory().getRobbi().schiebeSchrotthaufen(); - } catch (NoPileOfScrapAheadException | TileBlockedException ex) { - mainStage.getSnackbarController().showMessage(ex.getMessage()); - } - }); - - menuBar.getItemPresentMenuItem().setOnAction(e -> { - boolean itemPresent = mainStage.getTerritory().getRobbi().gegenstandDa(); - mainStage.getSnackbarController().showMessage("Execution.information.itemPresent", itemPresent); - }); - menuBar.getIsStockpileMenuItem().setOnAction(e -> { - boolean stockpilePresent = mainStage.getTerritory().getRobbi().istLagerplatz(); - mainStage.getSnackbarController().showMessage("Execution.information.stockpile", stockpilePresent); - }); - menuBar.getHollowAheadMenuItem().setOnAction(e -> { - boolean hollowAhead = mainStage.getTerritory().getRobbi().vornKuhle(); - mainStage.getSnackbarController().showMessage("Execution.information.hollow", hollowAhead); - }); - menuBar.getPileOfScrapAheadMenuItem().setOnAction(e -> { - boolean pileOfScrapAhead = mainStage.getTerritory().getRobbi().vornSchrotthaufen(); - mainStage.getSnackbarController().showMessage("Execution.information.pileOfScrap", pileOfScrapAhead); - }); - menuBar.getIsBagFullMenuItem().setOnAction(e -> { - boolean isBagFull = mainStage.getTerritory().getRobbi().istTascheVoll(); - mainStage.getSnackbarController().showMessage("Execution.information.bag", isBagFull); - }); - - // simulation (menuBar) -> SimualtionController - // examples (menuBar) -> ExamplesController - // tutor (menuBar) -> TutorController / StudentController - // window (menuBar) - // language -> LangaugeController - - menuBar.getChangeCursorMenuItem().setOnAction(e -> { - setChangeCursor(menuBar.getChangeCursorMenuItem().isSelected()); - if (!menuBar.getChangeCursorMenuItem().isSelected()) - mainStage.getScene().setCursor(Cursor.DEFAULT); - }); - menuBar.getDarkModeMenuItem().selectedProperty().bindBidirectional(SceneManager.darkmodeProperty()); - SceneManager.darkmodeProperty().addListener((obs, oldVal, newVal) -> { - if (Boolean.TRUE.equals(newVal)) { - mainStage.getScene().getStylesheets().add(SceneManager.getDarkmodeCss()); - mainStage.getTextArea().getEditor().setCurrentTheme("vs-dark"); - } else { - mainStage.getScene().getStylesheets().remove(SceneManager.getDarkmodeCss()); - mainStage.getTextArea().getEditor().setCurrentTheme("vs-light"); - } - }); - if (SceneManager.getDarkmode()) { - mainStage.getScene().getStylesheets().add(SceneManager.getDarkmodeCss()); - mainStage.getTextArea().getEditor().setCurrentTheme("vs-dark"); - } - menuBar.getEnableSoundsMenuItem().selectedProperty().bindBidirectional(SoundManager.soundProperty()); - menuBar.getInfoMenuItem() - .setOnAction(e -> AlertHelper.showAlertAndWait(AlertType.INFORMATION, i18n("Menu.window.info.content"), - mainStage, Modality.WINDOW_MODAL, i18n("Menu.window.info.title"), - i18n("Menu.window.info.header"))); - menuBar.getLibraryMenuItem().setOnAction(e -> { - String javaFxVersion = System.getProperty("javafx.version"); - String javaVersion = System.getProperty("java.version"); - String derbyVersion = "10.x"; // TODO: read info from derby's info.properties - String jaxbVersion = JAXBContext.class.getPackage().getImplementationVersion(); - String hibernateVersion = Version.getVersionString(); - String lombokVersion = Generated.class.getPackage().getImplementationVersion(); - String log4jVersion = Logger.class.getPackage().getImplementationVersion(); - String jfoenixVersion = JFXUtilities.class.getPackage().getImplementationVersion(); - String monacoFxVersion = MonacoFX.class.getPackage().getImplementationVersion(); - AlertHelper.showAlertAndWait(AlertType.INFORMATION, - i18n("Menu.window.libraries.content", javaVersion, javaFxVersion, jfoenixVersion, monacoFxVersion, - derbyVersion, jaxbVersion, hibernateVersion, log4jVersion, lombokVersion), - mainStage, Modality.WINDOW_MODAL, i18n("Menu.window.libraries.title"), - i18n("Menu.window.libraries.header")); - }); - - Toolbar toolbar = mainStage.getToolbar(); - - // Editor (toolbar) - toolbar.getNewButtonToolbar().onActionProperty().bind(menuBar.getNewEditorMenuItem().onActionProperty()); - toolbar.getLoadButtonToolbar().onActionProperty().bind(menuBar.getOpenEditorMenuItem().onActionProperty()); - toolbar.getSaveButtonToolbar().onActionProperty().bind(menuBar.getSaveEditorMenuItem().onActionProperty()); - toolbar.getCompileButtonToolbar().onActionProperty() - .bind(menuBar.getCompileEditorMenuItem().onActionProperty()); - // Territory (toolbar) - toolbar.getChangeSizeButtonToolbar().onActionProperty() - .bind(menuBar.getChangeSizeTerritoryMenuItem().onActionProperty()); - toolbar.getPlaceRobbiToggleButtonToolbar() - .setOnAction(getButtonHandler(MainStage.menuRobbiImage, ButtonState.ROBBI)); - toolbar.getPlaceHollowToggleButtonToolbar() - .setOnAction(getButtonHandler(MainStage.menuHollowImage, ButtonState.HOLLOW)); - toolbar.getPlacePileOfScrapToggleButtonToolbar() - .setOnAction(getButtonHandler(MainStage.menuPileOfScrapImage, ButtonState.PILE_OF_SCRAP)); - toolbar.getPlaceStockpileToggleButtonToolbar() - .setOnAction(getButtonHandler(MainStage.menuStockpileImage, ButtonState.STOCKPILE)); - toolbar.getPlaceAccuToggleButtonToolbar() - .setOnAction(getButtonHandler(MainStage.menuAccuImage, ButtonState.ACCU)); - toolbar.getPlaceScrewToggleButtonToolbar() - .setOnAction(getButtonHandler(MainStage.menuScrewImage, ButtonState.SCREW)); - toolbar.getPlaceNutToggleButtonToolbar().setOnAction(getButtonHandler(MainStage.menuNutImage, ButtonState.NUT)); - toolbar.getDeleteFieldToggleButtonToolbar() - .setOnAction(getButtonHandler(MainStage.menuDeleteImage, ButtonState.CLEAR)); - // Robbi (Toolbar) - toolbar.getRobbiMoveButtonToolbar().onActionProperty().bind(menuBar.getMoveMenuItem().onActionProperty()); - toolbar.getRobbiTurnLeftButtonToolbar().onActionProperty() - .bind(menuBar.getTurnLeftMenuItem().onActionProperty()); - toolbar.getRobbiPutButtonToolbar().onActionProperty().bind(menuBar.getPutMenuItem().onActionProperty()); - toolbar.getRobbiTakeButtonToolbar().onActionProperty().bind(menuBar.getTakeMenuItem().onActionProperty()); - // Simulation (Toolbar) -> SimulationController - - // editor Panel - mainStage.getTextArea().getEditor().getDocument().textProperty().addListener((observalble, oldVal, newVal) -> { - Program program = mainStage.getProgram(); - boolean before = program.isEdited(); - program.setEdited(!newVal.equals(program.getEditorContent())); - if (before != program.isEdited()) - mainStage.setTitle(getTitle(program)); - }); - - } - - /** - * Saves the current territoryPanel in the given file with the given extension. - * If the file does not have the correct extension, the given extension will be - * appended to the files name. - * - * @param file The file the image should be written into - * @param extension The image extension - * @return false, if the creation failed, true otherwise - */ - private boolean saveAsImage(File file, String extension) { - - if (!file.getName().endsWith(extension)) { - file = new File(file.getAbsolutePath() + extension); - } - - TerritoryPanel tPanel = mainStage.getTerritoryPanel(); - WritableImage snapshot = tPanel.snapshot(new SnapshotParameters(), null); - - BufferedImage bufferedImage = new BufferedImage((int) tPanel.getWidth(), (int) tPanel.getHeight(), - BufferedImage.TYPE_INT_ARGB); - - BufferedImage image = SwingFXUtils.fromFXImage(snapshot, bufferedImage); - try { - logger.info("Saving territory to {}", file); - if (!ImageIO.write(image, extension.substring(1), file)) - logger.debug("Failed to find appropiate ImageWriter"); - - } catch (IOException e) { - logger.info("Failed to save territory as image"); - return false; - } - return true; - } - - /** - * Opens a FileChooser in the default programs folder and asks the user to - * select or save a file to save an image. - * - * @param description the description for the extension - * @param extension the file-extension - * @return the selected file, null otherwise - */ - private File getFile(String description, String extension) { - FileChooser chooser = new FileChooser(); - chooser.setInitialDirectory(new File(ProgramController.PATH_TO_PROGRAMS)); - chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(description, "*" + extension)); - return chooser.showSaveDialog(mainStage); - } - - /** - * Gets the title for the application by the corresponding program - * - * @param program The program for the current instance - * @return the name of the application include a * at the end, if the program - * has unsaved changes - */ - private String getTitle(Program program) { - StringBuilder builder = new StringBuilder(); - builder.append(i18n("Main.title")).append(": ").append(program.getName()) - .append((program.isEdited() ? "*" : "")); - return builder.toString(); - } - - /** - * Sends the territory to a printer in order to print it. - */ - private void printTerritory() { - - PrinterJob printerJob = PrinterJob.createPrinterJob(); - - if (printerJob != null) { - boolean flag = printerJob.showPrintDialog(mainStage); - if (!flag) - return; - TerritoryPanel node = mainStage.getTerritoryPanel(); - - // scale the node - // https://www.tabnine.com/code/java/methods/javafx.print.PageLayout/getPrintableWidth - PageLayout pageLayout = printerJob.getJobSettings().getPageLayout(); - double scaleX = 1.0; - if (pageLayout.getPrintableWidth() < node.getBoundsInParent().getWidth()) { - scaleX = pageLayout.getPrintableWidth() / node.getBoundsInParent().getWidth(); - } - double scaleY = 1.0; - if (pageLayout.getPrintableHeight() < node.getBoundsInParent().getHeight()) { - scaleY = pageLayout.getPrintableHeight() / node.getBoundsInParent().getHeight(); - } - double scaleXY = Double.min(scaleX, scaleY); - Scale scale = new Scale(scaleXY, scaleXY); - node.getTransforms().add(scale); - // Print the node - flag = printerJob.printPage(node); - node.getTransforms().remove(scale); - if (flag) { - // End the printer job - if (!printerJob.endJob()) { - logger.debug("Could not end printerJob"); - } - } else { - logger.info("Printing failed or cancled"); - } - } else { - logger.info("Failed to create printerJob"); - AlertHelper.showAlertAndWait(AlertType.ERROR, i18n("Menu.territory.print.error"), mainStage); - } - } - - /** - * Creates an EventHandler for the Toolbar buttons. This EventHandlers are only - * usable for the buttons which allow to interact with the territory, e.g. - * placing something into the territory or removing items from it. - * - * @param img The image, the cursor should change to, if the button is pressed - * @param index the buttonIndex, the buttonState should change to if the button - * is pressed - * @return the eventHandler for the Toolbar button - */ - private EventHandler getButtonHandler(Image img, int index) { - return e -> { - if (((ToggleButton) (e.getSource())).isSelected()) { - if (changeCursor && img != null) - mainStage.getMainStageScene().setCursor(new ImageCursor(img)); - buttonState.setSelected(index); - } else { - if (changeCursor) - mainStage.getMainStageScene().setCursor(Cursor.DEFAULT); - buttonState.deselect(); - } - }; - } - - /** - * Creates an EventHandler for the MenuItem buttons. This EventHandlers are only - * usable for the items which allow to interact with the territory, e.g. placing - * something into the territory or removing items from it. - * - * @param img The image, the cursor should change to, if the item is selected - * @param index the buttonIndex, the buttonState should change to if the item is - * selected - * @return the eventHandler for the MenuItem - */ - private EventHandler getRadioHandler(Image img, int index) { - return e -> { - RadioMenuItem item = ((RadioMenuItem) (e.getSource())); - if (item.equals(selectedRadioMenuItem)) { - if (changeCursor) - mainStage.getMainStageScene().setCursor(Cursor.DEFAULT); - selectedRadioMenuItem.setSelected(false); - selectedRadioMenuItem = null; - buttonState.deselect(); - } else { - if (changeCursor && img != null) - mainStage.getMainStageScene().setCursor(new ImageCursor(img)); - selectedRadioMenuItem = item; - buttonState.setSelected(index); - } - - }; - } - - @Override - public void update(Observable observable) { - if (observable instanceof Program program) { - mainStage.getTextArea().getEditor().getDocument().setText(program.getEditorContent()); - } - } + private final ButtonState buttonState; + + private final MainStage mainStage; + + @Setter + private boolean changeCursor = false; + @Getter + private final boolean soundsEnabled = false; + + private RadioMenuItem selectedRadioMenuItem = null; + + /** + * Creates a new MainStageController and sets all the actions for the buttons in + * the mainStage + * + * @param mainStage the mainStage for this controller + * @param buttonState the buttonState for this controller + */ + public MainStageController(MainStage mainStage, ButtonState buttonState) { + this.mainStage = mainStage; + this.buttonState = buttonState; + this.mainStage.getProgram().addObserver(this); + + mainStage.setTitle(i18n("Main.title") + ": " + mainStage.getProgram().getName()); + + mainStage.setOnCloseRequest(e -> { + mainStage.getProgram().save(mainStage.getTextArea().getEditor().getDocument().getText()); + ProgramController.close(mainStage.getProgram().getName()); + }); + + MenuBar menuBar = mainStage.getMenubar(); + + // editor (menuBar) + menuBar.getNewEditorMenuItem().setOnAction(e -> ProgramController.createAndShow(mainStage)); + menuBar.getOpenEditorMenuItem().setOnAction(e -> ProgramController.openProgram(mainStage)); + menuBar.getSaveEditorMenuItem().setOnAction(e -> { + mainStage.getProgram().save(mainStage.getTextArea().getEditor().getDocument().getText()); + mainStage.setTitle(getTitle(mainStage.getProgram())); + }); + + menuBar.getFormatSourceCodeMenuItem().setOnAction(e -> { + mainStage.getNotificationController().showMessage(1500, "not.implemented"); + // For this the following PR should first be merged: + // https://github.com/miho/MonacoFX/pull/25 + }); + + menuBar.getCompileEditorMenuItem().setOnAction(e -> { + mainStage.getSimulationController().stopSimulation(); + Program program = mainStage.getProgram(); + program.save(mainStage.getTextArea().getEditor().getDocument().getText()); + mainStage.setTitle(getTitle(program)); + ProgramController.compile(program, mainStage); + }); + // TODO print editor content + menuBar.getPrintEditorMenuItem() + .setOnAction(e -> mainStage.getNotificationController().showMessage(1500, "not.implemented")); + menuBar.getQuitEditorMenuItem().setOnAction(e -> { + Program program = mainStage.getProgram(); + logger.info("exiting {}", program.getName()); + program.save(mainStage.getTextArea().getEditor().getDocument().getText()); + ProgramController.close(program.getName()); + mainStage.close(); + }); + // Territory (menuBar) + // save -> TerritorySaveController + menuBar.getSaveAsPNGMenuItem().setOnAction(e -> { + String extension = ".png"; + File file = getFile(i18n("Menu.territory.saveAsPic.png.description"), extension); + if (file == null) + return; + + if (!saveAsImage(file, extension)) { + mainStage.getNotificationController().showMessage(3000, "Menu.territory.saveAsPic.error"); + } + }); + menuBar.getSaveAsGifMenuItem().setOnAction(e -> { + String extension = ".gif"; + File file = getFile(i18n("Menu.territory.saveAsPic.gif.description"), extension); + if (file == null) + return; + + if (!saveAsImage(file, extension)) { + mainStage.getNotificationController().showMessage(3000, "Menu.territory.saveAsPic.error"); + } + }); + menuBar.getPrintTerritoryMenuItem().setOnAction(e -> printTerritory()); + menuBar.getChangeSizeTerritoryMenuItem() + .setOnAction(new ChangeTerritorySizeHandler(mainStage, mainStage.getTerritory())); + + menuBar.getPlaceRobbiTerritoryRadioMenuItem() + .setOnAction(getRadioHandler(MainStage.menuRobbiImage, ButtonState.ROBBI)); + menuBar.getPlaceHollowTerritoryRadioMenuItem() + .setOnAction(getRadioHandler(MainStage.menuHollowImage, ButtonState.HOLLOW)); + menuBar.getPlacePileOfScrapTerritoryRadioMenuItem() + .setOnAction(getRadioHandler(MainStage.menuPileOfScrapImage, ButtonState.PILE_OF_SCRAP)); + menuBar.getPlaceStockpileTerritoryRadioMenuItem() + .setOnAction(getRadioHandler(MainStage.menuStockpileImage, ButtonState.STOCKPILE)); + menuBar.getPlaceAccuTerritoryRadioMenuItem() + .setOnAction(getRadioHandler(MainStage.menuAccuImage, ButtonState.ACCU)); + menuBar.getPlaceScrewTerritoryRadioMenuItem() + .setOnAction(getRadioHandler(MainStage.menuScrewImage, ButtonState.SCREW)); + menuBar.getPlaceNutTerritoryRadioMenuItem() + .setOnAction(getRadioHandler(MainStage.menuNutImage, ButtonState.NUT)); + menuBar.getDeleteFieldRadioMenuItem() + .setOnAction(getRadioHandler(MainStage.menuDeleteImage, ButtonState.CLEAR)); + // Robbi (menuBar) + menuBar.getMoveMenuItem().setOnAction(e -> { + try { + mainStage.getTerritory().getRobbi().vor(); + } catch (HollowAheadException ex) { + mainStage.getNotificationController().showMessage(3000, ex.getMessage()); + } + }); + menuBar.getTurnLeftMenuItem().setOnAction(e -> mainStage.getTerritory().getRobbi().linksUm()); + menuBar.getPutMenuItem().setOnAction(e -> { + try { + mainStage.getTerritory().getRobbi().legeAb(); + } catch (BagIsEmptyException | TileIsFullException ex) { + mainStage.getNotificationController().showMessage(3000, ex.getMessage()); + } + }); + menuBar.getTakeMenuItem().setOnAction(e -> { + try { + mainStage.getTerritory().getRobbi().nehmeAuf(); + } catch (NoItemException | BagIsFullException ex) { + mainStage.getNotificationController().showMessage(3000, ex.getMessage()); + } + }); + menuBar.getPushPileOfScrapMenuItem().setOnAction(e -> { + try { + mainStage.getTerritory().getRobbi().schiebeSchrotthaufen(); + } catch (NoPileOfScrapAheadException | TileBlockedException ex) { + mainStage.getNotificationController().showMessage(3000, ex.getMessage()); + } + }); + + menuBar.getItemPresentMenuItem().setOnAction(e -> { + boolean itemPresent = mainStage.getTerritory().getRobbi().gegenstandDa(); + mainStage.getNotificationController().showMessage(3000, "Execution.information.itemPresent", itemPresent); + }); + menuBar.getIsStockpileMenuItem().setOnAction(e -> { + boolean stockpilePresent = mainStage.getTerritory().getRobbi().istLagerplatz(); + mainStage.getNotificationController().showMessage(3000, "Execution.information.stockpile", stockpilePresent); + }); + menuBar.getHollowAheadMenuItem().setOnAction(e -> { + boolean hollowAhead = mainStage.getTerritory().getRobbi().vornKuhle(); + mainStage.getNotificationController().showMessage(3000, "Execution.information.hollow", hollowAhead); + }); + menuBar.getPileOfScrapAheadMenuItem().setOnAction(e -> { + boolean pileOfScrapAhead = mainStage.getTerritory().getRobbi().vornSchrotthaufen(); + mainStage.getNotificationController().showMessage(3000, "Execution.information.pileOfScrap", pileOfScrapAhead); + }); + menuBar.getIsBagFullMenuItem().setOnAction(e -> { + boolean isBagFull = mainStage.getTerritory().getRobbi().istTascheVoll(); + mainStage.getNotificationController().showMessage(3000, "Execution.information.bag", isBagFull); + }); + + // simulation (menuBar) -> SimualtionController + // examples (menuBar) -> ExamplesController + // tutor (menuBar) -> TutorController / StudentController + // window (menuBar) + // language -> LangaugeController + + menuBar.getChangeCursorMenuItem().setOnAction(e -> { + setChangeCursor(menuBar.getChangeCursorMenuItem().isSelected()); + if (!menuBar.getChangeCursorMenuItem().isSelected()) + mainStage.getScene().setCursor(Cursor.DEFAULT); + }); + menuBar.getDarkModeMenuItem().selectedProperty().bindBidirectional(SceneManager.darkmodeProperty()); + SceneManager.darkmodeProperty().addListener((obs, oldVal, newVal) -> { + if (Boolean.TRUE.equals(newVal)) { + mainStage.getScene().getStylesheets().add(SceneManager.getDarkmodeCss()); + mainStage.getTextArea().getEditor().setCurrentTheme("vs-dark"); + } else { + mainStage.getScene().getStylesheets().remove(SceneManager.getDarkmodeCss()); + mainStage.getTextArea().getEditor().setCurrentTheme("vs-light"); + } + }); + if (SceneManager.getDarkmode()) { + mainStage.getScene().getStylesheets().add(SceneManager.getDarkmodeCss()); + mainStage.getTextArea().getEditor().setCurrentTheme("vs-dark"); + } + menuBar.getEnableSoundsMenuItem().selectedProperty().bindBidirectional(SoundManager.soundProperty()); + menuBar.getInfoMenuItem() + .setOnAction(e -> AlertHelper.showAlertAndWait(AlertType.INFORMATION, i18n("Menu.window.info.content"), + mainStage, Modality.WINDOW_MODAL, i18n("Menu.window.info.title"), + i18n("Menu.window.info.header"))); + menuBar.getLibraryMenuItem().setOnAction(e -> { + String javaFxVersion = System.getProperty("javafx.version"); + String javaVersion = System.getProperty("java.version"); + String derbyVersion = "10.x"; // TODO: read info from derby's info.properties + String jaxbVersion = JAXBContext.class.getPackage().getImplementationVersion(); + String hibernateVersion = Version.getVersionString(); + String lombokVersion = Generated.class.getPackage().getImplementationVersion(); + String log4jVersion = Logger.class.getPackage().getImplementationVersion(); + String monacoFxVersion = MonacoFX.class.getPackage().getImplementationVersion(); + AlertHelper.showAlertAndWait(AlertType.INFORMATION, + i18n("Menu.window.libraries.content", javaVersion, javaFxVersion, monacoFxVersion, + derbyVersion, jaxbVersion, hibernateVersion, log4jVersion, lombokVersion), + mainStage, Modality.WINDOW_MODAL, i18n("Menu.window.libraries.title"), + i18n("Menu.window.libraries.header")); + }); + + Toolbar toolbar = mainStage.getToolbar(); + + // Editor (toolbar) + toolbar.getNewButtonToolbar().onActionProperty().bind(menuBar.getNewEditorMenuItem().onActionProperty()); + toolbar.getLoadButtonToolbar().onActionProperty().bind(menuBar.getOpenEditorMenuItem().onActionProperty()); + toolbar.getSaveButtonToolbar().onActionProperty().bind(menuBar.getSaveEditorMenuItem().onActionProperty()); + toolbar.getCompileButtonToolbar().onActionProperty() + .bind(menuBar.getCompileEditorMenuItem().onActionProperty()); + // Territory (toolbar) + toolbar.getChangeSizeButtonToolbar().onActionProperty() + .bind(menuBar.getChangeSizeTerritoryMenuItem().onActionProperty()); + toolbar.getPlaceRobbiToggleButtonToolbar() + .setOnAction(getButtonHandler(MainStage.menuRobbiImage, ButtonState.ROBBI)); + toolbar.getPlaceHollowToggleButtonToolbar() + .setOnAction(getButtonHandler(MainStage.menuHollowImage, ButtonState.HOLLOW)); + toolbar.getPlacePileOfScrapToggleButtonToolbar() + .setOnAction(getButtonHandler(MainStage.menuPileOfScrapImage, ButtonState.PILE_OF_SCRAP)); + toolbar.getPlaceStockpileToggleButtonToolbar() + .setOnAction(getButtonHandler(MainStage.menuStockpileImage, ButtonState.STOCKPILE)); + toolbar.getPlaceAccuToggleButtonToolbar() + .setOnAction(getButtonHandler(MainStage.menuAccuImage, ButtonState.ACCU)); + toolbar.getPlaceScrewToggleButtonToolbar() + .setOnAction(getButtonHandler(MainStage.menuScrewImage, ButtonState.SCREW)); + toolbar.getPlaceNutToggleButtonToolbar().setOnAction(getButtonHandler(MainStage.menuNutImage, ButtonState.NUT)); + toolbar.getDeleteFieldToggleButtonToolbar() + .setOnAction(getButtonHandler(MainStage.menuDeleteImage, ButtonState.CLEAR)); + // Robbi (Toolbar) + toolbar.getRobbiMoveButtonToolbar().onActionProperty().bind(menuBar.getMoveMenuItem().onActionProperty()); + toolbar.getRobbiTurnLeftButtonToolbar().onActionProperty() + .bind(menuBar.getTurnLeftMenuItem().onActionProperty()); + toolbar.getRobbiPutButtonToolbar().onActionProperty().bind(menuBar.getPutMenuItem().onActionProperty()); + toolbar.getRobbiTakeButtonToolbar().onActionProperty().bind(menuBar.getTakeMenuItem().onActionProperty()); + // Simulation (Toolbar) -> SimulationController + + // editor Panel + mainStage.getTextArea().getEditor().getDocument().textProperty().addListener((observalble, oldVal, newVal) -> { + Program program = mainStage.getProgram(); + boolean before = program.isEdited(); + program.setEdited(!newVal.equals(program.getEditorContent())); + if (before != program.isEdited()) + mainStage.setTitle(getTitle(program)); + }); + + } + + /** + * Saves the current territoryPanel in the given file with the given extension. + * If the file does not have the correct extension, the given extension will be + * appended to the files name. + * + * @param file The file the image should be written into + * @param extension The image extension + * @return false, if the creation failed, true otherwise + */ + private boolean saveAsImage(File file, String extension) { + + if (!file.getName().endsWith(extension)) { + file = new File(file.getAbsolutePath() + extension); + } + + TerritoryPanel tPanel = mainStage.getTerritoryPanel(); + WritableImage snapshot = tPanel.snapshot(new SnapshotParameters(), null); + + BufferedImage bufferedImage = new BufferedImage((int) tPanel.getWidth(), (int) tPanel.getHeight(), + BufferedImage.TYPE_INT_ARGB); + + BufferedImage image = SwingFXUtils.fromFXImage(snapshot, bufferedImage); + try { + logger.info("Saving territory to {}", file); + if (!ImageIO.write(image, extension.substring(1), file)) + logger.debug("Failed to find appropiate ImageWriter"); + + } catch (IOException e) { + logger.info("Failed to save territory as image"); + return false; + } + return true; + } + + /** + * Opens a FileChooser in the default programs folder and asks the user to + * select or save a file to save an image. + * + * @param description the description for the extension + * @param extension the file-extension + * @return the selected file, null otherwise + */ + private File getFile(String description, String extension) { + FileChooser chooser = new FileChooser(); + chooser.setInitialDirectory(new File(ProgramController.PATH_TO_PROGRAMS)); + chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(description, "*" + extension)); + return chooser.showSaveDialog(mainStage); + } + + /** + * Gets the title for the application by the corresponding program + * + * @param program The program for the current instance + * @return the name of the application include a * at the end, if the program + * has unsaved changes + */ + private String getTitle(Program program) { + return i18n("Main.title") + ": " + program.getName() + (program.isEdited() ? "*" : ""); + } + + /** + * Sends the territory to a printer in order to print it. + */ + private void printTerritory() { + + PrinterJob printerJob = PrinterJob.createPrinterJob(); + + if (printerJob != null) { + boolean flag = printerJob.showPrintDialog(mainStage); + if (!flag) + return; + TerritoryPanel node = mainStage.getTerritoryPanel(); + + // scale the node + // https://www.tabnine.com/code/java/methods/javafx.print.PageLayout/getPrintableWidth + PageLayout pageLayout = printerJob.getJobSettings().getPageLayout(); + double scaleX = 1.0; + if (pageLayout.getPrintableWidth() < node.getBoundsInParent().getWidth()) { + scaleX = pageLayout.getPrintableWidth() / node.getBoundsInParent().getWidth(); + } + double scaleY = 1.0; + if (pageLayout.getPrintableHeight() < node.getBoundsInParent().getHeight()) { + scaleY = pageLayout.getPrintableHeight() / node.getBoundsInParent().getHeight(); + } + double scaleXY = Double.min(scaleX, scaleY); + Scale scale = new Scale(scaleXY, scaleXY); + node.getTransforms().add(scale); + // Print the node + flag = printerJob.printPage(node); + node.getTransforms().remove(scale); + if (flag) { + // End the printer job + if (!printerJob.endJob()) { + logger.debug("Could not end printerJob"); + } + } else { + logger.info("Printing failed or cancled"); + } + } else { + logger.info("Failed to create printerJob"); + AlertHelper.showAlertAndWait(AlertType.ERROR, i18n("Menu.territory.print.error"), mainStage); + } + } + + /** + * Creates an EventHandler for the Toolbar buttons. This EventHandlers are only + * usable for the buttons which allow to interact with the territory, e.g. + * placing something into the territory or removing items from it. + * + * @param img The image, the cursor should change to, if the button is pressed + * @param index the buttonIndex, the buttonState should change to if the button + * is pressed + * @return the eventHandler for the Toolbar button + */ + private EventHandler getButtonHandler(Image img, int index) { + return e -> { + if (((ToggleButton) (e.getSource())).isSelected()) { + if (changeCursor && img != null) + mainStage.getMainStageScene().setCursor(new ImageCursor(img)); + buttonState.setSelected(index); + } else { + if (changeCursor) + mainStage.getMainStageScene().setCursor(Cursor.DEFAULT); + buttonState.deselect(); + } + }; + } + + /** + * Creates an EventHandler for the MenuItem buttons. This EventHandlers are only + * usable for the items which allow to interact with the territory, e.g. placing + * something into the territory or removing items from it. + * + * @param img The image, the cursor should change to, if the item is selected + * @param index the buttonIndex, the buttonState should change to if the item is + * selected + * @return the eventHandler for the MenuItem + */ + private EventHandler getRadioHandler(Image img, int index) { + return e -> { + RadioMenuItem item = ((RadioMenuItem) (e.getSource())); + if (item.equals(selectedRadioMenuItem)) { + if (changeCursor) + mainStage.getMainStageScene().setCursor(Cursor.DEFAULT); + selectedRadioMenuItem.setSelected(false); + selectedRadioMenuItem = null; + buttonState.deselect(); + } else { + if (changeCursor && img != null) + mainStage.getMainStageScene().setCursor(new ImageCursor(img)); + selectedRadioMenuItem = item; + buttonState.setSelected(index); + } + + }; + } + + @Override + public void update(Observable observable) { + if (observable instanceof Program program) { + mainStage.getTextArea().getEditor().getDocument().setText(program.getEditorContent()); + } + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/MethodHandler.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/MethodHandler.java index 9e133ac..7158944 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/MethodHandler.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/MethodHandler.java @@ -1,33 +1,34 @@ package com.JayPi4c.RobbiSimulator.controller; -import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.util.ArrayList; -import java.util.List; - import com.JayPi4c.RobbiSimulator.model.RobbiException; import com.JayPi4c.RobbiSimulator.model.Territory; import com.JayPi4c.RobbiSimulator.utils.AlertHelper; import com.JayPi4c.RobbiSimulator.utils.I18nUtils; import com.JayPi4c.RobbiSimulator.utils.annotations.Default; import com.JayPi4c.RobbiSimulator.view.MainStage; - import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.control.Alert.AlertType; +import lombok.extern.slf4j.Slf4j; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.ArrayList; +import java.util.List; /** * MethodHandler to handle a methods invocation * * @author Jonas Pohl */ +@Slf4j public class MethodHandler implements EventHandler { - private Method method; - private Territory territory; - private MainStage parent; + private final Method method; + private final Territory territory; + private final MainStage parent; /** * Creates a new MethodHandler with the method and the territory the message is @@ -65,13 +66,13 @@ public void handle(ActionEvent event) { if (result != null) { AlertHelper.showAlertAndWait(AlertType.INFORMATION, - I18nUtils.i18n("Execution.information.result") + result.toString(), parent); + I18nUtils.i18n("Execution.information.result") + result, parent); } } catch (IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); + logger.error("Failed to invoke method", e); } catch (InvocationTargetException e) { if (e.getCause() instanceof RobbiException) { - parent.getSnackbarController().showMessage(e.getCause().getLocalizedMessage()); + parent.getNotificationController().showMessage(3000, e.getCause().getLocalizedMessage()); } else { AlertHelper.showAlertAndWait(AlertType.ERROR, e.getCause().getMessage(), parent); } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/NotificationController.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/NotificationController.java new file mode 100644 index 0000000..c65ceb7 --- /dev/null +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/NotificationController.java @@ -0,0 +1,49 @@ +package com.JayPi4c.RobbiSimulator.controller; + +import javafx.animation.KeyFrame; +import javafx.animation.Timeline; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.util.Duration; +import lombok.extern.slf4j.Slf4j; +import org.controlsfx.control.NotificationPane; + +import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; + +@Slf4j +public class NotificationController { + + private final NotificationPane notificationPane; + private final Timeline hideTimeline = new Timeline(); + + public NotificationController(Node node) { + notificationPane = new NotificationPane(node); + notificationPane.setShowFromTop(false); + notificationPane.getStyleClass().add(NotificationPane.STYLE_CLASS_DARK); + hideTimeline.setCycleCount(1); + } + + public Parent getScene() { + return notificationPane; + } + + /** + * Shows a message in a notification pane. The message will be shown for the given timeout in milliseconds. + * If a message is already shown, it will be replaced by the new message and the timeout will be reset. + * + * @param timeout in milliseconds + * @param key the key of the message + * @param args the arguments for the message + */ + public void showMessage(int timeout, String key, Object... args) { + hideTimeline.stop(); + notificationPane.show(i18n(key, args)); + KeyFrame kf = new KeyFrame(Duration.millis(timeout), e -> { + if (notificationPane.isShowing()) + notificationPane.hide(); + }); + hideTimeline.getKeyFrames().setAll(kf); + hideTimeline.play(); + } + +} diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/SnackbarController.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/SnackbarController.java deleted file mode 100644 index ca39c0b..0000000 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/SnackbarController.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.JayPi4c.RobbiSimulator.controller; - -import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; - -import com.jfoenix.controls.JFXSnackbar; -import com.jfoenix.controls.JFXSnackbar.SnackbarEvent; -import com.jfoenix.controls.JFXSnackbarLayout; - -import javafx.scene.layout.Pane; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class SnackbarController { - - private JFXSnackbar snackbar; - - public SnackbarController(Pane root) { - snackbar = new JFXSnackbar(root); - } - - public void showMessage(String key, Object... args) { - logger.debug("Showing snackbar-message: {}; {}", key, i18n(key, args)); - snackbar.fireEvent(new SnackbarEvent(new JFXSnackbarLayout(i18n(key, args)))); - } - -} diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/TerritoryEventHandler.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/TerritoryEventHandler.java index 62fbc2a..fc26435 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/TerritoryEventHandler.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/TerritoryEventHandler.java @@ -4,121 +4,119 @@ import com.JayPi4c.RobbiSimulator.view.MainStage; import com.JayPi4c.RobbiSimulator.view.RobbiContextMenu; import com.JayPi4c.RobbiSimulator.view.TerritoryPanel; - import javafx.event.EventHandler; import javafx.scene.input.MouseEvent; /** * This class combines all functions needed to control the user interaction with * the territory - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public class TerritoryEventHandler implements EventHandler { - private Territory territory; + private final Territory territory; - private boolean robbiDragged = false; + private boolean robbiDragged = false; - private ButtonState buttonState; + private final ButtonState buttonState; - private RobbiContextMenu robbiContextMenu; + private RobbiContextMenu robbiContextMenu; - /** - * Creates a new TerritoryEventHandler and sets the contextMenuRequest to show a - * RobbiContextMenu if the user right-clicks on Robbi - * - * @param territory the Territory this handler is for - * @param territoryPanel the TerritoryPanel, this handler is handling - * @param buttonState the ButtonState for this eventHandler - * @param parent the parent window to show alerts relative to it - */ - public TerritoryEventHandler(Territory territory, TerritoryPanel territoryPanel, ButtonState buttonState, - MainStage parent) { - this.territory = territory; - this.buttonState = buttonState; - territoryPanel.setOnContextMenuRequested(event -> { - if (territory.robbiOnTile(getCol(event.getX()), getRow(event.getY()))) { - robbiContextMenu = new RobbiContextMenu(territory, parent); - robbiContextMenu.show(territoryPanel.getScene().getWindow(), event.getScreenX(), event.getScreenY()); - } - }); + /** + * Creates a new TerritoryEventHandler and sets the contextMenuRequest to show a + * RobbiContextMenu if the user right-clicks on Robbi + * + * @param territory the Territory this handler is for + * @param territoryPanel the TerritoryPanel, this handler is handling + * @param buttonState the ButtonState for this eventHandler + * @param parent the parent window to show alerts relative to it + */ + public TerritoryEventHandler(Territory territory, TerritoryPanel territoryPanel, ButtonState buttonState, + MainStage parent) { + this.territory = territory; + this.buttonState = buttonState; + territoryPanel.setOnContextMenuRequested(event -> { + if (territory.robbiOnTile(getCol(event.getX()), getRow(event.getY()))) { + robbiContextMenu = new RobbiContextMenu(territory, parent); + robbiContextMenu.show(territoryPanel.getScene().getWindow(), event.getScreenX(), event.getScreenY()); + } + }); - } + } - /** - * {@inheritDoc}
- * Places an Item or Object in the territory or drags and drops the robbi - */ - @Override - public void handle(MouseEvent event) { - int col = getCol(event.getX()); - int row = getRow(event.getY()); - if (event.getEventType() == MouseEvent.MOUSE_PRESSED) { - robbiDragged = territory.robbiOnTile(col, row); - } else if (event.getEventType() == MouseEvent.MOUSE_DRAGGED) { - if (robbiDragged) - territory.placeRobbi(col, row); - } else if (event.getEventType() == MouseEvent.MOUSE_RELEASED) { - if (!robbiDragged) { - switch (buttonState.getSelected()) { - case ButtonState.ROBBI: - territory.placeRobbi(col, row); - break; - case ButtonState.HOLLOW: - territory.placeHollow(col, row); - break; - case ButtonState.PILE_OF_SCRAP: - territory.placePileOfScrap(col, row); - break; - case ButtonState.STOCKPILE: - territory.placeStockpile(col, row); - break; - case ButtonState.ACCU: - territory.placeAccu(col, row); - break; - case ButtonState.SCREW: - territory.placeScrew(col, row); - break; - case ButtonState.NUT: - territory.placeNut(col, row); - break; - case ButtonState.CLEAR: - territory.clearTile(col, row); - break; - case ButtonState.NONE: - // fall through - default: - // Do nothing - } - } - robbiDragged = false; - } + /** + * {@inheritDoc}
+ * Places an Item or Object in the territory or drags and drops the robbi + */ + @Override + public void handle(MouseEvent event) { + int col = getCol(event.getX()); + int row = getRow(event.getY()); + if (event.getEventType() == MouseEvent.MOUSE_PRESSED) { + robbiDragged = territory.robbiOnTile(col, row); + } else if (event.getEventType() == MouseEvent.MOUSE_DRAGGED) { + if (robbiDragged) + territory.placeRobbi(col, row); + } else if (event.getEventType() == MouseEvent.MOUSE_RELEASED) { + if (!robbiDragged) { + switch (buttonState.getSelected()) { + case ButtonState.ROBBI: + territory.placeRobbi(col, row); + break; + case ButtonState.HOLLOW: + territory.placeHollow(col, row); + break; + case ButtonState.PILE_OF_SCRAP: + territory.placePileOfScrap(col, row); + break; + case ButtonState.STOCKPILE: + territory.placeStockpile(col, row); + break; + case ButtonState.ACCU: + territory.placeAccu(col, row); + break; + case ButtonState.SCREW: + territory.placeScrew(col, row); + break; + case ButtonState.NUT: + territory.placeNut(col, row); + break; + case ButtonState.CLEAR: + territory.clearTile(col, row); + break; + case ButtonState.NONE: + // fall through + default: + // Do nothing + } + } + robbiDragged = false; + } - } + } - /** - * Calculates the column to a given horizontal mouse-position - * - * @param x Horizontal position of the event relative to the origin of the - * MouseEvent's source. - * @return column corresponding to the given x - */ - private int getCol(double x) { - return (int) (x / (TerritoryPanel.getCellsize() + TerritoryPanel.getCellspacer())); - } + /** + * Calculates the column to a given horizontal mouse-position + * + * @param x Horizontal position of the event relative to the origin of the + * MouseEvent's source. + * @return column corresponding to the given x + */ + private int getCol(double x) { + return (int) (x / (TerritoryPanel.getCellsize() + TerritoryPanel.getCellspacer())); + } - /** - * Calculates the row to a given vertical mouse-position - * - * @param y Vertical position of the event relative to the origin of the - * MouseEvent's source. - * @return row corresponding to the given y - */ - private int getRow(double y) { - return (int) (y / (TerritoryPanel.getCellsize() + TerritoryPanel.getCellspacer())); + /** + * Calculates the row to a given vertical mouse-position + * + * @param y Vertical position of the event relative to the origin of the + * MouseEvent's source. + * @return row corresponding to the given y + */ + private int getRow(double y) { + return (int) (y / (TerritoryPanel.getCellsize() + TerritoryPanel.getCellspacer())); - } + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/TerritorySaveController.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/TerritorySaveController.java index 4a07c0d..c901caa 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/TerritorySaveController.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/TerritorySaveController.java @@ -1,36 +1,9 @@ package com.JayPi4c.RobbiSimulator.controller; -import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OutputStream; -import java.io.Writer; -import java.util.Optional; - import com.JayPi4c.RobbiSimulator.controller.program.ProgramController; -import com.JayPi4c.RobbiSimulator.model.Accu; -import com.JayPi4c.RobbiSimulator.model.DIRECTION; -import com.JayPi4c.RobbiSimulator.model.Hollow; -import com.JayPi4c.RobbiSimulator.model.InvalidTerritoryException; -import com.JayPi4c.RobbiSimulator.model.Item; -import com.JayPi4c.RobbiSimulator.model.Nut; -import com.JayPi4c.RobbiSimulator.model.PileOfScrap; -import com.JayPi4c.RobbiSimulator.model.Screw; -import com.JayPi4c.RobbiSimulator.model.Stockpile; -import com.JayPi4c.RobbiSimulator.model.Territory; -import com.JayPi4c.RobbiSimulator.model.TerritoryState; +import com.JayPi4c.RobbiSimulator.model.*; import com.JayPi4c.RobbiSimulator.utils.AlertHelper; import com.JayPi4c.RobbiSimulator.view.MainStage; - import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBException; import jakarta.xml.bind.Marshaller; @@ -39,242 +12,242 @@ import javafx.stage.FileChooser; import lombok.extern.slf4j.Slf4j; +import java.io.*; +import java.util.Optional; + +import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; + /** * Controller to handle all territory save action. It handles load and save * actions for all supported formats. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Slf4j public class TerritorySaveController { - private static final String DEFAULT_SERIALISATION_FILE_EXTENSION = ".ter"; - private static final String DEFAULT_XML_FILE_EXTENSION = ".rsxml"; - private static final String DEFAULT_JAXB_FILE_EXTENSION = ".rsjaxb"; - - private MainStage mainStage; - - /** - * Constructor to create a new TerritorySaveController for the given mainStage. - * - * @param mainStage the mainStage this controller is for - */ - public TerritorySaveController(MainStage mainStage) { - this.mainStage = mainStage; - this.mainStage.getMenubar().getSaveSerialTerritoryMenuItem().setOnAction(e -> serialize()); - this.mainStage.getMenubar().getLoadSerialTerritoryMenuItem().setOnAction(e -> deserialize()); - - this.mainStage.getMenubar().getSaveXMLTerritoryMenuItem().setOnAction(e -> saveXMLtoFile()); - this.mainStage.getMenubar().getLoadXMLTerritoryMenuItem().setOnAction(e -> loadXMLfromFile()); - - this.mainStage.getMenubar().getSaveJAXBTerritoryMenuItem().setOnAction(e -> saveJAXB()); - this.mainStage.getMenubar().getLoadJAXBTerritoryMenuItem().setOnAction(e -> loadJAXB()); - } - - /** - * Helper to serialize the territory of the mainStage into a file. - */ - private void serialize() { - Optional fileOpt = getSaveFile(i18n("Territory.save.dialog.title"), - i18n("Territory.save.dialog.filter.serial"), DEFAULT_SERIALISATION_FILE_EXTENSION); - - if (fileOpt.isEmpty()) { - logger.debug("No file was selected to serialze in."); - return; - } - - File file = fileOpt.get(); - if (!file.getName().endsWith(DEFAULT_SERIALISATION_FILE_EXTENSION)) { - file = new File(file.getAbsolutePath() + DEFAULT_SERIALISATION_FILE_EXTENSION); - } - - logger.debug("serialize in file {}", file); - try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) { - synchronized (mainStage.getTerritory()) { - oos.writeObject(mainStage.getTerritory()); - oos.writeObject(mainStage.getTerritory().getRobbiItem()); - oos.writeInt(mainStage.getTerritory().getRobbiX()); - oos.writeInt(mainStage.getTerritory().getRobbiY()); - oos.writeObject(mainStage.getTerritory().getRobbiDirection()); - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - logger.info("finished serialization"); - } - - } - - /** - * Helper to deserialize a territory from a file and update the old territory to - * the new values. - */ - private void deserialize() { - Optional fileOpt = getLoadFile(i18n("Territory.load.dialog.title"), - i18n("Territory.load.dialog.filter.deserial"), DEFAULT_SERIALISATION_FILE_EXTENSION); - - if (fileOpt.isEmpty()) { - logger.debug("No file was selected to deserialize from."); - return; - } - File file = fileOpt.get(); - - try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { - logger.debug("deserialize from file {}", file); - Territory t = (Territory) ois.readObject(); - Item item = (Item) ois.readObject(); - int x = ois.readInt(); - int y = ois.readInt(); - DIRECTION facing = (DIRECTION) ois.readObject(); - mainStage.getTerritory().update(t, item, x, y, facing); - } catch (InvalidTerritoryException e) { - AlertHelper.showAlertAndWait(AlertType.WARNING, i18n("Territory.load.failure"), mainStage); - } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); - } finally { - logger.info("finished deserialization"); - } - } - - /** - * Asks for a file to save the xml to and then writes the territory into this - * file. - */ - private void saveXMLtoFile() { - Optional fileOpt = getSaveFile(i18n("Territory.save.dialog.title"), - i18n("Territory.save.dialog.filter.xml"), DEFAULT_XML_FILE_EXTENSION); - - if (fileOpt.isEmpty()) { - logger.debug("No file selected to save territory in."); - return; - } - File file = fileOpt.get(); - - if (!file.getName().endsWith(DEFAULT_XML_FILE_EXTENSION)) { - file = new File(file.getAbsolutePath() + DEFAULT_XML_FILE_EXTENSION); - } - - logger.debug("save as XML in file {}", file); - try (ByteArrayOutputStream baos = mainStage.getTerritory().toXML(); - OutputStream outputStream = new FileOutputStream(file.getAbsolutePath())) { - baos.writeTo(outputStream); - } catch (IOException e) { - logger.debug("failed"); - } - - } - - /** - * Asks for an XML-File and loads the contents into the territory. - */ - private void loadXMLfromFile() { - Optional fileOpt = getLoadFile(i18n("Territory.load.dialog.title"), - i18n("Territory.load.dialog.filter.xml"), DEFAULT_XML_FILE_EXTENSION); - - if (fileOpt.isEmpty()) { - logger.debug("No file selected to load XML from."); - return; - } - - File file = fileOpt.get(); - logger.debug("load territory from xml-file {}", file); - try { - if (mainStage.getTerritory().fromXML(new FileInputStream(file))) - logger.info("finished loading from xml-file"); - else - logger.info("Failed loading from xml-file"); - } catch (FileNotFoundException e) { - logger.debug("Could not find file {}", file.getAbsolutePath()); - } - - } - - /** - * Loads a territory by a filename using JAXB - * - * @return true if the territory was loaded successfully, false otherwise - */ - private void loadJAXB() { - Optional fileOpt = getLoadFile(i18n("Territory.load.dialog.title"), - i18n("Territory.load.dialog.filter.jaxb"), DEFAULT_JAXB_FILE_EXTENSION); - - if (fileOpt.isEmpty()) { - logger.debug("No file selected"); - return; - } - File file = fileOpt.get(); - logger.debug("load territory from jaxb-file {}", file); - try { - JAXBContext context = JAXBContext.newInstance(TerritoryState.class, Nut.class, Screw.class, Accu.class, - Stockpile.class, PileOfScrap.class, Hollow.class); - Unmarshaller um = context.createUnmarshaller(); - TerritoryState ter = (TerritoryState) um.unmarshal(new FileReader(file)); - mainStage.getTerritory().restore(ter); - } catch (IOException | JAXBException e) { - e.printStackTrace(); - logger.debug("failed to load JAXB"); - } - } - - /** - * Saves the territory using JAXB. - */ - private void saveJAXB() { - Optional fileOpt = getSaveFile(i18n("Territory.save.dialog.title"), - i18n("Territory.save.dialog.filter.jaxb"), DEFAULT_JAXB_FILE_EXTENSION); - - if (fileOpt.isEmpty()) { - logger.debug("No file selected to save territory in."); - return; - } - File file = fileOpt.get(); - if (!file.getName().endsWith(DEFAULT_JAXB_FILE_EXTENSION)) { - file = new File(file.getAbsolutePath() + DEFAULT_JAXB_FILE_EXTENSION); - } - logger.debug("save territory from jaxb-file {}", file); - try (Writer w = new FileWriter(file)) { - JAXBContext context = JAXBContext.newInstance(TerritoryState.class, Nut.class, Screw.class, Accu.class, - Stockpile.class, PileOfScrap.class, Hollow.class); - Marshaller m = context.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); - m.marshal(mainStage.getTerritory().save(), w); - } catch (IOException | JAXBException e) { - e.printStackTrace(); - logger.debug("failed to save jaxb"); - } - } - - /** - * Opens a dialog to ask for a file to save the territory in. - * - * @param title the title of the dialog - * @param description file description for the allowed files - * @param fileExtension allowed file-extension - * @return a file to save the territory in - */ - private Optional getSaveFile(String title, String description, String fileExtension) { - FileChooser chooser = new FileChooser(); - chooser.setTitle(title); - chooser.setInitialDirectory(new File(ProgramController.PATH_TO_PROGRAMS)); - chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(description, "*" + fileExtension)); - return Optional.ofNullable(chooser.showSaveDialog(mainStage)); - } - - /** - * Opens a dialog to ask for a file to load a territory from. - * - * @param title the title of the dialog - * @param description file description for allowed files - * @param fileExtension allowed file-extension - * @return a file to load a territory from - */ - public Optional getLoadFile(String title, String description, String fileExtension) { - FileChooser fileChooser = new FileChooser(); - fileChooser.setTitle(title); - fileChooser.setInitialDirectory(new File(ProgramController.PATH_TO_PROGRAMS)); - fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(description, "*" + fileExtension)); - return Optional.ofNullable(fileChooser.showOpenDialog(mainStage)); - } + private static final String DEFAULT_SERIALISATION_FILE_EXTENSION = ".ter"; + private static final String DEFAULT_XML_FILE_EXTENSION = ".rsxml"; + private static final String DEFAULT_JAXB_FILE_EXTENSION = ".rsjaxb"; + + private final MainStage mainStage; + + /** + * Constructor to create a new TerritorySaveController for the given mainStage. + * + * @param mainStage the mainStage this controller is for + */ + public TerritorySaveController(MainStage mainStage) { + this.mainStage = mainStage; + this.mainStage.getMenubar().getSaveSerialTerritoryMenuItem().setOnAction(e -> serialize()); + this.mainStage.getMenubar().getLoadSerialTerritoryMenuItem().setOnAction(e -> deserialize()); + + this.mainStage.getMenubar().getSaveXMLTerritoryMenuItem().setOnAction(e -> saveXMLtoFile()); + this.mainStage.getMenubar().getLoadXMLTerritoryMenuItem().setOnAction(e -> loadXMLfromFile()); + + this.mainStage.getMenubar().getSaveJAXBTerritoryMenuItem().setOnAction(e -> saveJAXB()); + this.mainStage.getMenubar().getLoadJAXBTerritoryMenuItem().setOnAction(e -> loadJAXB()); + } + + /** + * Helper to serialize the territory of the mainStage into a file. + */ + private void serialize() { + Optional fileOpt = getSaveFile(i18n("Territory.save.dialog.title"), + i18n("Territory.save.dialog.filter.serial"), DEFAULT_SERIALISATION_FILE_EXTENSION); + + if (fileOpt.isEmpty()) { + logger.debug("No file was selected to serialze in."); + return; + } + + File file = fileOpt.get(); + if (!file.getName().endsWith(DEFAULT_SERIALISATION_FILE_EXTENSION)) { + file = new File(file.getAbsolutePath() + DEFAULT_SERIALISATION_FILE_EXTENSION); + } + + logger.debug("serialize in file {}", file); + try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) { + synchronized (mainStage.getTerritory()) { + oos.writeObject(mainStage.getTerritory()); + oos.writeObject(mainStage.getTerritory().getRobbiItem()); + oos.writeInt(mainStage.getTerritory().getRobbiX()); + oos.writeInt(mainStage.getTerritory().getRobbiY()); + oos.writeObject(mainStage.getTerritory().getRobbiDirection()); + } + } catch (IOException e) { + logger.error("Failed to serialize territory", e); + } finally { + logger.info("finished serialization"); + } + + } + + /** + * Helper to deserialize a territory from a file and update the old territory to + * the new values. + */ + private void deserialize() { + Optional fileOpt = getLoadFile(i18n("Territory.load.dialog.title"), + i18n("Territory.load.dialog.filter.deserial"), DEFAULT_SERIALISATION_FILE_EXTENSION); + + if (fileOpt.isEmpty()) { + logger.debug("No file was selected to deserialize from."); + return; + } + File file = fileOpt.get(); + + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { + logger.debug("deserialize from file {}", file); + Territory t = (Territory) ois.readObject(); + Item item = (Item) ois.readObject(); + int x = ois.readInt(); + int y = ois.readInt(); + DIRECTION facing = (DIRECTION) ois.readObject(); + mainStage.getTerritory().update(t, item, x, y, facing); + } catch (InvalidTerritoryException e) { + AlertHelper.showAlertAndWait(AlertType.WARNING, i18n("Territory.load.failure"), mainStage); + } catch (IOException | ClassNotFoundException e) { + logger.error("Failed to deserialize territory", e); + } finally { + logger.info("finished deserialization"); + } + } + + /** + * Asks for a file to save the xml to and then writes the territory into this + * file. + */ + private void saveXMLtoFile() { + Optional fileOpt = getSaveFile(i18n("Territory.save.dialog.title"), + i18n("Territory.save.dialog.filter.xml"), DEFAULT_XML_FILE_EXTENSION); + + if (fileOpt.isEmpty()) { + logger.debug("No file selected to save territory in."); + return; + } + File file = fileOpt.get(); + + if (!file.getName().endsWith(DEFAULT_XML_FILE_EXTENSION)) { + file = new File(file.getAbsolutePath() + DEFAULT_XML_FILE_EXTENSION); + } + + logger.debug("save as XML in file {}", file); + try (ByteArrayOutputStream baos = mainStage.getTerritory().toXML(); + OutputStream outputStream = new FileOutputStream(file.getAbsolutePath())) { + baos.writeTo(outputStream); + } catch (IOException e) { + logger.debug("failed"); + } + + } + + /** + * Asks for an XML-File and loads the contents into the territory. + */ + private void loadXMLfromFile() { + Optional fileOpt = getLoadFile(i18n("Territory.load.dialog.title"), + i18n("Territory.load.dialog.filter.xml"), DEFAULT_XML_FILE_EXTENSION); + + if (fileOpt.isEmpty()) { + logger.debug("No file selected to load XML from."); + return; + } + + File file = fileOpt.get(); + logger.debug("load territory from xml-file {}", file); + try { + if (mainStage.getTerritory().fromXML(new FileInputStream(file))) + logger.info("finished loading from xml-file"); + else + logger.info("Failed loading from xml-file"); + } catch (FileNotFoundException e) { + logger.debug("Could not find file {}", file.getAbsolutePath()); + } + + } + + /** + * Loads a territory by a filename using JAXB + */ + private void loadJAXB() { + Optional fileOpt = getLoadFile(i18n("Territory.load.dialog.title"), + i18n("Territory.load.dialog.filter.jaxb"), DEFAULT_JAXB_FILE_EXTENSION); + + if (fileOpt.isEmpty()) { + logger.debug("No file selected"); + return; + } + File file = fileOpt.get(); + logger.debug("load territory from jaxb-file {}", file); + try { + JAXBContext context = JAXBContext.newInstance(TerritoryState.class, Nut.class, Screw.class, Accu.class, + Stockpile.class, PileOfScrap.class, Hollow.class); + Unmarshaller um = context.createUnmarshaller(); + TerritoryState ter = (TerritoryState) um.unmarshal(new FileReader(file)); + mainStage.getTerritory().restore(ter); + } catch (IOException | JAXBException e) { + logger.error("failed to load JAXB.", e); + } + } + + /** + * Saves the territory using JAXB. + */ + private void saveJAXB() { + Optional fileOpt = getSaveFile(i18n("Territory.save.dialog.title"), + i18n("Territory.save.dialog.filter.jaxb"), DEFAULT_JAXB_FILE_EXTENSION); + + if (fileOpt.isEmpty()) { + logger.debug("No file selected to save territory in."); + return; + } + File file = fileOpt.get(); + if (!file.getName().endsWith(DEFAULT_JAXB_FILE_EXTENSION)) { + file = new File(file.getAbsolutePath() + DEFAULT_JAXB_FILE_EXTENSION); + } + logger.debug("save territory from jaxb-file {}", file); + try (Writer w = new FileWriter(file)) { + JAXBContext context = JAXBContext.newInstance(TerritoryState.class, Nut.class, Screw.class, Accu.class, + Stockpile.class, PileOfScrap.class, Hollow.class); + Marshaller m = context.createMarshaller(); + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + m.marshal(mainStage.getTerritory().save(), w); + } catch (IOException | JAXBException e) { + logger.debug("failed to save jaxb.", e); + } + } + + /** + * Opens a dialog to ask for a file to save the territory in. + * + * @param title the title of the dialog + * @param description file description for the allowed files + * @param fileExtension allowed file-extension + * @return a file to save the territory in + */ + private Optional getSaveFile(String title, String description, String fileExtension) { + FileChooser chooser = new FileChooser(); + chooser.setTitle(title); + chooser.setInitialDirectory(new File(ProgramController.PATH_TO_PROGRAMS)); + chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(description, "*" + fileExtension)); + return Optional.ofNullable(chooser.showSaveDialog(mainStage)); + } + + /** + * Opens a dialog to ask for a file to load a territory from. + * + * @param title the title of the dialog + * @param description file description for allowed files + * @param fileExtension allowed file-extension + * @return a file to load a territory from + */ + public Optional getLoadFile(String title, String description, String fileExtension) { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle(title); + fileChooser.setInitialDirectory(new File(ProgramController.PATH_TO_PROGRAMS)); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(description, "*" + fileExtension)); + return Optional.ofNullable(fileChooser.showOpenDialog(mainStage)); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/Example.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/Example.java index f53cd69..e49cd4b 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/Example.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/Example.java @@ -1,56 +1,46 @@ package com.JayPi4c.RobbiSimulator.controller.examples; -import java.util.List; - import com.JayPi4c.RobbiSimulator.controller.program.ProgramController; - -import jakarta.persistence.CollectionTable; -import jakarta.persistence.Column; -import jakarta.persistence.ElementCollection; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Lob; -import jakarta.persistence.Table; +import jakarta.persistence.*; import lombok.Data; +import java.util.List; + /** * This class holds all information needed for an example to be loaded. It * provides a method to load a new MainStage with the given Information. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Data @Entity @Table(name = "EXAMPLES") public class Example { - @Id - @Column(unique = true, nullable = false) - @GeneratedValue(strategy = GenerationType.AUTO) - private int id; + @Id + @Column(unique = true, nullable = false) + @GeneratedValue(strategy = GenerationType.AUTO) + private int id; - private String programName; + private String programName; - @Lob - @Column(columnDefinition = "clob") - private String code; + @Lob + @Column(columnDefinition = "clob") + private String code; - @Lob - @Column(columnDefinition = "clob") - private String territory; + @Lob + @Column(columnDefinition = "clob") + private String territory; - @ElementCollection - @CollectionTable(name = "TAGS") - private List tags; + @ElementCollection + @CollectionTable(name = "TAGS") + private List tags; - /** - * Loads a new MainStage with the given information. - */ - public void load() { - ProgramController.createAndShow(programName, code, territory); - } + /** + * Loads a new MainStage with the given information. + */ + public void load() { + ProgramController.createAndShow(programName, code, territory); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/ExampleService.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/ExampleService.java index 53cbdd8..51b9a3b 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/ExampleService.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/ExampleService.java @@ -1,21 +1,19 @@ package com.JayPi4c.RobbiSimulator.controller.examples; -import java.util.List; -import java.util.Optional; - -import org.hibernate.Session; - import com.JayPi4c.RobbiSimulator.utils.HibernateUtils; - import javafx.util.Pair; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.hibernate.Session; + +import java.util.List; +import java.util.Optional; /** * Service to easily access examples from the database using the HibernateUtils * class. - * + * * @author Jonas Pohl * @since 1.0.0 */ @@ -23,79 +21,79 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ExampleService { - /** - * Method to store an example with its tags in the database. - * - * @param programName the name of the program - * @param editorContent the code in the editor - * @param territoryXML the territory encoded in XML - * @param tags the tags related to this example - * @return true if the example was stored successfully, false otherwise - */ - public static boolean store(String programName, String editorContent, String territoryXML, List tags) { - Example example = new Example(); - example.setProgramName(programName); - example.setCode(editorContent); - example.setTerritory(territoryXML); - example.setTags(tags); + /** + * Method to store an example with its tags in the database. + * + * @param programName the name of the program + * @param editorContent the code in the editor + * @param territoryXML the territory encoded in XML + * @param tags the tags related to this example + * @return true if the example was stored successfully, false otherwise + */ + public static boolean store(String programName, String editorContent, String territoryXML, List tags) { + Example example = new Example(); + example.setProgramName(programName); + example.setCode(editorContent); + example.setTerritory(territoryXML); + example.setTags(tags); - try (Session session = HibernateUtils.getSessionFactory().openSession()) { - session.beginTransaction(); - session.merge(example); - session.getTransaction().commit(); - return true; - } catch (Exception e) { - logger.error("Could not store example", e); - return false; - } - } + try (Session session = HibernateUtils.getSessionFactory().openSession()) { + session.beginTransaction(); + session.merge(example); + session.getTransaction().commit(); + return true; + } catch (Exception e) { + logger.error("Could not store example", e); + return false; + } + } - /** - * Method to get all examples identified by id and programName by their tag. - * - * @param tag the tag to search the examples for - * @return List of examples as pairs of id and programName - */ - public static Optional>> query(String tag) { - try (Session session = HibernateUtils.getSessionFactory().openSession()) { - List allExamples = session.createQuery("from Example", Example.class).list(); + /** + * Method to get all examples identified by id and programName by their tag. + * + * @param tag the tag to search the examples for + * @return List of examples as pairs of id and programName + */ + public static Optional>> query(String tag) { + try (Session session = HibernateUtils.getSessionFactory().openSession()) { + List allExamples = session.createQuery("from Example", Example.class).list(); - List taggedExamples = allExamples.stream().filter(ex -> ex.getTags().contains(tag)).toList(); + List taggedExamples = allExamples.stream().filter(ex -> ex.getTags().contains(tag)).toList(); - return Optional.of(taggedExamples.stream().map(ex -> new Pair<>(ex.getId(), ex.getProgramName())).toList()); - } - } + return Optional.of(taggedExamples.stream().map(ex -> new Pair<>(ex.getId(), ex.getProgramName())).toList()); + } + } - /** - * Loads an example from the database by the given id. - * - * @param id the ID of the example to load - * @return Optional containing the example or an empty Optional if no example - * could be found - */ - public static Optional loadExample(int id) { - Example example; - try (Session session = HibernateUtils.getSessionFactory().openSession()) { - session.beginTransaction(); - example = session.get(Example.class, id); - session.getTransaction().commit(); - } - return Optional.ofNullable(example); - } + /** + * Loads an example from the database by the given id. + * + * @param id the ID of the example to load + * @return Optional containing the example or an empty Optional if no example + * could be found + */ + public static Optional loadExample(int id) { + Example example; + try (Session session = HibernateUtils.getSessionFactory().openSession()) { + session.beginTransaction(); + example = session.get(Example.class, id); + session.getTransaction().commit(); + } + return Optional.ofNullable(example); + } - /** - * Method to load all distinct tags from the database. - * - * @return List of all distinct tags stored in the database - */ - public static Optional> getAllTags() { - List tags; - try (Session session = HibernateUtils.getSessionFactory().openSession()) { - List allExamples = session.createQuery("from Example", Example.class).list(); - logger.debug("Found {} examples", allExamples.size()); - tags = allExamples.stream().flatMap(ex -> ex.getTags().stream()).distinct().toList(); - } - return Optional.of(tags); - } + /** + * Method to load all distinct tags from the database. + * + * @return List of all distinct tags stored in the database + */ + public static Optional> getAllTags() { + List tags; + try (Session session = HibernateUtils.getSessionFactory().openSession()) { + List allExamples = session.createQuery("from Example", Example.class).list(); + logger.debug("Found {} examples", allExamples.size()); + tags = allExamples.stream().flatMap(ex -> ex.getTags().stream()).distinct().toList(); + } + return Optional.of(tags); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/ExamplesController.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/ExamplesController.java index 3cf9589..41eb404 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/ExamplesController.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/ExamplesController.java @@ -1,14 +1,6 @@ package com.JayPi4c.RobbiSimulator.controller.examples; -import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.StringTokenizer; - import com.JayPi4c.RobbiSimulator.view.MainStage; - import javafx.application.Platform; import javafx.beans.Observable; import javafx.beans.property.BooleanProperty; @@ -18,350 +10,346 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; -import javafx.scene.control.ButtonType; -import javafx.scene.control.ComboBox; -import javafx.scene.control.Control; -import javafx.scene.control.Dialog; -import javafx.scene.control.DialogPane; -import javafx.scene.control.Label; -import javafx.scene.control.ListView; -import javafx.scene.control.TextField; +import javafx.scene.control.*; import javafx.scene.control.skin.ComboBoxListViewSkin; import javafx.scene.input.KeyEvent; import javafx.scene.layout.GridPane; import javafx.util.Pair; import lombok.extern.slf4j.Slf4j; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.StringTokenizer; + +import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; + /** * This controller contains all settings for the examples menus. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Slf4j public class ExamplesController { - private MainStage stage; - - // language keys - private static final String EXAMPLES_LOAD_DIALOG_TAGS_FAIL = "Examples.load.dialog.tags.fail"; - private static final String EXAMPLES_LOAD_DIALOG_PROGRAM_TITLE = "Examples.load.dialog.program.title"; - private static final String EXAMPLES_LOAD_DIALOG_PROGRAM_HEADER = "Examples.load.dialog.program.header"; - private static final String EXAMPLES_LOAD_DIALOG_PROGRAM_NAME = "Examples.load.dialog.program.name"; - private static final String EXAMPLES_LOAD_DIALOG_TAGS_TITLE = "Examples.load.dialog.tags.title"; - private static final String EXAMPLES_LOAD_DIALOG_TAGS_HEADER = "Examples.load.dialog.tags.header"; - private static final String EXAMPLES_LOAD_DIALOG_TAGS_NAME = "Examples.load.dialog.tags.name"; - private static final String EXAMPLES_SAVE_TAGS_TITLE = "Examples.save.tags.title"; - private static final String EXAMPLES_SAVE_TAGS_HEADER = "Examples.save.tags.header"; - private static final String EXAMPLES_SAVE_TAGS_PROMPT = "Examples.save.tags.prompt"; - private static final String EXAMPLES_SAVE_TAGS_NAME = "Examples.save.tags.name"; - - /** - * Constructor to create a new ExamplesController for the mainStage. - * - * @param stage the mainStage, this controller is for - */ - public ExamplesController(MainStage stage) { - this.stage = stage; - - stage.getMenubar().getSaveExampleMenuItem().setOnAction(e -> { - Optional> tags = enterTags(); - tags.ifPresentOrElse(ts -> { - String territoryXML = stage.getTerritory().toXML().toString(); - if (!ExampleService.store(stage.getProgram().getName(), stage.getProgram().getEditorContent(), - territoryXML, ts)) - logger.debug("Could not save example in database"); - }, () -> logger.debug("No tags were entered")); - }); - - stage.getMenubar().getLoadExampleMenuItem().setOnAction(e -> { - Optional> tagsOpt = ExampleService.getAllTags(); - tagsOpt.ifPresentOrElse(tags -> { - Optional s = showTagSelection(tags); - s.ifPresentOrElse(selectedTag -> { - Optional>> programsOpt = ExampleService.query(selectedTag); - if (programsOpt.isPresent()) { - Optional idOpt = showProgramSelection(programsOpt.get()); - idOpt.ifPresentOrElse(id -> { - Optional exOpt = ExampleService.loadExample(id); - exOpt.ifPresentOrElse(Example::load, - () -> logger.debug("Could not load example from database")); - }, () -> logger.debug("No example selected")); - } - }, () -> logger.debug("No tag selected")); - }, () -> { - logger.info("No tags are stored in database"); - stage.getSnackbarController().showMessage(EXAMPLES_LOAD_DIALOG_TAGS_FAIL); - }); - - }); - } - - /** - * - * Shows an DialogWindow listing all programNames and its IDs. The user can - * select a program, which will be returned as an Optional containing the id. - * - * @param programs a list of pairs of IDs and programNames, representing a - * program - * @return the id of the selected Program - */ - public Optional showProgramSelection(List> programs) { - Dialog dialog = new Dialog<>(); - dialog.setTitle(i18n(EXAMPLES_LOAD_DIALOG_PROGRAM_TITLE)); - dialog.setHeaderText(i18n(EXAMPLES_LOAD_DIALOG_PROGRAM_HEADER)); - dialog.initOwner(stage); - DialogPane dialogPane = dialog.getDialogPane(); - dialogPane.getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); - - ComboBox>> comboBox = createComboBoxWithAutoCompletionSupport(programs); - comboBox.getSelectionModel().select(0); - GridPane grid = new GridPane(); - grid.addRow(0, new Label(i18n(EXAMPLES_LOAD_DIALOG_PROGRAM_NAME)), comboBox); - - dialogPane.setContent(grid); - Platform.runLater(comboBox::requestFocus); - dialog.setResultConverter( - button -> (button == ButtonType.OK) ? comboBox.getValue().getObject().getKey() : null); - return dialog.showAndWait(); - } - - /** - * Shows an DialogWindow listing all tags. The user can select a tag, which will - * be returned as an Optional. - * - * @param tags a list of tags to be shown to the user - * @return the selected tag - */ - public Optional showTagSelection(List tags) { - Dialog dialog = new Dialog<>(); - dialog.setTitle(i18n(EXAMPLES_LOAD_DIALOG_TAGS_TITLE)); - dialog.setHeaderText(i18n(EXAMPLES_LOAD_DIALOG_TAGS_HEADER)); - dialog.initOwner(stage); - DialogPane dialogPane = dialog.getDialogPane(); - dialogPane.getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); - - ComboBox> comboBox = createComboBoxWithAutoCompletionSupport(tags); - comboBox.getSelectionModel().select(0); - GridPane grid = new GridPane(); - grid.addRow(0, new Label(i18n(EXAMPLES_LOAD_DIALOG_TAGS_NAME)), comboBox); - - dialogPane.setContent(grid); - Platform.runLater(comboBox::requestFocus); - dialog.setResultConverter(button -> (button == ButtonType.OK) ? comboBox.getValue().toString() : null); - return dialog.showAndWait(); - } - - /** - * Opens a dialog for the user to enter tags for the example to save them - * by.
- * Tags must be entered comma-separated. - * - * @return a List of tags - */ - private Optional> enterTags() { - Dialog dialog = new Dialog<>(); - dialog.setTitle(i18n(EXAMPLES_SAVE_TAGS_TITLE)); - dialog.setHeaderText(i18n(EXAMPLES_SAVE_TAGS_HEADER)); - dialog.initOwner(stage); - DialogPane dialogPane = dialog.getDialogPane(); - dialogPane.getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); - TextField tagsField = new TextField(); - tagsField.setPromptText(i18n(EXAMPLES_SAVE_TAGS_PROMPT)); - - tagsField.textProperty().addListener((observable, oldVal, newVal) -> dialog.getDialogPane() - .lookupButton(ButtonType.OK).setDisable(newVal.isBlank())); - - dialog.getDialogPane().lookupButton(ButtonType.OK).setDisable(true); - GridPane grid = new GridPane(); - grid.addRow(0, new Label(i18n(EXAMPLES_SAVE_TAGS_NAME)), tagsField); - - dialogPane.setContent(grid); - Platform.runLater(tagsField::requestFocus); - dialog.setResultConverter(button -> (button == ButtonType.OK) ? tagsField.getText() : null); - - Optional result = dialog.showAndWait(); - if (result.isPresent()) { - StringTokenizer tokenizer = new StringTokenizer(result.get()); - List tags = new ArrayList<>(); - while (tokenizer.hasMoreTokens()) { - String token = tokenizer.nextToken(); - if (!tags.contains(token)) - tags.add(token); - } - return Optional.of(tags); - } else - return Optional.empty(); - } - - /** - * Taken from - * Stackoverflow. - * This class holds information for the entries of the AutocompletionComboBox. - * - * @author Eng.Fouad - * - * @param Type of the objects stored in the instance - */ - private static class HideableItem { - private final ObjectProperty object = new SimpleObjectProperty<>(); - private final BooleanProperty hidden = new SimpleBooleanProperty(); - - /** - * Constructor to create a new HideableItem. - * - * @param object Object to be stored in the item. - */ - private HideableItem(T object) { - setObject(object); - } - - /** - * Getter for the ObjectProperty attribute - * - * @return the items objectProperty - */ - private ObjectProperty objectProperty() { - return this.object; - } - - /** - * Getter for the value stored in the item. - * - * @return the items value - */ - private T getObject() { - return this.objectProperty().get(); - } - - /** - * Setter for the value stored in the item. - * - * @param object the items new value - */ - private void setObject(T object) { - this.objectProperty().set(object); - } - - /** - * Getter for the hiddenProperty attribute - * - * @return the items hiddenProperty - */ - private BooleanProperty hiddenProperty() { - return this.hidden; - } - - /** - * Getter for the hidden value of this item. - * - * @return true if the item is hidden, false otherwise - */ - private boolean isHidden() { - return this.hiddenProperty().get(); - } - - /** - * Setter for the hidden value of this item. - * - * @param hidden the new hidden value - */ - private void setHidden(boolean hidden) { - this.hiddenProperty().set(hidden); - } - - @Override - public String toString() { - return getObject() == null ? null : getObject().toString(); - } - } - - /** - * Taken from - * Stackoverflow. - *
- * In order to allow a quick search over the attributes in the comboBox, this - * comboBox has the option to type in the name of the entry and if it is present - * it will show the entry and select it. - * - * @param Type of the items stored in the comboBox - * @param items the items to store in the ComboBox. - * @return a comboBox, that supports Auto-Completion - */ - private static ComboBox> createComboBoxWithAutoCompletionSupport(List items) { - ObservableList> hideableHideableItems = FXCollections - .observableArrayList(hidealbeItem -> new Observable[] { hidealbeItem.hiddenProperty() }); - - items.forEach(item -> { - HideableItem hideableItem = new HideableItem<>(item); - hideableHideableItems.add(hideableItem); - }); - - FilteredList> filteredHideableItems = new FilteredList<>(hideableHideableItems, - t -> !t.isHidden()); - - ComboBox> comboBox = new ComboBox<>(); - comboBox.setItems(filteredHideableItems); - - @SuppressWarnings("unchecked") - HideableItem[] selectedItem = new HideableItem[1]; - - comboBox.addEventHandler(KeyEvent.KEY_PRESSED, event -> { - if (!comboBox.isShowing()) - return; - comboBox.setEditable(true); - comboBox.getEditor().clear(); - }); - - comboBox.showingProperty().addListener((obs, oldVal, newVal) -> { - if (Boolean.TRUE.equals(newVal)) { - @SuppressWarnings("unchecked") - ListView> lv = (ListView>) ((ComboBoxListViewSkin) comboBox - .getSkin()).getPopupContent(); - - Platform.runLater(() -> { - if (selectedItem[0] == null) { // first use - double cellHeight = ((Control) lv.lookup(".list-cell")).getHeight(); - lv.setFixedCellSize(cellHeight); - } - }); - lv.scrollTo(comboBox.getValue()); - } else { - HideableItem value = comboBox.getValue(); - if (value != null) - selectedItem[0] = value; - comboBox.setEditable(false); - Platform.runLater(() -> { - comboBox.getSelectionModel().select(selectedItem[0]); - comboBox.setValue(selectedItem[0]); - }); - } - }); - - comboBox.setOnHidden(event -> hideableHideableItems.forEach(item -> item.setHidden(false))); - - comboBox.getEditor().textProperty().addListener((obs, oldVal, newVal) -> { - if (!comboBox.isShowing()) - return; - - Platform.runLater(() -> { - if (comboBox.getSelectionModel().getSelectedItem() == null) { - hideableHideableItems.forEach(item -> item - .setHidden(!item.getObject().toString().toLowerCase().contains(newVal.toLowerCase()))); - } else { - boolean validText = false; - for (HideableItem hideableItem : hideableHideableItems) { - if (hideableItem.getObject().toString().equals(newVal)) { - validText = true; - break; - } - } - if (!validText) - comboBox.getSelectionModel().select(null); - } - }); - }); - return comboBox; - } + // language keys + private static final String EXAMPLES_LOAD_DIALOG_TAGS_FAIL = "Examples.load.dialog.tags.fail"; + private static final String EXAMPLES_LOAD_DIALOG_PROGRAM_TITLE = "Examples.load.dialog.program.title"; + private static final String EXAMPLES_LOAD_DIALOG_PROGRAM_HEADER = "Examples.load.dialog.program.header"; + private static final String EXAMPLES_LOAD_DIALOG_PROGRAM_NAME = "Examples.load.dialog.program.name"; + private static final String EXAMPLES_LOAD_DIALOG_TAGS_TITLE = "Examples.load.dialog.tags.title"; + private static final String EXAMPLES_LOAD_DIALOG_TAGS_HEADER = "Examples.load.dialog.tags.header"; + private static final String EXAMPLES_LOAD_DIALOG_TAGS_NAME = "Examples.load.dialog.tags.name"; + private static final String EXAMPLES_SAVE_TAGS_TITLE = "Examples.save.tags.title"; + private static final String EXAMPLES_SAVE_TAGS_HEADER = "Examples.save.tags.header"; + private static final String EXAMPLES_SAVE_TAGS_PROMPT = "Examples.save.tags.prompt"; + private static final String EXAMPLES_SAVE_TAGS_NAME = "Examples.save.tags.name"; + private MainStage stage; + + /** + * Constructor to create a new ExamplesController for the mainStage. + * + * @param stage the mainStage, this controller is for + */ + public ExamplesController(MainStage stage) { + this.stage = stage; + + stage.getMenubar().getSaveExampleMenuItem().setOnAction(e -> { + Optional> tags = enterTags(); + tags.ifPresentOrElse(ts -> { + String territoryXML = stage.getTerritory().toXML().toString(); + if (!ExampleService.store(stage.getProgram().getName(), stage.getProgram().getEditorContent(), + territoryXML, ts)) + logger.debug("Could not save example in database"); + }, () -> logger.debug("No tags were entered")); + }); + + stage.getMenubar().getLoadExampleMenuItem().setOnAction(e -> { + Optional> tagsOpt = ExampleService.getAllTags(); + tagsOpt.ifPresentOrElse(tags -> { + Optional s = showTagSelection(tags); + s.ifPresentOrElse(selectedTag -> { + Optional>> programsOpt = ExampleService.query(selectedTag); + if (programsOpt.isPresent()) { + Optional idOpt = showProgramSelection(programsOpt.get()); + idOpt.ifPresentOrElse(id -> { + Optional exOpt = ExampleService.loadExample(id); + exOpt.ifPresentOrElse(Example::load, + () -> logger.debug("Could not load example from database")); + }, () -> logger.debug("No example selected")); + } + }, () -> logger.debug("No tag selected")); + }, () -> { + logger.info("No tags are stored in database"); + stage.getNotificationController().showMessage(3000, EXAMPLES_LOAD_DIALOG_TAGS_FAIL); + }); + + }); + } + + /** + * Taken from + * Stackoverflow. + *
+ * In order to allow a quick search over the attributes in the comboBox, this + * comboBox has the option to type in the name of the entry and if it is present + * it will show the entry and select it. + * + * @param Type of the items stored in the comboBox + * @param items the items to store in the ComboBox. + * @return a comboBox, that supports Auto-Completion + */ + private static ComboBox> createComboBoxWithAutoCompletionSupport(List items) { + ObservableList> hideableHideableItems = FXCollections + .observableArrayList(hidealbeItem -> new Observable[]{hidealbeItem.hiddenProperty()}); + + items.forEach(item -> { + HideableItem hideableItem = new HideableItem<>(item); + hideableHideableItems.add(hideableItem); + }); + + FilteredList> filteredHideableItems = new FilteredList<>(hideableHideableItems, + t -> !t.isHidden()); + + ComboBox> comboBox = new ComboBox<>(); + comboBox.setItems(filteredHideableItems); + + @SuppressWarnings("unchecked") + HideableItem[] selectedItem = new HideableItem[1]; + + comboBox.addEventHandler(KeyEvent.KEY_PRESSED, event -> { + if (!comboBox.isShowing()) + return; + comboBox.setEditable(true); + comboBox.getEditor().clear(); + }); + + comboBox.showingProperty().addListener((obs, oldVal, newVal) -> { + if (Boolean.TRUE.equals(newVal)) { + @SuppressWarnings("unchecked") + ListView> lv = (ListView>) ((ComboBoxListViewSkin) comboBox + .getSkin()).getPopupContent(); + + Platform.runLater(() -> { + if (selectedItem[0] == null) { // first use + double cellHeight = ((Control) lv.lookup(".list-cell")).getHeight(); + lv.setFixedCellSize(cellHeight); + } + }); + lv.scrollTo(comboBox.getValue()); + } else { + HideableItem value = comboBox.getValue(); + if (value != null) + selectedItem[0] = value; + comboBox.setEditable(false); + Platform.runLater(() -> { + comboBox.getSelectionModel().select(selectedItem[0]); + comboBox.setValue(selectedItem[0]); + }); + } + }); + + comboBox.setOnHidden(event -> hideableHideableItems.forEach(item -> item.setHidden(false))); + + comboBox.getEditor().textProperty().addListener((obs, oldVal, newVal) -> { + if (!comboBox.isShowing()) + return; + + Platform.runLater(() -> { + if (comboBox.getSelectionModel().getSelectedItem() == null) { + hideableHideableItems.forEach(item -> item + .setHidden(!item.getObject().toString().toLowerCase().contains(newVal.toLowerCase()))); + } else { + boolean validText = false; + for (HideableItem hideableItem : hideableHideableItems) { + if (hideableItem.getObject().toString().equals(newVal)) { + validText = true; + break; + } + } + if (!validText) + comboBox.getSelectionModel().select(null); + } + }); + }); + return comboBox; + } + + /** + * Shows an DialogWindow listing all programNames and its IDs. The user can + * select a program, which will be returned as an Optional containing the id. + * + * @param programs a list of pairs of IDs and programNames, representing a + * program + * @return the id of the selected Program + */ + public Optional showProgramSelection(List> programs) { + Dialog dialog = new Dialog<>(); + dialog.setTitle(i18n(EXAMPLES_LOAD_DIALOG_PROGRAM_TITLE)); + dialog.setHeaderText(i18n(EXAMPLES_LOAD_DIALOG_PROGRAM_HEADER)); + dialog.initOwner(stage); + DialogPane dialogPane = dialog.getDialogPane(); + dialogPane.getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); + + ComboBox>> comboBox = createComboBoxWithAutoCompletionSupport(programs); + comboBox.getSelectionModel().select(0); + GridPane grid = new GridPane(); + grid.addRow(0, new Label(i18n(EXAMPLES_LOAD_DIALOG_PROGRAM_NAME)), comboBox); + + dialogPane.setContent(grid); + Platform.runLater(comboBox::requestFocus); + dialog.setResultConverter( + button -> (button == ButtonType.OK) ? comboBox.getValue().getObject().getKey() : null); + return dialog.showAndWait(); + } + + /** + * Shows an DialogWindow listing all tags. The user can select a tag, which will + * be returned as an Optional. + * + * @param tags a list of tags to be shown to the user + * @return the selected tag + */ + public Optional showTagSelection(List tags) { + Dialog dialog = new Dialog<>(); + dialog.setTitle(i18n(EXAMPLES_LOAD_DIALOG_TAGS_TITLE)); + dialog.setHeaderText(i18n(EXAMPLES_LOAD_DIALOG_TAGS_HEADER)); + dialog.initOwner(stage); + DialogPane dialogPane = dialog.getDialogPane(); + dialogPane.getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); + + ComboBox> comboBox = createComboBoxWithAutoCompletionSupport(tags); + comboBox.getSelectionModel().select(0); + GridPane grid = new GridPane(); + grid.addRow(0, new Label(i18n(EXAMPLES_LOAD_DIALOG_TAGS_NAME)), comboBox); + + dialogPane.setContent(grid); + Platform.runLater(comboBox::requestFocus); + dialog.setResultConverter(button -> (button == ButtonType.OK) ? comboBox.getValue().toString() : null); + return dialog.showAndWait(); + } + + /** + * Opens a dialog for the user to enter tags for the example to save them + * by.
+ * Tags must be entered comma-separated. + * + * @return a List of tags + */ + private Optional> enterTags() { + Dialog dialog = new Dialog<>(); + dialog.setTitle(i18n(EXAMPLES_SAVE_TAGS_TITLE)); + dialog.setHeaderText(i18n(EXAMPLES_SAVE_TAGS_HEADER)); + dialog.initOwner(stage); + DialogPane dialogPane = dialog.getDialogPane(); + dialogPane.getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); + TextField tagsField = new TextField(); + tagsField.setPromptText(i18n(EXAMPLES_SAVE_TAGS_PROMPT)); + + tagsField.textProperty().addListener((observable, oldVal, newVal) -> dialog.getDialogPane() + .lookupButton(ButtonType.OK).setDisable(newVal.isBlank())); + + dialog.getDialogPane().lookupButton(ButtonType.OK).setDisable(true); + GridPane grid = new GridPane(); + grid.addRow(0, new Label(i18n(EXAMPLES_SAVE_TAGS_NAME)), tagsField); + + dialogPane.setContent(grid); + Platform.runLater(tagsField::requestFocus); + dialog.setResultConverter(button -> (button == ButtonType.OK) ? tagsField.getText() : null); + + Optional result = dialog.showAndWait(); + if (result.isPresent()) { + StringTokenizer tokenizer = new StringTokenizer(result.get()); + List tags = new ArrayList<>(); + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + if (!tags.contains(token)) + tags.add(token); + } + return Optional.of(tags); + } else + return Optional.empty(); + } + + /** + * Taken from + * Stackoverflow. + * This class holds information for the entries of the AutocompletionComboBox. + * + * @param Type of the objects stored in the instance + * @author Eng.Fouad + */ + private static class HideableItem { + private final ObjectProperty object = new SimpleObjectProperty<>(); + private final BooleanProperty hidden = new SimpleBooleanProperty(); + + /** + * Constructor to create a new HideableItem. + * + * @param object Object to be stored in the item. + */ + private HideableItem(T object) { + setObject(object); + } + + /** + * Getter for the ObjectProperty attribute + * + * @return the items objectProperty + */ + private ObjectProperty objectProperty() { + return this.object; + } + + /** + * Getter for the value stored in the item. + * + * @return the items value + */ + private T getObject() { + return this.objectProperty().get(); + } + + /** + * Setter for the value stored in the item. + * + * @param object the items new value + */ + private void setObject(T object) { + this.objectProperty().set(object); + } + + /** + * Getter for the hiddenProperty attribute + * + * @return the items hiddenProperty + */ + private BooleanProperty hiddenProperty() { + return this.hidden; + } + + /** + * Getter for the hidden value of this item. + * + * @return true if the item is hidden, false otherwise + */ + private boolean isHidden() { + return this.hiddenProperty().get(); + } + + /** + * Setter for the hidden value of this item. + * + * @param hidden the new hidden value + */ + private void setHidden(boolean hidden) { + this.hiddenProperty().set(hidden); + } + + @Override + public String toString() { + return getObject() == null ? null : getObject().toString(); + } + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/program/Diagnostics.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/program/Diagnostics.java index a7dd7c4..a58bf3a 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/program/Diagnostics.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/program/Diagnostics.java @@ -4,52 +4,49 @@ import java.util.List; /** - * * This class contains a list of Diagnostics, which are created during the * post-compile annotation check. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public class Diagnostics { - private List diags; - - /** - * Constructor for the Diagnostics-class. It creates the list to hold the - * diagnostics. - */ - public Diagnostics() { - diags = new ArrayList<>(); - } - - /** - * Adds the given diagnostic to the list of diagnostics - * - * @param d The Diagnostic to be added to the list of diagnostics - */ - public void add(Diagnostic d) { - diags.add(d); - } - - /** - * Returns a list of all collected Diagnostics. - * - * @return the list of all diagnostics - */ - public List getDiagnosis() { - return diags; - } - - /** - * Container to hold a single Diagnostic, which consists of a type and the - * faulty value provided for this type. - * - * @author Jonas Pohl - * - */ - public record Diagnostic(String type, String value) { - - } + private final List diags; + + /** + * Constructor for the Diagnostics-class. It creates the list to hold the + * diagnostics. + */ + public Diagnostics() { + diags = new ArrayList<>(); + } + + /** + * Adds the given diagnostic to the list of diagnostics + * + * @param d The Diagnostic to be added to the list of diagnostics + */ + public void add(Diagnostic d) { + diags.add(d); + } + + /** + * Returns a list of all collected Diagnostics. + * + * @return the list of all diagnostics + */ + public List getDiagnosis() { + return diags; + } + + /** + * Container to hold a single Diagnostic, which consists of a type and the + * faulty value provided for this type. + * + * @author Jonas Pohl + */ + public record Diagnostic(String type, String value) { + + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/program/Program.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/program/Program.java index 7685dad..b926aa8 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/program/Program.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/program/Program.java @@ -1,108 +1,103 @@ package com.JayPi4c.RobbiSimulator.controller.program; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; - import com.JayPi4c.RobbiSimulator.utils.Observable; - import lombok.Getter; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import java.io.*; /** * This class contains the contents, the name of program and whether it is * edited or not - * - * @author Jonas Pohl * + * @author Jonas Pohl */ +@Slf4j @Getter public class Program extends Observable { - /** - * The name of the program - */ - @Setter - private String name; + /** + * The name of the program + */ + @Setter + private String name; - private String editorContent; - private File file; + private String editorContent; + private final File file; - /** - * Whether the program is edited or not - */ - @Setter - private boolean edited = false; + /** + * Whether the program is edited or not + */ + @Setter + private boolean edited = false; - /** - * Creates and loads a program from a given file and the program name - * - * @param f the file on the fileSystem storing the program - * @param name the name of the program - */ - public Program(File f, String name) { - this.file = f; - this.name = name; - try (BufferedReader reader = new BufferedReader(new FileReader(f))) { - StringBuilder bobTheBuilder = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - bobTheBuilder.append(line); - bobTheBuilder.append(System.lineSeparator()); - } + /** + * Creates and loads a program from a given file and the program name + * + * @param f the file on the fileSystem storing the program + * @param name the name of the program + */ + public Program(File f, String name) { + this.file = f; + this.name = name; + try (BufferedReader reader = new BufferedReader(new FileReader(f))) { + StringBuilder bobTheBuilder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + bobTheBuilder.append(line); + bobTheBuilder.append(System.lineSeparator()); + } - String content = bobTheBuilder.toString(); + String content = bobTheBuilder.toString(); - editorContent = content.replace(ProgramController.createPrefix(name), ""); - int endIndex = editorContent.lastIndexOf('}'); - bobTheBuilder.setLength(0); - editorContent = bobTheBuilder.append(editorContent).deleteCharAt(endIndex).toString().trim(); - } catch (IOException e) { - e.printStackTrace(); - } - } + editorContent = content.replace(ProgramController.createPrefix(name), ""); + int endIndex = editorContent.lastIndexOf('}'); + bobTheBuilder.setLength(0); + editorContent = bobTheBuilder.append(editorContent).deleteCharAt(endIndex).toString().trim(); + } catch (IOException e) { + logger.error("Could not load program", e); + } + } - /** - * Sets the editorContent and notifies all Observers, namely the - * MainStageController to update the textEditors content. - * - * @param content the new content - */ - public void setEditorContent(String content) { - this.editorContent = content; - this.edited = true; - this.setChanged(); - this.notifyAllObservers(); - } + /** + * Sets the editorContent and notifies all Observers, namely the + * MainStageController to update the textEditors content. + * + * @param content the new content + */ + public void setEditorContent(String content) { + this.editorContent = content; + this.edited = true; + this.setChanged(); + this.notifyAllObservers(); + } - /** - * saves the text of the editor-content into the corresponding file It does only - * save the given text, if the changes were made - * - * @param text the text to save in the file - */ - public void save(String text) { - if (!edited) - return; + /** + * saves the text of the editor-content into the corresponding file It does only + * save the given text, if the changes were made + * + * @param text the text to save in the file + */ + public void save(String text) { + if (!edited) + return; - editorContent = text; - save(); - } + editorContent = text; + save(); + } - /** - * Forces the program to be saved even if it might not be edited. - */ - public void save() { - String content = ProgramController.createTemplate(name, editorContent); - try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { - writer.write(content); - } catch (IOException e) { - e.printStackTrace(); - } - edited = false; - } + /** + * Forces the program to be saved even if it might not be edited. + */ + public void save() { + String content = ProgramController.createTemplate(name, editorContent); + try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { + writer.write(content); + } catch (IOException e) { + logger.error("Could not save program", e); + } + edited = false; + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/program/ProgramController.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/program/ProgramController.java index 7539e9b..b26cc9e 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/program/ProgramController.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/program/ProgramController.java @@ -1,51 +1,12 @@ package com.JayPi4c.RobbiSimulator.controller.program; -import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -import javax.lang.model.SourceVersion; -import javax.tools.Diagnostic; -import javax.tools.DiagnosticCollector; -import javax.tools.JavaCompiler; -import javax.tools.JavaCompiler.CompilationTask; -import javax.tools.JavaFileObject; -import javax.tools.StandardJavaFileManager; -import javax.tools.ToolProvider; - import com.JayPi4c.RobbiSimulator.model.Robbi; import com.JayPi4c.RobbiSimulator.utils.AlertHelper; import com.JayPi4c.RobbiSimulator.utils.annotations.Default; import com.JayPi4c.RobbiSimulator.view.MainStage; - import javafx.application.Platform; -import javafx.scene.control.Alert; +import javafx.scene.control.*; import javafx.scene.control.Alert.AlertType; -import javafx.scene.control.ButtonType; -import javafx.scene.control.Dialog; -import javafx.scene.control.DialogPane; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; import javafx.scene.layout.GridPane; import javafx.stage.FileChooser; import javafx.stage.Modality; @@ -55,677 +16,681 @@ import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import javax.lang.model.SourceVersion; +import javax.tools.*; +import javax.tools.JavaCompiler.CompilationTask; +import java.io.*; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.*; +import java.util.stream.Collectors; + +import static com.JayPi4c.RobbiSimulator.utils.I18nUtils.i18n; + /** * This controller contains all functionality to initialize the application, * load, save and compile a program. Furthermore, it creates and opens a new * stage, if a new Program is loaded, or it loads the DefaultProgram on the * Application start - * + * * @author Jonas Pohl - * */ @Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ProgramController { - /** - * Constant String with the Path name for the programs directory. - */ - public static final String PATH_TO_PROGRAMS = "programs"; - /** - * Constant String for the Default Robbi File name. - */ - public static final String DEFAULT_ROBBI_FILE_NAME = "DefaultRobbi"; - /** - * Constant String for the default file extension name. - */ - public static final String DEFAULT_FILE_EXTENSION = ".java"; - /** - * Constant String for the default content of the editor when the file is newly - * created.
- * The comment "// place your code here" could also be internationalized. TODO - */ - public static final String DEFAULT_CONTENT = "void main(){" + System.lineSeparator() + "\t// place your code here" - + System.lineSeparator() + "}"; - /** - * Constant String for the editor prefix which is needed to compile the class - * and load into the simulator. This String will not be shown in the editor - * content. - */ - public static final String PREFIX_TEMPLATE = "import com.JayPi4c.RobbiSimulator.utils.annotations.*;import lombok.extern.slf4j.Slf4j; @Slf4j public class %s extends com.JayPi4c.RobbiSimulator.model.Robbi{"; - /** - * Constant String for the editor postfix, to close the class and make it - * compilable. This postfix will not be shown in the editor content. - */ - public static final String POSTFIX_TEMPLATE = System.lineSeparator() + "}"; - - private static HashMap programs; - - private static URLClassLoader classLoader; - - // language keys - private static final String NEW_DIALOG_TITLE = "New.dialog.title"; - private static final String NEW_DIALOG_HEADER = "New.dialog.header"; - private static final String NEW_DIALOG_PROMPT = "New.dialog.prompt"; - private static final String NEW_DIALOG_NAME = "New.dialog.name"; - private static final String COMPILATION_DIAGNOSTIC_KIND = "Compilation.diagnostic.kind"; - private static final String COMPILATION_DIAGNOSTIC_CODEANDMESSAGE = "Compilation.diagnostic.CodeAndMessage"; - private static final String COMPILATION_DIAGNOSTIC_ROW = "Compilation.diagnostic.row"; - private static final String COMPILATION_DIAGNOSTIC_TITLE = "Compilation.diagnostic.title"; - private static final String COMPILATION_ANNOTATIONS_MSG_DEFAULT = "Compilation.annotations.msg.default"; - private static final String COMPILATION_ANNOTATIONS_MSG_INFO = "Compilation.annotations.msg.info"; - private static final String COMPILATION_ANNOTATIONS_TITLE = "Compilation.annotations.title"; - private static final String COMPILATION_ANNOTATIONS_HEADER = "Compilation.annotations.header"; - private static final String COMPILATION_SUCCESS_MESSAGE = "Compilation.success.message"; - private static final String COMPILATION_SUCCESS_TITLE = "Compilation.success.title"; - private static final String COMPILATION_SUCCESS_HEADER = "Compilation.success.header"; - private static final String COMPILATION_DIAGNOSTIC_OVERRIDE = "Compilation.diagnostic.override"; - - /** - * Initializes the Application on startup and makes sure the programs folder and - * the DefaultRobbi class exists - * - * @return true if initialization finished successfully, false otherwise - */ - public static boolean initialize() { - File dir = new File(PATH_TO_PROGRAMS); - if (!dir.exists() && !dir.mkdir()) { - return false; - } - - File defaultProgram = new File( - PATH_TO_PROGRAMS + File.separatorChar + DEFAULT_ROBBI_FILE_NAME + DEFAULT_FILE_EXTENSION); - if (!defaultProgram.exists()) { - try { - if (!defaultProgram.createNewFile()) - return false; - } catch (IOException e) { - e.printStackTrace(); - return false; - } - - try (BufferedWriter writer = new BufferedWriter(new FileWriter(defaultProgram))) { - writer.write(createTemplate(DEFAULT_ROBBI_FILE_NAME, DEFAULT_CONTENT)); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - programs = new HashMap<>(); - return true; - } - - /** - * Looks into the programs directory and collects all java filenames. - * - * @return Collection of java filenames in programs directory - */ - private static Collection getFilenamesInDirectory() { - // get a list of all files in programs directory that end with .java - File[] files = new File(PATH_TO_PROGRAMS) - .listFiles(file -> (file.isFile() && file.getName().endsWith(DEFAULT_FILE_EXTENSION))); - // get a String collection with all filenames in the programs directory - Collection filenamesInDirectory = Arrays.stream(files) - .map(file -> file.getName().replace(DEFAULT_FILE_EXTENSION, "")) - .collect(Collectors.toCollection(ArrayList::new)); - - logger.debug("Found following files in 'programs' directory:"); - filenamesInDirectory.forEach(logger::debug); - return filenamesInDirectory; - } - - /** - * Creates a Dialog and asks the user to for the name of the new program. - * Afterwards, it initiates the creation of a new stage for the program. - * - * @param parent the parent window to show alerts relative to the parent window - * @see ProgramController#createAndShow(String) - */ - public static void createAndShow(Window parent) { - Optional result = getNameForProgram(parent); - result.ifPresent(ProgramController::createAndShow); - } - - /** - * Asks the user to enter a name for the program. It only allows names, that are - * no java identifiers and are not already used in the programs folder - * - * @param parent window to show the dialog relative to - * @return the new name for the program - */ - private static Optional getNameForProgram(Window parent) { - Dialog dialog = new Dialog<>(); - dialog.setTitle(i18n(NEW_DIALOG_TITLE)); - dialog.setHeaderText(i18n(NEW_DIALOG_HEADER)); - dialog.initOwner(parent); - DialogPane dialogPane = dialog.getDialogPane(); - dialogPane.getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); - TextField nameField = new TextField(); - nameField.setPromptText(i18n(NEW_DIALOG_PROMPT)); - - Collection filenamesInDirectory = getFilenamesInDirectory(); - - nameField.textProperty() - .addListener((observable, oldVal, newVal) -> dialog.getDialogPane().lookupButton(ButtonType.OK) - .setDisable(SourceVersion.isKeyword(nameField.getText()) // https://stackoverflow.com/a/54141029/13670629 - || !SourceVersion.isIdentifier(nameField.getText()) - // || !nameField.getText().matches(VALID_IDENTIFIER_REGEX) - || filenamesInDirectory.contains(nameField.getText()))); - - dialog.getDialogPane().lookupButton(ButtonType.OK).setDisable(true); - GridPane grid = new GridPane(); - grid.addRow(0, new Label(i18n(NEW_DIALOG_NAME)), nameField); - - dialogPane.setContent(grid); - Platform.runLater(nameField::requestFocus); - dialog.setResultConverter(button -> (button == ButtonType.OK) ? nameField.getText() : null); - - return dialog.showAndWait(); - } - - /** - * Creates a new file with default content, if it does not exist. Afterwards, it - * creates a program and a new Stage. Finally, it compiles the code and loads - * the Robbi instance into the simulation - * - * @param programName the name of the program to create - */ - public static void createAndShow(String programName) { - String content = createTemplate(programName, DEFAULT_CONTENT); - File f = new File(PATH_TO_PROGRAMS + File.separatorChar + programName + DEFAULT_FILE_EXTENSION); - if (!f.exists()) { - try { - if (!f.createNewFile()) - logger.debug("Could not create file '{}'", f.getAbsolutePath()); - } catch (IOException e) { - e.printStackTrace(); - } - try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) { - writer.write(content); - } catch (IOException e) { - e.printStackTrace(); - } - } - - Program program = new Program(f, programName); - Stage stage = new MainStage(program); - programs.put(programName, stage); - ProgramController.compile(program, false, stage); - } - - private static final String EXAMPLES_DUPLICATION_MESSAGE = "Examples.duplication.message"; - private static final String EXAMPLES_DUPLICATION_HEADER = "Examples.duplication.header"; - private static final String EXAMPLES_DUPLICATION_TITLE = "Examples.duplication.title"; - - /** - * Creates a stage with the given programName, the programCode provided and the - * territory encoded as XML. If the user has already a stage with the same name, - * he will be warned, that his program will be overwritten. Optionally, he can - * save his program and territory under another name. After that a new Stage - * will be created according to the given information.
- * This method might only be used to load an example from the database. - * - * @param programName name of the program - * @param programCode code of the program - * @param territoryXML XML-encoded territory - */ - public static void createAndShow(String programName, String programCode, String territoryXML) { - - String content = createTemplate(programName, DEFAULT_CONTENT); - File f = new File(PATH_TO_PROGRAMS + File.separatorChar + programName + DEFAULT_FILE_EXTENSION); - if (f.exists()) { - logger.debug("There is a file with the name of the example"); - if (programs.containsKey(programName)) { - logger.debug("Program is opened: closing..."); - MainStage stage = (MainStage) programs.get(programName); - Program p = stage.getProgram(); - p.save(p.getEditorContent()); - stage.close(); - } - Alert alert = AlertHelper.createAlert(AlertType.INFORMATION, i18n(EXAMPLES_DUPLICATION_MESSAGE), null); - alert.setHeaderText(i18n(EXAMPLES_DUPLICATION_HEADER)); - alert.setTitle(i18n(EXAMPLES_DUPLICATION_TITLE)); - alert.getButtonTypes().remove(ButtonType.OK); - alert.getButtonTypes().addAll(ButtonType.YES, ButtonType.NO); - Optional result = alert.showAndWait(); - if (result.isPresent()) { - ButtonType button = result.get(); - if (button.equals(ButtonType.YES)) { - Optional newName = getNameForProgram(null); - if (newName.isPresent()) { - String name = newName.get(); - - StringBuilder bobTheBuilder = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new FileReader(f))) { - String line = null; - while ((line = reader.readLine()) != null) { - bobTheBuilder.append(line); - bobTheBuilder.append(System.lineSeparator()); - } - bobTheBuilder.replace(0, createPrefix(programName).length(), createPrefix(name)); - - } catch (IOException e) { - logger.debug("Failed to read class contents"); - } - try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) { - writer.write(bobTheBuilder.toString()); - } catch (IOException e) { - logger.debug("Failed to write new classname in file"); - } - if (!renameFile(f, name)) { - logger.debug("Failed to rename file"); - } - } - } - } - } - - if (!f.exists()) { - try { - if (!f.createNewFile()) - logger.debug("Could not create file '{}'", f.getAbsolutePath()); - } catch (IOException e) { - e.printStackTrace(); - } - try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) { - writer.write(content); - } catch (IOException e) { - e.printStackTrace(); - } - } - - Program program = new Program(f, programName); - program.setEdited(true); - program.save(programCode); - MainStage stage = new MainStage(program); - stage.getTerritory().fromXML(new ByteArrayInputStream(territoryXML.getBytes())); - programs.put(programName, stage); - ProgramController.compile(program, false, stage); - } - - /** - * Renames a java file in the programs directory to the new name. - * - * @param f the file to rename - * @param newName the new name of the file - * @return true if the renaming was successful, false otherwise - */ - public static boolean renameFile(File f, String newName) { - File newFile = new File(PATH_TO_PROGRAMS + File.separatorChar + newName + DEFAULT_FILE_EXTENSION); - - if (newFile.exists()) - return false; - return f.renameTo(newFile); - } - - /** - * Creates the prefix for the program-file - * - * @param name the name of the class - * @return the prefix for the class - */ - public static String createPrefix(String name) { - return String.format(PREFIX_TEMPLATE, name); - } - - /** - * Creates the postfix for the program-file - * - * @return the postfix for the class - */ - public static String createPostfix() { - return POSTFIX_TEMPLATE; - } - - /** - * Creates the template for a compilable class - * - * @param name the name of the class - * @param content the content of the class - * @return a String containing a compilable java code with the given content - */ - public static String createTemplate(String name, String content) { - return createPrefix(name) + content + createPostfix(); - - } - - private static final String OPEN_DIALOG_TITLE = "Open.dialog.title"; - private static final String OPEN_DIALOG_FILTER = "Open.dialog.filter"; - - /** - * Opens a FileChooser and loads the selected file. If the file is already - * loaded, the ProgramController requests the focus for the loaded program. - * Otherwise it creates a new program and a new stage. - * - * @param parent the window to open the chooser relative to the calling window - */ - public static void openProgram(Window parent) { - FileChooser fileChooser = new FileChooser(); - fileChooser.setTitle(i18n(OPEN_DIALOG_TITLE)); - fileChooser.setInitialDirectory(new File(PATH_TO_PROGRAMS)); - fileChooser.getExtensionFilters() - .add(new FileChooser.ExtensionFilter(i18n(OPEN_DIALOG_FILTER), "*" + DEFAULT_FILE_EXTENSION)); - - File file = fileChooser.showOpenDialog(parent); - if (file != null) { - String name = file.getName().replaceFirst(DEFAULT_FILE_EXTENSION, ""); - if (!programs.containsKey(name)) { - logger.debug("Opening '{}' since it is not loaded yet.", name); - Program program = new Program(file, name); - Stage stage = new MainStage(program); - programs.put(name, stage); - compile(program, false, parent); - } else { - logger.debug("File is already loaded! Requesting focus..."); - programs.get(name).requestFocus(); - } - } - - } - - /** - * Compiles the given Program and shows an error Alert if the Compilation failed - * otherwise it shows an success alert. If the compilation finished - * successfully, the new Robbi will be loaded into the territory. - * - * @param program the program to compile - * @param parent the calling window to show alerts relative to the calling - * window - */ - public static void compile(Program program, Window parent) { - compile(program, true, parent); - } - - /** - * Compiles the given Program and shows an error Alert if the Compilation failed - * otherwise it shows an success alert. If the compilation finished - * successfully, the new Robbi will be loaded into the territory. Before it does - * this, it checks if the Annotations are set correctly and if the main-Method - * is overwritten. An error with this post-compile check will be visualized with - * an Alert. - * - * @param program the program to compile - * @param showAlerts flag to determine if the alerts need to be shown or not - * @param parent the window calling the method in order to show the alerts - * relative to it - */ - public static void compile(Program program, boolean showAlerts, Window parent) { - - JavaCompiler javac = ToolProvider.getSystemJavaCompiler(); - DiagnosticCollector diagnostics = new DiagnosticCollector<>(); - - try (StandardJavaFileManager manager = javac.getStandardFileManager(diagnostics, null, null)) { - Iterable units = manager - .getJavaFileObjectsFromFiles(Arrays.asList(program.getFile())); - // https://stackoverflow.com/questions/60016127/can-toolprovider-getsystemjavacompiler-access-runtime-generated-in-memory-sour - CompilationTask task = javac.getTask(null, manager, diagnostics, List.of("-p", System.getProperty("jdk.module.path")), null, units); - task.addModules(List.of("RobbiSimulator")); //https://docs.oracle.com/javase%2F9%2Fdocs%2Fapi%2F%2F/javax/tools/JavaCompiler.CompilationTask.html - - if (Boolean.FALSE.equals(task.call())) { - boolean showedAlert = false; // flag to indicate that only one alert is shown - diagnostics.toString(); - logger.error("Compilation failed"); - for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { - diagnostic.toString(); - - logger.error("Kind: {}", diagnostic.getKind()); - logger.error("Quelle: {}", diagnostic.getSource()); - logger.error("Code und Nachricht: {}: {}", diagnostic.getCode(), diagnostic.getMessage(null)); - logger.error("Zeile: {}", diagnostic.getLineNumber()); - logger.error("Position/Spalte: {}/{}", diagnostic.getPosition(), diagnostic.getColumnNumber()); - logger.error("Startpostion/Endposition: {}/{}", diagnostic.getStartPosition(), - diagnostic.getEndPosition()); - // TODO change String.format to use i18n directly - if (showAlerts && !showedAlert) { - StringBuilder bobTheBuilder = new StringBuilder(); - bobTheBuilder.append(String.format(i18n(COMPILATION_DIAGNOSTIC_KIND), diagnostic.getKind())); - bobTheBuilder.append(String.format(i18n(COMPILATION_DIAGNOSTIC_CODEANDMESSAGE), - diagnostic.getCode(), diagnostic.getMessage(null))); - bobTheBuilder.append( - String.format(i18n(COMPILATION_DIAGNOSTIC_ROW), diagnostic.getLineNumber() - 1)); - AlertHelper.showAlertAndWait(AlertType.ERROR, bobTheBuilder.toString(), parent, - Modality.WINDOW_MODAL, i18n(COMPILATION_DIAGNOSTIC_TITLE), - diagnostic.getKind().toString()); - showedAlert = true; - } - } - } else {// compilation successful - logger.info("Compilation successful"); - Optional robbi = loadNewRobbi(program.getName()); - - robbi.ifPresentOrElse(r -> { - Diagnostics diag = new Diagnostics(); - if (!hasValidAnnotations(r, diag)) { - List diags = diag.getDiagnosis(); - String val = null; - String type = null; - if (!diags.isEmpty()) { - Diagnostics.Diagnostic diagnostic = diags.get(0); - val = diagnostic.value(); - type = diagnostic.type(); - logger.error("[Annotation Error]: {} is not applicable for {}", val, type); - } else - logger.error("[Annotation Error]: Error has been found but could not be diagnosed."); - - if (showAlerts) { - String msg = i18n(COMPILATION_ANNOTATIONS_MSG_DEFAULT); - if (val != null && type != null) { - msg = String.format(i18n(COMPILATION_ANNOTATIONS_MSG_INFO), val, type); - } - AlertHelper.showAlertAndWait(AlertType.WARNING, msg, parent, Modality.WINDOW_MODAL, - i18n(COMPILATION_ANNOTATIONS_TITLE), i18n(COMPILATION_ANNOTATIONS_HEADER)); - } - } else { - if (overwritesMainMethod(r)) { - // set new Robbi in territory - MainStage s = (MainStage) programs.get(program.getName()); - s.getTerritory().setRobbi(r); - logger.debug("New Robbi instance loaded"); - if (showAlerts) { - // TODO change to snackbar - AlertHelper.showAlertAndWait(AlertType.INFORMATION, - String.format(i18n(COMPILATION_SUCCESS_MESSAGE), program.getName()), parent, - Modality.WINDOW_MODAL, i18n(COMPILATION_SUCCESS_TITLE), - i18n(COMPILATION_SUCCESS_HEADER)); - } - } else { - AlertHelper.showAlertAndWait(AlertType.ERROR, i18n(COMPILATION_DIAGNOSTIC_OVERRIDE), - parent); - logger.error("The custom Robbi class does not overwrite the main-Method"); - } - } - }, () -> logger.error("Failed to load new Robbi instance...")); - - } - } catch (IOException e) { - e.printStackTrace(); - } - - } - - /** - * Checks if the robbi overwrites the Main-Method. - * - * @param robbi the robbi to check if it overwrites the Main-Method - * @return true if robbi overwrites it, false otherwise - */ - private static boolean overwritesMainMethod(Robbi robbi) { - if (Robbi.class == robbi.getClass()) { - return true; // Default Robbi always has main-Method overwritten - } - for (Method m : robbi.getClass().getDeclaredMethods()) { - if (m.getName().equals("main")) { - return true; - } - } - return false; - } - - /** - * Checks if the given robbi has valid annotations set. Currently it checks if - * the default Annotation has values matching its parameter - * - * @param robbi Instance of the users robbi implementation to check - * @return true if the annotions are valid, false otherwise - */ - private static boolean hasValidAnnotations(Robbi robbi, Diagnostics diag) { - Method[] methods = getCustomMethods(robbi); - boolean result = true; - for (Method method : methods) { - if (!hasValidDefaultAnnotation(method, diag)) { - result = false; - - // use return to fasten things up. Following annotation errors are ignored - } - } - return result; - } - - /** - * Checks if the method has Default annotations matching the type of its - * parameter - * - * @param method the method to check the annotations for - * @return true if and only if all Default annotations match their parameter - * type, false otherwise - */ - private static boolean hasValidDefaultAnnotation(Method method, Diagnostics diag) { - boolean result = true; - for (Parameter parameter : method.getParameters()) { - Annotation[] annotations = parameter.getAnnotations(); - for (Annotation annotation : annotations) { - if (annotation instanceof Default anno && !valueAcceptable(parameter, anno, diag)) { - result = false; - } - } - } - return result; - } - - /** - * Checks if the parameter and the Default Annotation match types. This is done - * by checking, if the value of the Default annotation can be parsed to the type - * of the parameter. If it fails, this method returns false. - * - * @param parameter The parameter to check the type of - * @param annotation The annotation whose value has to be checked against the - * parameter type - * @return true if the annotation value matches the parameter type, false - * otherwise - */ - private static boolean valueAcceptable(Parameter parameter, Default annotation, Diagnostics diag) { - String val = annotation.value(); - String type = parameter.getType().getName(); - try { - switch (type) { - case "int": - Integer.parseInt(val); - return true; - case "boolean": - return val.equalsIgnoreCase("true") || val.equalsIgnoreCase("false"); - case "char": - return !val.isBlank(); - case "double": - Double.parseDouble(val); - return true; - case "float": - Float.parseFloat(val); - return true; - case "long": - Long.parseLong(val); - return true; - case "String": - default: - return true; - } - } catch (IllegalArgumentException | IndexOutOfBoundsException e) { - diag.add(new Diagnostics.Diagnostic(type, val)); - return false; - } - - } - - /** - * Returns all methods, that are not part of the default robbi implementation, - * i.e. returns all methods the user has implemented - * - * @param robbi the new robbi instance - * @return an ArrayList of all new methods of the robbi - */ - private static Method[] getCustomMethods(Robbi robbi) { - List methods = new ArrayList<>(); - // if robbi is custom class - if (Robbi.class != robbi.getClass()) { - methods.addAll(Arrays.asList(robbi.getClass().getDeclaredMethods())); - } - return methods.toArray(Method[]::new); - } - - /** - * Loads a new Robbi by the name of the class - * - * @param name the name of the class - * @return an Optional of the given robbi class - */ - private static Optional loadNewRobbi(String name) { - Optional robbi; - try { - // if an old classloader exists, close it - if(classLoader != null) - classLoader.close(); + /** + * Constant String with the Path name for the programs' directory. + */ + public static final String PATH_TO_PROGRAMS = "programs"; + /** + * Constant String for the Default Robbi File name. + */ + public static final String DEFAULT_ROBBI_FILE_NAME = "DefaultRobbi"; + /** + * Constant String for the default file extension name. + */ + public static final String DEFAULT_FILE_EXTENSION = ".java"; + /** + * Constant String for the default content of the editor when the file is newly + * created.
+ * The comment "// place your code here" could also be internationalized. TODO + */ + public static final String DEFAULT_CONTENT = "void main(){" + System.lineSeparator() + "\t// place your code here" + + System.lineSeparator() + "}"; + /** + * Constant String for the editor prefix which is needed to compile the class + * and load into the simulator. This String will not be shown in the editor + * content. + */ + public static final String PREFIX_TEMPLATE = "import com.JayPi4c.RobbiSimulator.utils.annotations.*;import lombok.extern.slf4j.Slf4j; @Slf4j public class %s extends com.JayPi4c.RobbiSimulator.model.Robbi{"; + /** + * Constant String for the editor postfix, to close the class and make it + * compilable. This postfix will not be shown in the editor content. + */ + public static final String POSTFIX_TEMPLATE = System.lineSeparator() + "}"; + // language keys + private static final String NEW_DIALOG_TITLE = "New.dialog.title"; + private static final String NEW_DIALOG_HEADER = "New.dialog.header"; + private static final String NEW_DIALOG_PROMPT = "New.dialog.prompt"; + private static final String NEW_DIALOG_NAME = "New.dialog.name"; + private static final String COMPILATION_DIAGNOSTIC_KIND = "Compilation.diagnostic.kind"; + private static final String COMPILATION_DIAGNOSTIC_CODEANDMESSAGE = "Compilation.diagnostic.CodeAndMessage"; + private static final String COMPILATION_DIAGNOSTIC_ROW = "Compilation.diagnostic.row"; + private static final String COMPILATION_DIAGNOSTIC_TITLE = "Compilation.diagnostic.title"; + private static final String COMPILATION_ANNOTATIONS_MSG_DEFAULT = "Compilation.annotations.msg.default"; + private static final String COMPILATION_ANNOTATIONS_MSG_INFO = "Compilation.annotations.msg.info"; + private static final String COMPILATION_ANNOTATIONS_TITLE = "Compilation.annotations.title"; + private static final String COMPILATION_ANNOTATIONS_HEADER = "Compilation.annotations.header"; + private static final String COMPILATION_SUCCESS_MESSAGE = "Compilation.success.message"; + private static final String COMPILATION_SUCCESS_TITLE = "Compilation.success.title"; + private static final String COMPILATION_SUCCESS_HEADER = "Compilation.success.header"; + private static final String COMPILATION_DIAGNOSTIC_OVERRIDE = "Compilation.diagnostic.override"; + private static final String EXAMPLES_DUPLICATION_MESSAGE = "Examples.duplication.message"; + private static final String EXAMPLES_DUPLICATION_HEADER = "Examples.duplication.header"; + private static final String EXAMPLES_DUPLICATION_TITLE = "Examples.duplication.title"; + private static final String OPEN_DIALOG_TITLE = "Open.dialog.title"; + private static final String OPEN_DIALOG_FILTER = "Open.dialog.filter"; + private static HashMap programs; + private static URLClassLoader classLoader; + + /** + * Initializes the Application on startup and makes sure the programs folder and + * the DefaultRobbi class exists + * + * @return true if initialization finished successfully, false otherwise + */ + public static boolean initialize() { + File dir = new File(PATH_TO_PROGRAMS); + if (!dir.exists() && !dir.mkdir()) { + return false; + } + + File defaultProgram = new File( + PATH_TO_PROGRAMS + File.separatorChar + DEFAULT_ROBBI_FILE_NAME + DEFAULT_FILE_EXTENSION); + if (!defaultProgram.exists()) { + try { + if (!defaultProgram.createNewFile()) + return false; + } catch (IOException e) { + logger.error("Could not create file", e); + return false; + } + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(defaultProgram))) { + writer.write(createTemplate(DEFAULT_ROBBI_FILE_NAME, DEFAULT_CONTENT)); + } catch (IOException e) { + logger.error("Could not write to file", e); + return false; + } + } + programs = new HashMap<>(); + return true; + } + + /** + * Looks into the programs directory and collects all java filenames. + * + * @return Collection of java filenames in programs directory + */ + private static Collection getFilenamesInDirectory() { + // get a list of all files in programs directory that end with .java + File[] files = new File(PATH_TO_PROGRAMS) + .listFiles(file -> (file.isFile() && file.getName().endsWith(DEFAULT_FILE_EXTENSION))); + if (files == null) + return Collections.emptySet(); + // get a String collection with all filenames in the programs directory + Collection filenamesInDirectory = Arrays.stream(files) + .map(file -> file.getName().replace(DEFAULT_FILE_EXTENSION, "")) + .collect(Collectors.toCollection(ArrayList::new)); + + logger.debug("Found following files in 'programs' directory:"); + filenamesInDirectory.forEach(logger::debug); + return filenamesInDirectory; + } + + /** + * Creates a Dialog and asks the user to for the name of the new program. + * Afterward, it initiates the creation of a new stage for the program. + * + * @param parent the parent window to show alerts relative to the parent window + * @see ProgramController#createAndShow(String) + */ + public static void createAndShow(Window parent) { + Optional result = getNameForProgram(parent); + result.ifPresent(ProgramController::createAndShow); + } + + /** + * Asks the user to enter a name for the program. It only allows names, that are + * no java identifiers and are not already used in the programs folder + * + * @param parent window to show the dialog relative to + * @return the new name for the program + */ + private static Optional getNameForProgram(Window parent) { + Dialog dialog = new Dialog<>(); + dialog.setTitle(i18n(NEW_DIALOG_TITLE)); + dialog.setHeaderText(i18n(NEW_DIALOG_HEADER)); + dialog.initOwner(parent); + DialogPane dialogPane = dialog.getDialogPane(); + dialogPane.getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); + TextField nameField = new TextField(); + nameField.setPromptText(i18n(NEW_DIALOG_PROMPT)); + + Collection filenamesInDirectory = getFilenamesInDirectory(); + + nameField.textProperty() + .addListener((observable, oldVal, newVal) -> dialog.getDialogPane().lookupButton(ButtonType.OK) + .setDisable(SourceVersion.isKeyword(nameField.getText()) // https://stackoverflow.com/a/54141029/13670629 + || !SourceVersion.isIdentifier(nameField.getText()) + // || !nameField.getText().matches(VALID_IDENTIFIER_REGEX) + || filenamesInDirectory.contains(nameField.getText()))); + + dialog.getDialogPane().lookupButton(ButtonType.OK).setDisable(true); + GridPane grid = new GridPane(); + grid.addRow(0, new Label(i18n(NEW_DIALOG_NAME)), nameField); + + dialogPane.setContent(grid); + Platform.runLater(nameField::requestFocus); + dialog.setResultConverter(button -> (button == ButtonType.OK) ? nameField.getText() : null); + + return dialog.showAndWait(); + } + + /** + * Creates a new file with default content, if it does not exist. Afterwards, it + * creates a program and a new Stage. Finally, it compiles the code and loads + * the Robbi instance into the simulation + * + * @param programName the name of the program to create + */ + public static void createAndShow(String programName) { + String content = createTemplate(programName, DEFAULT_CONTENT); + File f = new File(PATH_TO_PROGRAMS + File.separatorChar + programName + DEFAULT_FILE_EXTENSION); + if (!f.exists()) { + try { + if (!f.createNewFile()) + logger.debug("Could not create file '{}'", f.getAbsolutePath()); + } catch (IOException e) { + logger.error("Failed to create file", e); + } + try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) { + writer.write(content); + } catch (IOException e) { + logger.error("Failed to write to file", e); + } + } + + Program program = new Program(f, programName); + Stage stage = new MainStage(program); + programs.put(programName, stage); + ProgramController.compile(program, false, stage); + } + + /** + * Creates a stage with the given programName, the programCode provided and the + * territory encoded as XML. If the user has already a stage with the same name, + * he will be warned, that his program will be overwritten. Optionally, he can + * save his program and territory under another name. After that a new Stage + * will be created according to the given information.
+ * This method might only be used to load an example from the database. + * + * @param programName name of the program + * @param programCode code of the program + * @param territoryXML XML-encoded territory + */ + public static void createAndShow(String programName, String programCode, String territoryXML) { + + String content = createTemplate(programName, DEFAULT_CONTENT); + File f = new File(PATH_TO_PROGRAMS + File.separatorChar + programName + DEFAULT_FILE_EXTENSION); + if (f.exists()) { + logger.debug("There is a file with the name of the example"); + if (programs.containsKey(programName)) { + logger.debug("Program is opened: closing..."); + MainStage stage = (MainStage) programs.get(programName); + Program p = stage.getProgram(); + p.save(p.getEditorContent()); + stage.close(); + } + Alert alert = AlertHelper.createAlert(AlertType.INFORMATION, i18n(EXAMPLES_DUPLICATION_MESSAGE), null); + alert.setHeaderText(i18n(EXAMPLES_DUPLICATION_HEADER)); + alert.setTitle(i18n(EXAMPLES_DUPLICATION_TITLE)); + alert.getButtonTypes().remove(ButtonType.OK); + alert.getButtonTypes().addAll(ButtonType.YES, ButtonType.NO); + Optional result = alert.showAndWait(); + if (result.isPresent()) { + ButtonType button = result.get(); + if (button.equals(ButtonType.YES)) { + Optional newName = getNameForProgram(null); + if (newName.isPresent()) { + String name = newName.get(); + + StringBuilder bobTheBuilder = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new FileReader(f))) { + String line = null; + while ((line = reader.readLine()) != null) { + bobTheBuilder.append(line); + bobTheBuilder.append(System.lineSeparator()); + } + bobTheBuilder.replace(0, createPrefix(programName).length(), createPrefix(name)); + + } catch (IOException e) { + logger.debug("Failed to read class contents"); + } + try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) { + writer.write(bobTheBuilder.toString()); + } catch (IOException e) { + logger.debug("Failed to write new classname in file"); + } + if (!renameFile(f, name)) { + logger.debug("Failed to rename file"); + } + } + } + } + } + + if (!f.exists()) { + try { + if (!f.createNewFile()) + logger.debug("Could not create file '{}'", f.getAbsolutePath()); + } catch (IOException e) { + logger.error("Failed to create file", e); + } + try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) { + writer.write(content); + } catch (IOException e) { + logger.error("Failed to write to file", e); + } + } + + Program program = new Program(f, programName); + program.setEdited(true); + program.save(programCode); + MainStage stage = new MainStage(program); + stage.getTerritory().fromXML(new ByteArrayInputStream(territoryXML.getBytes())); + programs.put(programName, stage); + ProgramController.compile(program, false, stage); + } + + /** + * Renames a java file in the programs directory to the new name. + * + * @param f the file to rename + * @param newName the new name of the file + * @return true if the renaming was successful, false otherwise + */ + public static boolean renameFile(File f, String newName) { + File newFile = new File(PATH_TO_PROGRAMS + File.separatorChar + newName + DEFAULT_FILE_EXTENSION); + + if (newFile.exists()) + return false; + return f.renameTo(newFile); + } + + /** + * Creates the prefix for the program-file + * + * @param name the name of the class + * @return the prefix for the class + */ + public static String createPrefix(String name) { + return String.format(PREFIX_TEMPLATE, name); + } + + /** + * Creates the postfix for the program-file + * + * @return the postfix for the class + */ + public static String createPostfix() { + return POSTFIX_TEMPLATE; + } + + /** + * Creates the template for a compilable class + * + * @param name the name of the class + * @param content the content of the class + * @return a String containing a compilable java code with the given content + */ + public static String createTemplate(String name, String content) { + return createPrefix(name) + content + createPostfix(); + + } + + /** + * Opens a FileChooser and loads the selected file. If the file is already + * loaded, the ProgramController requests the focus for the loaded program. + * Otherwise it creates a new program and a new stage. + * + * @param parent the window to open the chooser relative to the calling window + */ + public static void openProgram(Window parent) { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle(i18n(OPEN_DIALOG_TITLE)); + fileChooser.setInitialDirectory(new File(PATH_TO_PROGRAMS)); + fileChooser.getExtensionFilters() + .add(new FileChooser.ExtensionFilter(i18n(OPEN_DIALOG_FILTER), "*" + DEFAULT_FILE_EXTENSION)); + + File file = fileChooser.showOpenDialog(parent); + if (file != null) { + String name = file.getName().replaceFirst(DEFAULT_FILE_EXTENSION, ""); + if (!programs.containsKey(name)) { + logger.debug("Opening '{}' since it is not loaded yet.", name); + Program program = new Program(file, name); + Stage stage = new MainStage(program); + programs.put(name, stage); + compile(program, false, parent); + } else { + logger.debug("File is already loaded! Requesting focus..."); + programs.get(name).requestFocus(); + } + } + + } + + /** + * Compiles the given Program and shows an error Alert if the Compilation failed + * otherwise it shows an success alert. If the compilation finished + * successfully, the new Robbi will be loaded into the territory. + * + * @param program the program to compile + * @param parent the calling window to show alerts relative to the calling + * window + */ + public static void compile(Program program, Window parent) { + compile(program, true, parent); + } + + /** + * Compiles the given Program and shows an error Alert if the Compilation failed + * otherwise it shows an success alert. If the compilation finished + * successfully, the new Robbi will be loaded into the territory. Before it does + * this, it checks if the Annotations are set correctly and if the main-Method + * is overwritten. An error with this post-compile check will be visualized with + * an Alert. + * + * @param program the program to compile + * @param showAlerts flag to determine if the alerts need to be shown or not + * @param parent the window calling the method in order to show the alerts + * relative to it + */ + public static void compile(Program program, boolean showAlerts, Window parent) { + + JavaCompiler javac = ToolProvider.getSystemJavaCompiler(); + DiagnosticCollector diagnostics = new DiagnosticCollector<>(); + + try (StandardJavaFileManager manager = javac.getStandardFileManager(diagnostics, null, null)) { + Iterable units = manager + .getJavaFileObjectsFromFiles(Collections.singletonList(program.getFile())); + // https://stackoverflow.com/questions/60016127/can-toolprovider-getsystemjavacompiler-access-runtime-generated-in-memory-sour + CompilationTask task = javac.getTask(null, manager, diagnostics, List.of("-p", System.getProperty("jdk.module.path")), null, units); + task.addModules(List.of("RobbiSimulator")); //https://docs.oracle.com/javase%2F9%2Fdocs%2Fapi%2F%2F/javax/tools/JavaCompiler.CompilationTask.html + + if (Boolean.FALSE.equals(task.call())) { + boolean showedAlert = false; // flag to indicate that only one alert is shown + logger.error("Compilation failed"); + for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { + + logger.error("Kind: {}", diagnostic.getKind()); + logger.error("Quelle: {}", diagnostic.getSource()); + logger.error("Code und Nachricht: {}: {}", diagnostic.getCode(), diagnostic.getMessage(null)); + logger.error("Zeile: {}", diagnostic.getLineNumber()); + logger.error("Position/Spalte: {}/{}", diagnostic.getPosition(), diagnostic.getColumnNumber()); + logger.error("Startpostion/Endposition: {}/{}", diagnostic.getStartPosition(), + diagnostic.getEndPosition()); + // TODO change String.format to use i18n directly + if (showAlerts && !showedAlert) { + String bobTheBuilder = String.format(i18n(COMPILATION_DIAGNOSTIC_KIND), diagnostic.getKind()) + + String.format(i18n(COMPILATION_DIAGNOSTIC_CODEANDMESSAGE), + diagnostic.getCode(), diagnostic.getMessage(null)) + + String.format(i18n(COMPILATION_DIAGNOSTIC_ROW), diagnostic.getLineNumber() - 1); + AlertHelper.showAlertAndWait(AlertType.ERROR, bobTheBuilder, parent, + Modality.WINDOW_MODAL, i18n(COMPILATION_DIAGNOSTIC_TITLE), + diagnostic.getKind().toString()); + showedAlert = true; + } + } + } else {// compilation successful + logger.info("Compilation successful"); + Optional robbi = loadNewRobbi(program.getName()); + + robbi.ifPresentOrElse(r -> { + Diagnostics diag = new Diagnostics(); + if (!hasValidAnnotations(r, diag)) { + List diags = diag.getDiagnosis(); + String val = null; + String type = null; + if (!diags.isEmpty()) { + Diagnostics.Diagnostic diagnostic = diags.getFirst(); + val = diagnostic.value(); + type = diagnostic.type(); + logger.error("[Annotation Error]: {} is not applicable for {}", val, type); + } else + logger.error("[Annotation Error]: Error has been found but could not be diagnosed."); + + if (showAlerts) { + String msg = i18n(COMPILATION_ANNOTATIONS_MSG_DEFAULT); + if (val != null && type != null) { + msg = String.format(i18n(COMPILATION_ANNOTATIONS_MSG_INFO), val, type); + } + AlertHelper.showAlertAndWait(AlertType.WARNING, msg, parent, Modality.WINDOW_MODAL, + i18n(COMPILATION_ANNOTATIONS_TITLE), i18n(COMPILATION_ANNOTATIONS_HEADER)); + } + } else { + if (overwritesMainMethod(r)) { + // set new Robbi in territory + MainStage s = (MainStage) programs.get(program.getName()); + s.getTerritory().setRobbi(r); + logger.debug("New Robbi instance loaded"); + if (showAlerts) { + ((MainStage) parent).getNotificationController().showMessage(5000, "Snackbar.message.compile.success", program.getName()); + } + } else { + AlertHelper.showAlertAndWait(AlertType.ERROR, i18n(COMPILATION_DIAGNOSTIC_OVERRIDE), + parent); + logger.error("The custom Robbi class does not overwrite the main-Method"); + } + } + }, () -> logger.error("Failed to load new Robbi instance...")); + + } + } catch (IOException e) { + logger.error("Failed to compile program", e); + } + + } + + /** + * Checks if the robbi overwrites the Main-Method. + * + * @param robbi the robbi to check if it overwrites the Main-Method + * @return true if robbi overwrites it, false otherwise + */ + private static boolean overwritesMainMethod(Robbi robbi) { + if (Robbi.class == robbi.getClass()) { + return true; // Default Robbi always has main-Method overwritten + } + for (Method m : robbi.getClass().getDeclaredMethods()) { + if (m.getName().equals("main")) { + return true; + } + } + return false; + } + + /** + * Checks if the given robbi has valid annotations set. Currently it checks if + * the default Annotation has values matching its parameter + * + * @param robbi Instance of the users robbi implementation to check + * @return true if the annotions are valid, false otherwise + */ + private static boolean hasValidAnnotations(Robbi robbi, Diagnostics diag) { + Method[] methods = getCustomMethods(robbi); + boolean result = true; + for (Method method : methods) { + if (!hasValidDefaultAnnotation(method, diag)) { + result = false; + + // use return to fasten things up. Following annotation errors are ignored + } + } + return result; + } + + /** + * Checks if the method has Default annotations matching the type of its + * parameter + * + * @param method the method to check the annotations for + * @return true if and only if all Default annotations match their parameter + * type, false otherwise + */ + private static boolean hasValidDefaultAnnotation(Method method, Diagnostics diag) { + boolean result = true; + for (Parameter parameter : method.getParameters()) { + Annotation[] annotations = parameter.getAnnotations(); + for (Annotation annotation : annotations) { + if (annotation instanceof Default anno && !valueAcceptable(parameter, anno, diag)) { + result = false; + } + } + } + return result; + } + + /** + * Checks if the parameter and the Default Annotation match types. This is done + * by checking, if the value of the Default annotation can be parsed to the type + * of the parameter. If it fails, this method returns false. + * + * @param parameter The parameter to check the type of + * @param annotation The annotation whose value has to be checked against the + * parameter type + * @return true if the annotation value matches the parameter type, false + * otherwise + */ + private static boolean valueAcceptable(Parameter parameter, Default annotation, Diagnostics diag) { + String val = annotation.value(); + String type = parameter.getType().getName(); + try { + switch (type) { + case "int": + Integer.parseInt(val); + return true; + case "boolean": + return val.equalsIgnoreCase("true") || val.equalsIgnoreCase("false"); + case "char": + return !val.isBlank(); + case "double": + Double.parseDouble(val); + return true; + case "float": + Float.parseFloat(val); + return true; + case "long": + Long.parseLong(val); + return true; + case "String": + default: + return true; + } + } catch (IllegalArgumentException | IndexOutOfBoundsException e) { + diag.add(new Diagnostics.Diagnostic(type, val)); + return false; + } + + } + + /** + * Returns all methods, that are not part of the default robbi implementation, + * i.e. returns all methods the user has implemented + * + * @param robbi the new robbi instance + * @return an ArrayList of all new methods of the robbi + */ + private static Method[] getCustomMethods(Robbi robbi) { + List methods = new ArrayList<>(); + // if robbi is custom class + if (Robbi.class != robbi.getClass()) { + methods.addAll(Arrays.asList(robbi.getClass().getDeclaredMethods())); + } + return methods.toArray(Method[]::new); + } + + /** + * Loads a new Robbi by the name of the class + * + * @param name the name of the class + * @return an Optional of the given robbi class + */ + private static Optional loadNewRobbi(String name) { + Optional robbi; + try { + // if an old classloader exists, close it + if (classLoader != null) + classLoader.close(); // cant be closed here because then the simulation thread can't access lazily loaded classes loaded with this classloader - // maybe think about a factory solution: https://stackoverflow.com/a/13946807 - // see this whole discussion about when to close a classloader: https://stackoverflow.com/q/13944868 - classLoader = URLClassLoader.newInstance(new URL[] { new File(PATH_TO_PROGRAMS).toURI().toURL() }); - - Constructor c = classLoader.loadClass(name).getConstructor(); - Robbi r = (Robbi) c.newInstance(); - logger.debug("Loaded class of '{}'", name); - robbi = Optional.of(r); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | ClassNotFoundException - | NoSuchMethodException | SecurityException | IOException e) { - logger.error("Could not load class of '{}'. Message: {}", name, e.getMessage()); - robbi = Optional.empty(); - } - return robbi; - } - - /** - * Loads a new Robbi Instance from the classfile. - * - * @param name the name of the Robbi-Class - * @return An Optional with the Robbi instance or an empty optional if the - * loading failed. - */ - public static Optional getNewRobbi(String name) { - Optional robbi = loadNewRobbi(name); - if (robbi.isPresent()) { - Robbi r = robbi.get(); - Diagnostics diag = new Diagnostics(); - if (!hasValidAnnotations(r, diag)) { - logger.debug("{} has no valid annotations.", name); - } else { - if (overwritesMainMethod(r)) { - return Optional.of(r); - } else { - logger.error("The custom Robbi class does not overwrite the main-Method"); - } - } - } else - logger.error("Failed to load new Robbi instance..."); - return Optional.empty(); - } - - /** - * Removes the program from the programsList in order to make sure it can be - * reopened later - * - * @param name the name of the program which is closed - */ - public static void close(String name) { - programs.remove(name); - logger.debug("Closing program '{}'", name); - } + // maybe think about a factory solution: https://stackoverflow.com/a/13946807 + // see this whole discussion about when to close a classloader: https://stackoverflow.com/q/13944868 + classLoader = URLClassLoader.newInstance(new URL[]{new File(PATH_TO_PROGRAMS).toURI().toURL()}); + + Constructor c = classLoader.loadClass(name).getConstructor(); + Robbi r = (Robbi) c.newInstance(); + logger.debug("Loaded class of '{}'", name); + robbi = Optional.of(r); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | ClassNotFoundException + | NoSuchMethodException | SecurityException | IOException e) { + logger.error("Could not load class of '{}'. Message: {}", name, e.getMessage()); + robbi = Optional.empty(); + } + return robbi; + } + + /** + * Loads a new Robbi Instance from the classfile. + * + * @param name the name of the Robbi-Class + * @return An Optional with the Robbi instance or an empty optional if the + * loading failed. + */ + public static Optional getNewRobbi(String name) { + Optional robbi = loadNewRobbi(name); + if (robbi.isPresent()) { + Robbi r = robbi.get(); + Diagnostics diag = new Diagnostics(); + if (!hasValidAnnotations(r, diag)) { + logger.debug("{} has no valid annotations.", name); + } else { + if (overwritesMainMethod(r)) { + return Optional.of(r); + } else { + logger.error("The custom Robbi class does not overwrite the main-Method"); + } + } + } else + logger.error("Failed to load new Robbi instance..."); + return Optional.empty(); + } + + /** + * Removes the program from the programsList in order to make sure it can be + * reopened later + * + * @param name the name of the program which is closed + */ + public static void close(String name) { + programs.remove(name); + logger.debug("Closing program '{}'", name); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/simulation/Simulation.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/simulation/Simulation.java index c8a8c11..05d48a2 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/simulation/Simulation.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/simulation/Simulation.java @@ -1,15 +1,11 @@ package com.JayPi4c.RobbiSimulator.controller.simulation; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - import com.JayPi4c.RobbiSimulator.model.RobbiException; import com.JayPi4c.RobbiSimulator.model.Territory; import com.JayPi4c.RobbiSimulator.utils.AlertHelper; import com.JayPi4c.RobbiSimulator.utils.Observable; import com.JayPi4c.RobbiSimulator.utils.Observer; import com.JayPi4c.RobbiSimulator.utils.SoundManager; - import javafx.application.Platform; import javafx.scene.control.Alert.AlertType; import javafx.stage.Window; @@ -17,109 +13,111 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + /** - * This Simulation class is a separate thread that runs the code of robbis + * This Simulation class is a separate thread that runs the code of robbi's * main-Method. It can be paused, resumed and stopped by the user at any time. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Slf4j public class Simulation extends Thread implements Observer { - private Territory territory; - private SimulationController simController; - private Window parent; + private final Territory territory; + private final SimulationController simController; + private final Window parent; - @Getter - @Setter - private volatile boolean stopped; - @Getter - @Setter - private volatile boolean paused; - @Getter - private final Object lock = new Object(); + @Getter + @Setter + private volatile boolean stopped; + @Getter + @Setter + private volatile boolean paused; + @Getter + private final Object lock = new Object(); - /** - * Constructor to create a new Simulation, which can execute robbis main-Method. - * - * @param territory The territory in which the simulation is happening - * @param simController The SimulationController, which started this simulation - * @param parent The parent window to show alerts relative to the it - */ - public Simulation(Territory territory, SimulationController simController, Window parent) { - this.territory = territory; - this.simController = simController; - this.parent = parent; - stopped = false; - paused = false; - } + /** + * Constructor to create a new Simulation, which can execute robbi's main-Method. + * + * @param territory The territory in which the simulation is happening + * @param simController The SimulationController, which started this simulation + * @param parent The parent window to show alerts relative to it + */ + public Simulation(Territory territory, SimulationController simController, Window parent) { + this.territory = territory; + this.simController = simController; + this.parent = parent; + stopped = false; + paused = false; + } - /** - * Starts the simulation and triggers the robbis main-Method. It informs the - * user if the execution finished in an unexpected way. In the end it does some - * finalization. - */ - @Override - public void run() { - logger.info("Simulation started"); - territory.addObserver(this); - try { - Method main = territory.getRobbi().getClass().getDeclaredMethod("main"); - main.setAccessible(true); - main.invoke(territory.getRobbi()); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException - | SecurityException e) { - if (e.getCause() instanceof StopException) { - logger.debug("Simulation stopped"); - } else if (e.getCause() instanceof RobbiException re) { - logger.debug("Simulation stopped with exception: {}", re.getMessage()); - SoundManager.playWarnSound(); - // TODO: change to Snackbar? - Platform.runLater( - () -> AlertHelper.showAlertAndWait(AlertType.ERROR, re.getLocalizedMessage(), parent)); - } else - e.printStackTrace(); - } finally { - stopped = true; - territory.removeObserver(this); - simController.finish(); - logger.info("Simulation done."); - } + /** + * Starts the simulation and triggers the robbi's main-Method. It informs the + * user if the execution finished in an unexpected way. In the end it does some + * finalization. + */ + @Override + public void run() { + logger.info("Simulation started"); + territory.addObserver(this); + try { + Method main = territory.getRobbi().getClass().getDeclaredMethod("main"); + main.setAccessible(true); + main.invoke(territory.getRobbi()); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException + | SecurityException e) { + if (e.getCause() instanceof StopException) { + logger.debug("Simulation stopped"); + } else if (e.getCause() instanceof RobbiException re) { + logger.debug("Simulation stopped with exception: {}", re.getMessage()); + SoundManager.playWarnSound(); + // TODO: change to Snackbar? + Platform.runLater( + () -> AlertHelper.showAlertAndWait(AlertType.ERROR, re.getLocalizedMessage(), parent)); + } else + logger.error("Simulation stopped with unwanted exception", e); + } finally { + stopped = true; + territory.removeObserver(this); + simController.finish(); + logger.info("Simulation done."); + } - } + } - /** - * If the territory has been updated by a non-FXApplicationThread, this thread - * is sleeping for the time provided by the SimulationController. Furthermore, - * it waits while the simulation is stopped and throws an StopException if the - * Simulation has been terminated by the user. - * - * @param observable The observable, which caused the update to trigger - */ - @Override - public void update(Observable observable) { - if (Platform.isFxApplicationThread()) // if observable is called by gui, ignore it - return; - try { - sleep(simController.getSpeed()); - } catch (InterruptedException e) { - logger.debug("Stopping simulation during sleep"); - Thread.currentThread().interrupt(); - } - if (this.stopped) - throw new StopException(); - while (this.paused) - synchronized (lock) { - try { - lock.wait(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - if (stopped) - throw new StopException(); + /** + * If the territory has been updated by a non-FXApplicationThread, this thread + * is sleeping for the time provided by the SimulationController. Furthermore, + * it waits while the simulation is stopped and throws an StopException if the + * Simulation has been terminated by the user. + * + * @param observable The observable, which caused the update to trigger + */ + @Override + public void update(Observable observable) { + if (Platform.isFxApplicationThread()) // if observable is called by gui, ignore it + return; + try { + sleep(simController.getSpeed()); + } catch (InterruptedException e) { + logger.debug("Stopping simulation during sleep"); + Thread.currentThread().interrupt(); + } + if (this.stopped) + throw new StopException(); + while (this.paused) + synchronized (lock) { + try { + lock.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + if (stopped) + throw new StopException(); - } + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/simulation/SimulationController.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/simulation/SimulationController.java index 0e32940..6cb1546 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/simulation/SimulationController.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/simulation/SimulationController.java @@ -1,14 +1,11 @@ package com.JayPi4c.RobbiSimulator.controller.simulation; -import java.util.Optional; - import com.JayPi4c.RobbiSimulator.controller.program.ProgramController; import com.JayPi4c.RobbiSimulator.model.Robbi; import com.JayPi4c.RobbiSimulator.model.Territory; import com.JayPi4c.RobbiSimulator.model.TerritoryState; import com.JayPi4c.RobbiSimulator.view.MainStage; import com.JayPi4c.RobbiSimulator.view.Toolbar; - import javafx.application.Platform; import javafx.scene.control.Button; import javafx.scene.control.MenuItem; @@ -16,223 +13,224 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import java.util.Optional; + /** * Controller to handle all actions belonging to the simulation. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Slf4j public class SimulationController { - private static final int MIN_SPEED = 100; - private static final int MAX_SPEED = 2500; - - private Simulation simulation; - - private MainStage stage; - private Territory territory; - - @Getter - private volatile int speed; - - private MenuItem resetMenuItem; - private Button resetToolbar; - - private MenuItem pauseMenuItem; - private ToggleButton pauseToolbar; - private MenuItem startMenuItem; - private ToggleButton startToolbar; - private MenuItem stopMenuItem; - private ToggleButton stopToolbar; - - private TerritoryState territoryState; - - /** - * Constructor to create a new SimulationController, which adds all actions to - * the corresponding gui elements. - * - * @param stage The stage, this controller is for - * @param territory the territory in which the simulation takes place - */ - public SimulationController(MainStage stage, Territory territory) { - this.stage = stage; - this.territory = territory; - Toolbar toolbar = stage.getToolbar(); - speed = (int) toolbar.getSpeedSliderToolbar().getValue(); - - resetToolbar = toolbar.getResetButtonToolbar(); - resetToolbar.setOnAction(e -> reset()); - resetMenuItem = this.stage.getMenubar().getResetMenuItem(); - resetMenuItem.onActionProperty().bind(resetToolbar.onActionProperty()); - - startToolbar = toolbar.getStartToggleButtonToolbar(); - startToolbar.setOnAction(e -> { - if (!isSimulationRunning()) - start(); - else - resume(); - }); - startMenuItem = stage.getMenubar().getStartMenuItem(); - startMenuItem.onActionProperty().bind(startToolbar.onActionProperty()); - startMenuItem.disableProperty().bind(startToolbar.disableProperty()); - - pauseToolbar = toolbar.getPauseToggleButtonToolbar(); - pauseToolbar.setOnAction(e -> pause()); - pauseMenuItem = this.stage.getMenubar().getPauseMenuItem(); - pauseMenuItem.onActionProperty().bind(pauseToolbar.onActionProperty()); - pauseMenuItem.disableProperty().bind(pauseToolbar.disableProperty()); - - stopToolbar = toolbar.getStopToggleButtonToolbar(); - stopToolbar.setOnAction(e -> stop()); - stopMenuItem = this.stage.getMenubar().getStopMenuItem(); - stopMenuItem.onActionProperty().bind(stopToolbar.onActionProperty()); - stopMenuItem.disableProperty().bind(stopToolbar.disableProperty()); - - toolbar.getSpeedSliderToolbar().valueProperty().addListener((ov, oldVal, newVal) -> setSpeed((Double) newVal)); - setSpeed(toolbar.getSpeedSliderToolbar().getValue()); - disableButtonStates(false, true, true); - - } - - /** - * Helper to start e new simulation. - */ - private void start() { - logger.debug("Starting new simulation"); - Optional r = ProgramController.getNewRobbi(stage.getProgram().getName()); - if (r.isPresent()) - territory.setRobbi(r.get()); - else - logger.debug("Could not initialize Robbi"); - territoryState = territory.save(); - simulation = new Simulation(territory, this, stage); - simulation.setDaemon(true); // program should exit even if simulation is running - simulation.start(); - disableButtonStates(true, false, false); - } - - /** - * Helper to pause the current simulation. - */ - private void pause() { - logger.debug("Pausing simulation"); - simulation.setPaused(true); - disableButtonStates(false, true, false); - } - - /** - * Helper to resume the current simulation. - */ - private void resume() { - logger.debug("Resuming simulation"); - simulation.setPaused(false); - synchronized (simulation.getLock()) { - simulation.getLock().notifyAll(); - } - disableButtonStates(true, false, false); - } - - /** - * Helper to stop the current simulation. - */ - private void stop() { - logger.debug("Stopping simulation"); - simulation.setStopped(true); - simulation.setPaused(false); - simulation.interrupt(); - synchronized (simulation.getLock()) { - simulation.getLock().notifyAll(); - } - } - - /** - * Helper to reset the territory to the state at the beginning of the - * simulation. - */ - private void reset() { - logger.debug("Resetting the simulation"); - if (isSimulationRunning()) - stop(); - - if (territoryState != null) - territory.restore(territoryState); - } - - /** - * Helper to check if the simulation is currently running - * - * @return true if the simulation is not null or not stopped - */ - private boolean isSimulationRunning() { - return !(simulation == null || simulation.isStopped()); - } - - /** - * Stops the simulation if one exists. - */ - public void stopSimulation() { - if (simulation != null) { - stop(); - } - } - - /** - * Helper to finish up a simulation if it is finished or has been stopped by the - * user. - */ - public void finish() { - Platform.runLater(() -> { - disableButtonStates(false, true, true); - startToolbar.setSelected(false); - }); - } - - /** - * Helper to set the buttonstates according the the given parameters. - * - * @param start true, to disable start simulation button - * @param pause true, to disable pause simulation button - * @param stop true, to disable stop simulation button - */ - private void disableButtonStates(boolean start, boolean pause, boolean stop) { - if (!Platform.isFxApplicationThread()) { - Platform.runLater(() -> { - startToolbar.setDisable(start); - pauseToolbar.setDisable(pause); - stopToolbar.setDisable(stop); - }); - } else { - startToolbar.setDisable(start); - pauseToolbar.setDisable(pause); - stopToolbar.setDisable(stop); - } - } - - /** - * Updates the speed attribute to the given parameter. - * - * @param speed the new speed value - */ - public void setSpeed(double speed) { - this.speed = (int) map(speed, Toolbar.MIN_SPEED_VALUE, Toolbar.MAX_SPEED_VALUE, MAX_SPEED, MIN_SPEED); - } - - /** - * Maps the given value, which ranges between istart and istop on a value which - * ranges between ostart and ostop. - * - * @see Stackoverflow - * @param value value to map - * @param istart input start - * @param istop input stop - * @param ostart output start - * @param ostop output stop - * @return the mapped value - */ - private final double map(double value, double istart, double istop, double ostart, double ostop) { - return ostart + (ostop - ostart) * ((value - istart) / (istop - istart)); - } + private static final int MIN_SPEED = 100; + private static final int MAX_SPEED = 2500; + + private Simulation simulation; + + private final MainStage stage; + private final Territory territory; + + @Getter + private volatile int speed; + + private final MenuItem resetMenuItem; + private final Button resetToolbar; + + private final MenuItem pauseMenuItem; + private final ToggleButton pauseToolbar; + private final MenuItem startMenuItem; + private final ToggleButton startToolbar; + private final MenuItem stopMenuItem; + private final ToggleButton stopToolbar; + + private TerritoryState territoryState; + + /** + * Constructor to create a new SimulationController, which adds all actions to + * the corresponding gui elements. + * + * @param stage The stage, this controller is for + * @param territory the territory in which the simulation takes place + */ + public SimulationController(MainStage stage, Territory territory) { + this.stage = stage; + this.territory = territory; + Toolbar toolbar = stage.getToolbar(); + speed = (int) toolbar.getSpeedSliderToolbar().getValue(); + + resetToolbar = toolbar.getResetButtonToolbar(); + resetToolbar.setOnAction(e -> reset()); + resetMenuItem = this.stage.getMenubar().getResetMenuItem(); + resetMenuItem.onActionProperty().bind(resetToolbar.onActionProperty()); + + startToolbar = toolbar.getStartToggleButtonToolbar(); + startToolbar.setOnAction(e -> { + if (!isSimulationRunning()) + start(); + else + resume(); + }); + startMenuItem = stage.getMenubar().getStartMenuItem(); + startMenuItem.onActionProperty().bind(startToolbar.onActionProperty()); + startMenuItem.disableProperty().bind(startToolbar.disableProperty()); + + pauseToolbar = toolbar.getPauseToggleButtonToolbar(); + pauseToolbar.setOnAction(e -> pause()); + pauseMenuItem = this.stage.getMenubar().getPauseMenuItem(); + pauseMenuItem.onActionProperty().bind(pauseToolbar.onActionProperty()); + pauseMenuItem.disableProperty().bind(pauseToolbar.disableProperty()); + + stopToolbar = toolbar.getStopToggleButtonToolbar(); + stopToolbar.setOnAction(e -> stop()); + stopMenuItem = this.stage.getMenubar().getStopMenuItem(); + stopMenuItem.onActionProperty().bind(stopToolbar.onActionProperty()); + stopMenuItem.disableProperty().bind(stopToolbar.disableProperty()); + + toolbar.getSpeedSliderToolbar().valueProperty().addListener((ov, oldVal, newVal) -> setSpeed((Double) newVal)); + setSpeed(toolbar.getSpeedSliderToolbar().getValue()); + disableButtonStates(false, true, true); + + } + + /** + * Helper to start e new simulation. + */ + private void start() { + logger.debug("Starting new simulation"); + Optional r = ProgramController.getNewRobbi(stage.getProgram().getName()); + if (r.isPresent()) + territory.setRobbi(r.get()); + else + logger.debug("Could not initialize Robbi"); + territoryState = territory.save(); + simulation = new Simulation(territory, this, stage); + simulation.setDaemon(true); // program should exit even if simulation is running + simulation.start(); + disableButtonStates(true, false, false); + } + + /** + * Helper to pause the current simulation. + */ + private void pause() { + logger.debug("Pausing simulation"); + simulation.setPaused(true); + disableButtonStates(false, true, false); + } + + /** + * Helper to resume the current simulation. + */ + private void resume() { + logger.debug("Resuming simulation"); + simulation.setPaused(false); + synchronized (simulation.getLock()) { + simulation.getLock().notifyAll(); + } + disableButtonStates(true, false, false); + } + + /** + * Helper to stop the current simulation. + */ + private void stop() { + logger.debug("Stopping simulation"); + simulation.setStopped(true); + simulation.setPaused(false); + simulation.interrupt(); + synchronized (simulation.getLock()) { + simulation.getLock().notifyAll(); + } + } + + /** + * Helper to reset the territory to the state at the beginning of the + * simulation. + */ + private void reset() { + logger.debug("Resetting the simulation"); + if (isSimulationRunning()) + stop(); + + if (territoryState != null) + territory.restore(territoryState); + } + + /** + * Helper to check if the simulation is currently running + * + * @return true if the simulation is not null or not stopped + */ + private boolean isSimulationRunning() { + return !(simulation == null || simulation.isStopped()); + } + + /** + * Stops the simulation if one exists. + */ + public void stopSimulation() { + if (simulation != null) { + stop(); + } + } + + /** + * Helper to finish up a simulation if it is finished or has been stopped by the + * user. + */ + public void finish() { + Platform.runLater(() -> { + disableButtonStates(false, true, true); + startToolbar.setSelected(false); + }); + } + + /** + * Helper to set the buttonstates according the the given parameters. + * + * @param start true, to disable start simulation button + * @param pause true, to disable pause simulation button + * @param stop true, to disable stop simulation button + */ + private void disableButtonStates(boolean start, boolean pause, boolean stop) { + if (!Platform.isFxApplicationThread()) { + Platform.runLater(() -> { + startToolbar.setDisable(start); + pauseToolbar.setDisable(pause); + stopToolbar.setDisable(stop); + }); + } else { + startToolbar.setDisable(start); + pauseToolbar.setDisable(pause); + stopToolbar.setDisable(stop); + } + } + + /** + * Updates the speed attribute to the given parameter. + * + * @param speed the new speed value + */ + public void setSpeed(double speed) { + this.speed = (int) map(speed, Toolbar.MIN_SPEED_VALUE, Toolbar.MAX_SPEED_VALUE, MAX_SPEED, MIN_SPEED); + } + + /** + * Maps the given value, which ranges between inStart and inStop, on a value which + * ranges between outStart and outStop. + * + * @param value value to map + * @param inStart input start + * @param inStop input stop + * @param outStart output start + * @param outStop output stop + * @return the mapped value + * @see Stackoverflow + */ + private double map(double value, double inStart, double inStop, double outStart, double outStop) { + return outStart + (outStop - outStart) * ((value - inStart) / (inStop - inStart)); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/simulation/StopException.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/simulation/StopException.java index 4526466..af53ce6 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/simulation/StopException.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/simulation/StopException.java @@ -4,12 +4,9 @@ * This Exception indicates, that the simulation has been interrupted. It will * be thrown, if the user hits the stop button while the main Method of robbi is * still running. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public class StopException extends RuntimeException { - private static final long serialVersionUID = 1L; - } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/Answer.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/Answer.java index 92e0f8f..ddadb63 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/Answer.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/Answer.java @@ -1,14 +1,18 @@ package com.JayPi4c.RobbiSimulator.controller.tutor; +import java.io.Serial; import java.io.Serializable; /** * Wrapper to hold an answer instance. - * - * @author Jonas Pohl + * * @param code the code which is stored in the editor * @param territory the territory encoded as XML string + * @author Jonas Pohl */ public record Answer(String code, String territory) implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/ITutor.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/ITutor.java index 398e77c..0e705a5 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/ITutor.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/ITutor.java @@ -5,31 +5,30 @@ /** * Interface for RMI communication - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public interface ITutor extends Remote { - /** - * Sends a request of code and territory to the tutor and returns the id of the - * request at the tutors instance. - * - * @param code the requests code - * @param territory the requests territory - * @return the requests id - * @throws RemoteException thrown on connection errors - */ - public int sendRequest(String code, String territory) throws RemoteException; + /** + * Sends a request of code and territory to the tutor and returns the id of the + * request at the tutors instance. + * + * @param code the requests code + * @param territory the requests territory + * @return the requests id + * @throws RemoteException thrown on connection errors + */ + int sendRequest(String code, String territory) throws RemoteException; - /** - * Fetches the answer for the given id. The answer will be null, if no answer is - * set. - * - * @param id the id for the fetched answer - * @return the answer for the id or null, if no answer is set - * @throws RemoteException thrown on connection errors - */ - public Answer getAnswer(int id) throws RemoteException; + /** + * Fetches the answer for the given id. The answer will be null, if no answer is + * set. + * + * @param id the id for the fetched answer + * @return the answer for the id or null, if no answer is set + * @throws RemoteException thrown on connection errors + */ + Answer getAnswer(int id) throws RemoteException; } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/Request.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/Request.java index a013a5c..2db54cb 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/Request.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/Request.java @@ -2,9 +2,8 @@ /** * Wrapper to hold a request instance. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public record Request(int id, String code, String territory) { diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/StudentController.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/StudentController.java index e2fa814..6823343 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/StudentController.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/StudentController.java @@ -1,102 +1,99 @@ package com.JayPi4c.RobbiSimulator.controller.tutor; -import java.io.ByteArrayInputStream; -import java.rmi.NotBoundException; -import java.rmi.RemoteException; -import java.rmi.registry.LocateRegistry; -import java.rmi.registry.Registry; - import com.JayPi4c.RobbiSimulator.controller.program.Program; import com.JayPi4c.RobbiSimulator.controller.program.ProgramController; import com.JayPi4c.RobbiSimulator.utils.AlertHelper; import com.JayPi4c.RobbiSimulator.utils.I18nUtils; import com.JayPi4c.RobbiSimulator.utils.PropertiesLoader; import com.JayPi4c.RobbiSimulator.view.MainStage; - import javafx.scene.control.Alert.AlertType; import lombok.extern.slf4j.Slf4j; +import java.io.ByteArrayInputStream; +import java.rmi.NotBoundException; +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; + /** * Controller to handle actions of a student. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Slf4j public class StudentController { - private int requestID; - - private MainStage stage; + // language keys + private static final String MENU_TUTOR_RECEIVEANSWER_INFORMATION = "Menu.tutor.receiveAnswer.information"; + private static final String MENU_TUTOR_RECEIVEANSWER_ERROR = "Menu.tutor.receiveAnswer.error"; + private static final String MENU_TUTOR_SEND_REQUEST_INFORMATION = "Menu.tutor.sendRequest.information"; + private static final String MENU_TUTOR_SENDREQUEST_ERROR = "Menu.tutor.sendRequest.error"; + private int requestID; + private final MainStage stage; - // language keys - private static final String MENU_TUTOR_RECEIVEANSWER_INFORMATION = "Menu.tutor.receiveAnswer.information"; - private static final String MENU_TUTOR_RECEIVEANSWER_ERROR = "Menu.tutor.receiveAnswer.error"; - private static final String MENU_TUTOR_SEND_REQUEST_INFORMATION = "Menu.tutor.sendRequest.information"; - private static final String MENU_TUTOR_SENDREQUEST_ERROR = "Menu.tutor.sendRequest.error"; + /** + * Constructor to create a new StudentController + * + * @param stage the stage, the controller is for + */ + public StudentController(MainStage stage) { + this.stage = stage; + stage.getMenubar().getSendRequestMenuItem().setOnAction(e -> sendRequest()); + stage.getMenubar().getReceiveAnswerMenuItem().setOnAction(e -> receiveAnswer()); + stage.getMenubar().getSendRequestMenuItem().setDisable(false); + stage.getMenubar().getReceiveAnswerMenuItem().setDisable(true); + } - /** - * Constructor to create a new StudentController - * - * @param stage the stage, the controller is for - */ - public StudentController(MainStage stage) { - this.stage = stage; - stage.getMenubar().getSendRequestMenuItem().setOnAction(e -> sendRequest()); - stage.getMenubar().getReceiveAnswerMenuItem().setOnAction(e -> receiveAnswer()); - stage.getMenubar().getSendRequestMenuItem().setDisable(false); - stage.getMenubar().getReceiveAnswerMenuItem().setDisable(true); - } + /** + * Helper to handle receiveAnswer actions + */ + private void receiveAnswer() { + try { + Registry registry = LocateRegistry.getRegistry(PropertiesLoader.getTutorhost(), + PropertiesLoader.getTutorport()); + ITutor tutor = (ITutor) registry.lookup(TutorController.TUTOR_CODE); + Answer answer = tutor.getAnswer(requestID); + if (answer == null) { + logger.debug("Answer is not ready yet!"); + stage.getNotificationController().showMessage(3000, MENU_TUTOR_RECEIVEANSWER_INFORMATION); + return; + } + stage.getProgram().setEditorContent(answer.code()); + stage.getProgram().save(); + ProgramController.compile(stage.getProgram(), false, stage); + stage.getTerritory().fromXML(new ByteArrayInputStream(answer.territory().getBytes())); + stage.getMenubar().getSendRequestMenuItem().setDisable(false); + stage.getMenubar().getReceiveAnswerMenuItem().setDisable(true); + } catch (RemoteException | NotBoundException e) { + logger.debug("Failed to fetch answer from tutor."); + AlertHelper.showAlertAndWait(AlertType.ERROR, I18nUtils.i18n(MENU_TUTOR_RECEIVEANSWER_ERROR), stage); + } + } - /** - * Helper to handle receiveAnswer actions - */ - private void receiveAnswer() { - try { - Registry registry = LocateRegistry.getRegistry(PropertiesLoader.getTutorhost(), - PropertiesLoader.getTutorport()); - ITutor tutor = (ITutor) registry.lookup(TutorController.TUTOR_CODE); - Answer answer = tutor.getAnswer(requestID); - if (answer == null) { - logger.debug("Answer is not ready yet!"); - stage.getSnackbarController().showMessage(MENU_TUTOR_RECEIVEANSWER_INFORMATION); - return; - } - stage.getProgram().setEditorContent(answer.code()); - stage.getProgram().save(); - ProgramController.compile(stage.getProgram(), false, stage); - stage.getTerritory().fromXML(new ByteArrayInputStream(answer.territory().getBytes())); - stage.getMenubar().getSendRequestMenuItem().setDisable(false); - stage.getMenubar().getReceiveAnswerMenuItem().setDisable(true); - } catch (RemoteException | NotBoundException e) { - logger.debug("Failed to fetch answer from tutor."); - AlertHelper.showAlertAndWait(AlertType.ERROR, I18nUtils.i18n(MENU_TUTOR_RECEIVEANSWER_ERROR), stage); - } - } + /** + * Helper to handle sendRequest actions + */ + private void sendRequest() { + try { + Registry registry = LocateRegistry.getRegistry(PropertiesLoader.getTutorhost(), + PropertiesLoader.getTutorport()); + ITutor tutor = (ITutor) registry.lookup(TutorController.TUTOR_CODE); + Program program = stage.getProgram(); + program.setEdited(true); + program.save(stage.getTextArea().getEditor().getDocument().getText()); + stage.getLanguageController().updateTitle(); + requestID = tutor.sendRequest(program.getEditorContent(), stage.getTerritory().toXML().toString()); + logger.debug("The request has ID {}.", requestID); + stage.getMenubar().getSendRequestMenuItem().setDisable(true); + stage.getMenubar().getReceiveAnswerMenuItem().setDisable(false); - /** - * Helper to handle sendRequest actions - */ - private void sendRequest() { - try { - Registry registry = LocateRegistry.getRegistry(PropertiesLoader.getTutorhost(), - PropertiesLoader.getTutorport()); - ITutor tutor = (ITutor) registry.lookup(TutorController.TUTOR_CODE); - Program program = stage.getProgram(); - program.setEdited(true); - program.save(stage.getTextArea().getEditor().getDocument().getText()); - stage.getLanguageController().updateTitle(); - requestID = tutor.sendRequest(program.getEditorContent(), stage.getTerritory().toXML().toString()); - logger.debug("The request has ID {}.", requestID); - stage.getMenubar().getSendRequestMenuItem().setDisable(true); - stage.getMenubar().getReceiveAnswerMenuItem().setDisable(false); + stage.getNotificationController().showMessage(3000, MENU_TUTOR_SEND_REQUEST_INFORMATION); - stage.getSnackbarController().showMessage(MENU_TUTOR_SEND_REQUEST_INFORMATION); + } catch (RemoteException | NotBoundException e) { + logger.debug("Failed to send request to tutor."); + AlertHelper.showAlertAndWait(AlertType.ERROR, I18nUtils.i18n(MENU_TUTOR_SENDREQUEST_ERROR), stage); + } + } - } catch (RemoteException | NotBoundException e) { - logger.debug("Failed to send request to tutor."); - AlertHelper.showAlertAndWait(AlertType.ERROR, I18nUtils.i18n(MENU_TUTOR_SENDREQUEST_ERROR), stage); - } - } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/Tutor.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/Tutor.java index 7b78137..6bd9c26 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/Tutor.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/Tutor.java @@ -1,83 +1,80 @@ package com.JayPi4c.RobbiSimulator.controller.tutor; -import java.rmi.RemoteException; -import java.rmi.server.UnicastRemoteObject; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.Optional; -import java.util.Queue; - import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; +import java.io.Serial; +import java.rmi.RemoteException; +import java.rmi.server.UnicastRemoteObject; +import java.util.*; + /** * ITutor implementation to handle the RMI invocations. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Slf4j @EqualsAndHashCode(callSuper = true) public class Tutor extends UnicastRemoteObject implements ITutor { - private static final long serialVersionUID = 4722167139215525516L; + @Serial + private static final long serialVersionUID = 4722167139215525516L; - private transient Queue requests; - /** - * ID of the latest request. Will increment after a request arrived. - */ - private int currentID = 0; - /** - * Map to hold answers accessible by their id - */ - private Map answers; + private transient final Queue requests; + /** + * ID of the latest request. Will increment after a request arrived. + */ + private int currentID = 0; + /** + * Map to hold answers accessible by their id + */ + private final Map answers; - /** - * Create a new tutor-instance - * - * @throws RemoteException thrown on connection errors - */ - public Tutor() throws RemoteException { - super(); - requests = new LinkedList<>(); - answers = new HashMap<>(); - } + /** + * Create a new tutor-instance + * + * @throws RemoteException thrown on connection errors + */ + public Tutor() throws RemoteException { + super(); + requests = new LinkedList<>(); + answers = new HashMap<>(); + } - @Override - public synchronized int sendRequest(String code, String territory) throws RemoteException { - requests.add(new Request(currentID, code, territory)); - logger.debug("Added new request with id {}.", currentID); - return currentID++; - } + @Override + public synchronized int sendRequest(String code, String territory) throws RemoteException { + requests.add(new Request(currentID, code, territory)); + logger.debug("Added new request with id {}.", currentID); + return currentID++; + } - @Override - public synchronized Answer getAnswer(int id) throws RemoteException { - if (answers.containsKey(id)) { - logger.debug("Returning answer with id {}.", id); - return answers.remove(id); - } - logger.debug("Could not find answer with id {}.", id); - return null; - } + @Override + public synchronized Answer getAnswer(int id) throws RemoteException { + if (answers.containsKey(id)) { + logger.debug("Returning answer with id {}.", id); + return answers.remove(id); + } + logger.debug("Could not find answer with id {}.", id); + return null; + } - /** - * Returns an optional with a new request if one is available. - * - * @return Optional of the oldest request or an empty Optional. - */ - public synchronized Optional getNewRequest() { - return Optional.ofNullable(requests.poll()); - } + /** + * Returns an optional with a new request if one is available. + * + * @return Optional of the oldest request or an empty Optional. + */ + public synchronized Optional getNewRequest() { + return Optional.ofNullable(requests.poll()); + } - /** - * Adds an answer with the corresponding id in the list of done answers. - * - * @param id the id for the answer - * @param answer the actual answer - */ - public synchronized void setAnswer(int id, Answer answer) { - answers.put(id, answer); - } + /** + * Adds an answer with the corresponding id in the list of done answers. + * + * @param id the id for the answer + * @param answer the actual answer + */ + public synchronized void setAnswer(int id, Answer answer) { + answers.put(id, answer); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/TutorController.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/TutorController.java index a5cbd3b..789f0de 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/TutorController.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/tutor/TutorController.java @@ -1,5 +1,10 @@ package com.JayPi4c.RobbiSimulator.controller.tutor; +import com.JayPi4c.RobbiSimulator.controller.program.ProgramController; +import com.JayPi4c.RobbiSimulator.utils.PropertiesLoader; +import com.JayPi4c.RobbiSimulator.view.MainStage; +import lombok.extern.slf4j.Slf4j; + import java.io.ByteArrayInputStream; import java.rmi.AlreadyBoundException; import java.rmi.NotBoundException; @@ -8,119 +13,107 @@ import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; -import com.JayPi4c.RobbiSimulator.controller.program.ProgramController; -import com.JayPi4c.RobbiSimulator.utils.PropertiesLoader; -import com.JayPi4c.RobbiSimulator.view.MainStage; - -import lombok.extern.slf4j.Slf4j; - /** * Controller to handle actions of a tutor. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Slf4j public class TutorController { - private static Tutor tutor; - private static Registry registry; - - private MainStage stage; - - private static final int NO_ID = -1; - private int currentID = NO_ID; - - /** - * Code to bind a tutor instance to the RMI instance. - */ - public static final String TUTOR_CODE = "Tutor"; - - // language keys - private static final String MENU_TUTOR_LOADREQUEST_SUCCESS = "Menu.tutor.loadRequest.success"; - private static final String MENU_TUTOR_LOADREQUEST_WARNING = "Menu.tutor.loadRequest.warning"; - private static final String MENU_TUTOR_SAVEANSWER_INFORMATION = "Menu.tutor.saveAnswer.information"; - - /** - * Constructor to create a new TutorController. - * - * @param mainStage the stage, the controller is for - */ - public TutorController(MainStage mainStage) { - this.stage = mainStage; - mainStage.getMenubar().getLoadRequestMenuItem().setOnAction(e -> loadRequest()); - mainStage.getMenubar().getSaveAnswerMenuItem().setOnAction(e -> saveAnswer()); - mainStage.getMenubar().getLoadRequestMenuItem().setDisable(false); - mainStage.getMenubar().getSaveAnswerMenuItem().setDisable(true); - } - - /** - * Helper to handle loadRequest actions - * - */ - private void loadRequest() { - tutor.getNewRequest().ifPresentOrElse(request -> { - logger.debug("Loading request with id {}.", request.id()); - stage.getProgram().setEditorContent(request.code()); - stage.getProgram().save(); - ProgramController.compile(stage.getProgram(), false, stage); - stage.getTerritory().fromXML(new ByteArrayInputStream(request.territory().getBytes())); - currentID = request.id(); - stage.getMenubar().getLoadRequestMenuItem().setDisable(true); - stage.getMenubar().getSaveAnswerMenuItem().setDisable(false); - stage.getSnackbarController().showMessage(MENU_TUTOR_LOADREQUEST_SUCCESS, request.id()); - }, () -> { - logger.debug("no request available"); - stage.getSnackbarController().showMessage(MENU_TUTOR_LOADREQUEST_WARNING); - }); - } - - /** - * Helper to handle save Answer actions - */ - private void saveAnswer() { - logger.debug("Saving answer for id {}.", currentID); - stage.getProgram().save(stage.getTextArea().getEditor().getDocument().getText()); - tutor.setAnswer(currentID, - new Answer(stage.getProgram().getEditorContent(), stage.getTerritory().toXML().toString())); - currentID = NO_ID; - stage.getMenubar().getLoadRequestMenuItem().setDisable(false); - stage.getMenubar().getSaveAnswerMenuItem().setDisable(true); - stage.getSnackbarController().showMessage(MENU_TUTOR_SAVEANSWER_INFORMATION); - } - - /** - * Creates a new RMI registry and gets ready to accept connections. - * - * @return true if the initialization finished successfully, false otherwise - */ - public static boolean initialize() { - try { - tutor = new Tutor(); - - registry = LocateRegistry.createRegistry(PropertiesLoader.getTutorport()); - - registry.bind(TUTOR_CODE, tutor); - } catch (RemoteException | AlreadyBoundException re) { - return false; - } - return true; - } - - /** - * Unbinds all previously binded instances. - * - * @return true if the unbinding was successful, false otherwise - */ - public static boolean shutdown() { - try { - registry.unbind(TUTOR_CODE); - UnicastRemoteObject.unexportObject(tutor, true); - UnicastRemoteObject.unexportObject(registry, true); - } catch (RemoteException | NotBoundException e) { - return false; - } - return true; - } + /** + * Code to bind a tutor instance to the RMI instance. + */ + public static final String TUTOR_CODE = "Tutor"; + private static final int NO_ID = -1; + // language keys + private static final String MENU_TUTOR_LOADREQUEST_SUCCESS = "Menu.tutor.loadRequest.success"; + private static final String MENU_TUTOR_LOADREQUEST_WARNING = "Menu.tutor.loadRequest.warning"; + private static final String MENU_TUTOR_SAVEANSWER_INFORMATION = "Menu.tutor.saveAnswer.information"; + private static Tutor tutor; + private static Registry registry; + private final MainStage stage; + private int currentID = NO_ID; + + /** + * Constructor to create a new TutorController. + * + * @param mainStage the stage, the controller is for + */ + public TutorController(MainStage mainStage) { + this.stage = mainStage; + mainStage.getMenubar().getLoadRequestMenuItem().setOnAction(e -> loadRequest()); + mainStage.getMenubar().getSaveAnswerMenuItem().setOnAction(e -> saveAnswer()); + mainStage.getMenubar().getLoadRequestMenuItem().setDisable(false); + mainStage.getMenubar().getSaveAnswerMenuItem().setDisable(true); + } + + /** + * Creates a new RMI registry and gets ready to accept connections. + * + * @return true if the initialization finished successfully, false otherwise + */ + public static boolean initialize() { + try { + tutor = new Tutor(); + + registry = LocateRegistry.createRegistry(PropertiesLoader.getTutorport()); + + registry.bind(TUTOR_CODE, tutor); + } catch (RemoteException | AlreadyBoundException re) { + return false; + } + return true; + } + + /** + * Unbinds all previously bound instances. + * + * @return true if the unbinding was successful, false otherwise + */ + public static boolean shutdown() { + try { + registry.unbind(TUTOR_CODE); + UnicastRemoteObject.unexportObject(tutor, true); + UnicastRemoteObject.unexportObject(registry, true); + } catch (RemoteException | NotBoundException e) { + return false; + } + return true; + } + + /** + * Helper to handle loadRequest actions + */ + private void loadRequest() { + tutor.getNewRequest().ifPresentOrElse(request -> { + logger.debug("Loading request with id {}.", request.id()); + stage.getProgram().setEditorContent(request.code()); + stage.getProgram().save(); + ProgramController.compile(stage.getProgram(), false, stage); + stage.getTerritory().fromXML(new ByteArrayInputStream(request.territory().getBytes())); + currentID = request.id(); + stage.getMenubar().getLoadRequestMenuItem().setDisable(true); + stage.getMenubar().getSaveAnswerMenuItem().setDisable(false); + stage.getNotificationController().showMessage(3000, MENU_TUTOR_LOADREQUEST_SUCCESS, request.id()); + }, () -> { + logger.debug("no request available"); + stage.getNotificationController().showMessage(3000, MENU_TUTOR_LOADREQUEST_WARNING); + }); + } + + /** + * Helper to handle save Answer actions + */ + private void saveAnswer() { + logger.debug("Saving answer for id {}.", currentID); + stage.getProgram().save(stage.getTextArea().getEditor().getDocument().getText()); + tutor.setAnswer(currentID, + new Answer(stage.getProgram().getEditorContent(), stage.getTerritory().toXML().toString())); + currentID = NO_ID; + stage.getMenubar().getLoadRequestMenuItem().setDisable(false); + stage.getMenubar().getSaveAnswerMenuItem().setDisable(true); + stage.getNotificationController().showMessage(3000, MENU_TUTOR_SAVEANSWER_INFORMATION); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/Accu.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/Accu.java index 31448bc..b1e6d48 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/Accu.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/Accu.java @@ -2,15 +2,17 @@ import jakarta.xml.bind.annotation.XmlRootElement; +import java.io.Serial; + /** * Class representing the accu-item. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @XmlRootElement public class Accu implements Item { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/BagIsEmptyException.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/BagIsEmptyException.java index 2166e32..482bac1 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/BagIsEmptyException.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/BagIsEmptyException.java @@ -1,21 +1,23 @@ package com.JayPi4c.RobbiSimulator.model; +import java.io.Serial; + /** * Exception to be thrown if an item is placed down while there is no item in * the bag. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public class BagIsEmptyException extends RobbiException { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * Constructor for a new BagIsEmptyException with a localized message. - */ - public BagIsEmptyException() { - super("Exception.BagIsEmpty"); - } + /** + * Constructor for a new BagIsEmptyException with a localized message. + */ + public BagIsEmptyException() { + super("Exception.BagIsEmpty"); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/BagIsFullException.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/BagIsFullException.java index bd7d16b..96e1a4c 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/BagIsFullException.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/BagIsFullException.java @@ -1,20 +1,22 @@ package com.JayPi4c.RobbiSimulator.model; +import java.io.Serial; + /** * Exception to be thrown if an item is picked up while the bag is already full. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public class BagIsFullException extends RobbiException { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * Constructor for a new BagIsFullException with a localized message. - */ - public BagIsFullException() { - super("Exception.BagIsFull"); - } + /** + * Constructor for a new BagIsFullException with a localized message. + */ + public BagIsFullException() { + super("Exception.BagIsFull"); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/DIRECTION.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/DIRECTION.java index de3451c..26bd2c9 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/DIRECTION.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/DIRECTION.java @@ -1,48 +1,45 @@ package com.JayPi4c.RobbiSimulator.model; /** - * Enumeration to hold all cardinal-points to store robbis current facing. - * - * @author Jonas Pohl + * Enumeration to hold all cardinal-points to store robbi's current facing. * + * @author Jonas Pohl */ public enum DIRECTION { - /** - * Direction if robbi is looking up. - */ - NORTH, - /** - * Direction if robbi is looking left. - */ - WEST, - /** - * Direction if robbi is looking down. - */ - SOUTH, - /** - * Direction if robbi is looking right. - */ - EAST; - - private static DIRECTION[] vals = values(); + /** + * Direction if robbi is looking up. + */ + NORTH, + /** + * Direction if robbi is looking left. + */ + WEST, + /** + * Direction if robbi is looking down. + */ + SOUTH, + /** + * Direction if robbi is looking right. + */ + EAST; - /** - * moves through the direction counter-clockwise - * - * @return the next direction after a counter-clockwise 90 degrees turn. - */ - public DIRECTION next() { - return vals[(this.ordinal() + 1) % vals.length]; - } + /** + * moves through the direction counter-clockwise + * + * @return the next direction after a counter-clockwise 90 degrees turn. + */ + public DIRECTION next() { + return values()[(this.ordinal() + 1) % values().length]; + } - /** - * Moves through the direction clockwise. - * - * @return the next direction after a clockwise 90 degrees turn. - */ - public DIRECTION previous() { - return vals[(this.ordinal() + vals.length - 1) % vals.length]; - } + /** + * Moves through the direction clockwise. + * + * @return the next direction after a clockwise 90 degrees turn. + */ + public DIRECTION previous() { + return values()[(this.ordinal() + values().length - 1) % values().length]; + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/Dimension.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/Dimension.java index 6b22cb8..4a320eb 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/Dimension.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/Dimension.java @@ -2,9 +2,8 @@ /** * Record to hold information about the new dimension of the simulation gird. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public record Dimension(int cols, int rows) { diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/Hollow.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/Hollow.java index 8234253..e638e89 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/Hollow.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/Hollow.java @@ -2,38 +2,40 @@ import jakarta.xml.bind.annotation.XmlRootElement; +import java.io.Serial; + /** * Class representing a Hollow tile. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @XmlRootElement public class Hollow extends Tile { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * Getter for the item placed on the tile. - * - * @return the item placed on this tile. No item can be placed here, therefore - * the item returned is null - */ - @Override - public Item getItem() { - return null; - } + /** + * Getter for the item placed on the tile. + * + * @return the item placed on this tile. No item can be placed here, therefore + * the item returned is null + */ + @Override + public Item getItem() { + return null; + } - /** - * Setter to place an item in the hollow. Since the gamelogic forbids this - * action, the item given to the hollow will not be saved and dropped instead. - * - * @param item item to be placed in the hollow. - */ - @Override - public void setItem(Item item) { - // Robbi can't reach a hollow tile. Items will be dropped if it is placed into a - // hollow - } + /** + * Setter to place an item in the hollow. Since the game logic forbids this + * action, the item given to the hollow will not be saved and dropped instead. + * + * @param item item to be placed in the hollow. + */ + @Override + public void setItem(Item item) { + // Robbi can't reach a hollow tile. Items will be dropped if it is placed into a + // hollow + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/HollowAheadException.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/HollowAheadException.java index 0b37fb4..c9e3276 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/HollowAheadException.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/HollowAheadException.java @@ -1,19 +1,21 @@ package com.JayPi4c.RobbiSimulator.model; +import java.io.Serial; + /** - * Exception to be thrown if robbi tries to move into a hollow- - * - * @author Jonas Pohl + * Exception to be thrown if robbi tries to move into a hollow. * + * @author Jonas Pohl */ public class HollowAheadException extends RobbiException { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * Constructor for a new HollowAheadException with a localized message. - */ - public HollowAheadException() { - super("Exception.HollowAhead"); - } + /** + * Constructor for a new HollowAheadException with a localized message. + */ + public HollowAheadException() { + super("Exception.HollowAhead"); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/InvalidTerritoryException.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/InvalidTerritoryException.java index 1a3814e..b921764 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/InvalidTerritoryException.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/InvalidTerritoryException.java @@ -1,20 +1,22 @@ package com.JayPi4c.RobbiSimulator.model; +import java.io.Serial; + /** * Exception to be thrown if the user tries to load an invalid territory. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public class InvalidTerritoryException extends Exception { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * Constructor for a new InvalidTerritoryException with a message. - */ - public InvalidTerritoryException() { - super("The loaded territory is invalid."); - } + /** + * Constructor for a new InvalidTerritoryException with a message. + */ + public InvalidTerritoryException() { + super("The loaded territory is invalid."); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/Item.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/Item.java index e564244..91167e0 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/Item.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/Item.java @@ -4,9 +4,8 @@ /** * Interface for all item classes. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public interface Item extends Serializable { diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/NoItemException.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/NoItemException.java index 2770ef5..cfcb742 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/NoItemException.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/NoItemException.java @@ -1,21 +1,23 @@ package com.JayPi4c.RobbiSimulator.model; +import java.io.Serial; + /** * Exception to be thrown if robbi tries to pick up an item from a tile, which - * has no item provied. - * - * @author Jonas Pohl + * has no item provided. * + * @author Jonas Pohl */ public class NoItemException extends RobbiException { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * Constructor for a new NoItemException with a localized message. - */ - public NoItemException() { - super("Exception.NoItem"); - } + /** + * Constructor for a new NoItemException with a localized message. + */ + public NoItemException() { + super("Exception.NoItem"); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/NoPileOfScrapAheadException.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/NoPileOfScrapAheadException.java index e62ca3f..3a805cc 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/NoPileOfScrapAheadException.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/NoPileOfScrapAheadException.java @@ -1,21 +1,23 @@ package com.JayPi4c.RobbiSimulator.model; +import java.io.Serial; + /** * Exception to be thrown if robbi attempts to push a pile of scrap while there * is no pile of scrap ahead of robbi. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public class NoPileOfScrapAheadException extends RobbiException { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * Constructor for a new NoPileOfScrapAheadException with localized message. - */ - public NoPileOfScrapAheadException() { - super("Exception.NoPileOfScrapAhead"); - } + /** + * Constructor for a new NoPileOfScrapAheadException with localized message. + */ + public NoPileOfScrapAheadException() { + super("Exception.NoPileOfScrapAhead"); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/Nut.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/Nut.java index 13b1afe..1dfb0db 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/Nut.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/Nut.java @@ -2,15 +2,17 @@ import jakarta.xml.bind.annotation.XmlRootElement; +import java.io.Serial; + /** * Class representing the nut-item. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @XmlRootElement public class Nut implements Item { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/PileOfScrap.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/PileOfScrap.java index 3022a4f..266d294 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/PileOfScrap.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/PileOfScrap.java @@ -2,37 +2,39 @@ import jakarta.xml.bind.annotation.XmlRootElement; +import java.io.Serial; + /** * Class representing a PileOfScrap Tile. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @XmlRootElement public class PileOfScrap extends Tile { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * Setter to place an item on a PileOfScrap. Since the item placed on a pile of - * scrap, the item just vanishes. - * - * @param item the item to vanish - */ - @Override - public void setItem(Item item) { - // Item vanishes - } + /** + * Setter to place an item on a PileOfScrap. Since the item placed on a pile of + * scrap, the item just vanishes. + * + * @param item the item to vanish + */ + @Override + public void setItem(Item item) { + // Item vanishes + } - /** - * Getter for the item placed on the tile. - * - * @return the item placed on this tile. All items placed on a pile of scrap - * vanish, therefore the item returned will be null. - */ - @Override - public Item getItem() { - return null; - } + /** + * Getter for the item placed on the tile. + * + * @return the item placed on this tile. All items placed on a pile of scrap + * vanish, therefore the item returned will be null. + */ + @Override + public Item getItem() { + return null; + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/Robbi.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/Robbi.java index d7b13c8..eaa8666 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/Robbi.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/Robbi.java @@ -1,324 +1,319 @@ package com.JayPi4c.RobbiSimulator.model; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.Synchronized; +import lombok.*; import lombok.extern.slf4j.Slf4j; /** - * * This is the actor class of the simulator. All public functions in this class - * will be available in the simulators editor. - * + * will be available in the simulator's editor. + * * @author Jonas Pohl */ @Slf4j @NoArgsConstructor public class Robbi { - /** - * Attribute to store the territory in which robbi is living - */ - @Setter(value = AccessLevel.PACKAGE) - private Territory territory; - /** - * Attribute to store robbis x position in the territory - */ - @Getter(value = AccessLevel.PACKAGE) - private volatile int x; - /** - * Attribute to store robbis y position in the territory - */ - @Getter(value = AccessLevel.PACKAGE) - private volatile int y; - /** - * Attribute to store robbis item - */ - @Getter(value = AccessLevel.PACKAGE, onMethod_ = { @Synchronized("territory") }) - @Setter(value = AccessLevel.PACKAGE, onMethod_ = { @Synchronized("territory") }) - private Item item = null; + /** + * Attribute to store the territory in which robbi is living + */ + @Setter(value = AccessLevel.PACKAGE) + private Territory territory; + /** + * Attribute to store robbi's x position in the territory + */ + @Getter(value = AccessLevel.PACKAGE) + private volatile int x; + /** + * Attribute to store robbi's y position in the territory + */ + @Getter(value = AccessLevel.PACKAGE) + private volatile int y; + /** + * Attribute to store robbi's item + */ + @Getter(value = AccessLevel.PACKAGE, onMethod_ = {@Synchronized("territory")}) + @Setter(value = AccessLevel.PACKAGE, onMethod_ = {@Synchronized("territory")}) + private Item item = null; - /** - * Attribute to store robbis facing - */ - @Getter(value = AccessLevel.PACKAGE) - private volatile DIRECTION facing; + /** + * Attribute to store robbi's facing + */ + @Getter(value = AccessLevel.PACKAGE) + private volatile DIRECTION facing; - /** - * The main-Method which will be overwritten by every custom Robbi - * implementation. When starting a simulation, this method will be called. - */ - void main() { - // will be overritten - logger.error("Please overrite the main-method"); - } + /** + * The main-Method which will be overwritten by every custom Robbi + * implementation. When starting a simulation, this method will be called. + */ + void main() { + // will be overwritten + logger.error("Please overwrite the main-method"); + } - /** - * Constructor to create a new robbi with the given territory. - * - * @param t the territory in which robbi is placed. - */ - Robbi(Territory t) { - this.territory = t; - this.x = 0; - this.y = 0; - this.facing = DIRECTION.EAST; - } + /** + * Constructor to create a new robbi with the given territory. + * + * @param t the territory in which robbi is placed. + */ + Robbi(Territory t) { + this.territory = t; + this.x = 0; + this.y = 0; + this.facing = DIRECTION.EAST; + } - // ============= HELPER ================ + // ============= HELPER ================ - /** - * Sets the position of robbi. - * - * @param x new x position - * @param y new y position - */ - void setPosition(int x, int y) { - synchronized (territory) { - if (x < 0 || y < 0 || x >= territory.getNumCols() || y >= territory.getNumRows()) - throw new IllegalArgumentException("Robbi kann nicht außerhalb des Territoriums platziert werden."); - if (territory.getTile(x, y) instanceof Hollow) - throw new TileBlockedException(); - this.x = x; - this.y = y; - } - } + /** + * Sets the position of robbi. + * + * @param x new x position + * @param y new y position + */ + void setPosition(int x, int y) { + synchronized (territory) { + if (x < 0 || y < 0 || x >= territory.getNumCols() || y >= territory.getNumRows()) + throw new IllegalArgumentException("Robbi kann nicht außerhalb des Territoriums platziert werden."); + if (territory.getTile(x, y) instanceof Hollow) + throw new TileBlockedException(); + this.x = x; + this.y = y; + } + } - /** - * Updates the facing of robbi to the given facing. - * - * @param facing robbis new facing - */ - void setFacing(DIRECTION facing) { - synchronized (territory) { - this.facing = facing; - } - } + /** + * Updates the facing of robbi to the given facing. + * + * @param facing robbi's new facing + */ + void setFacing(DIRECTION facing) { + synchronized (territory) { + this.facing = facing; + } + } - // ==================== PUBLIC FUNCTIONS ========== + // ==================== PUBLIC FUNCTIONS ========== - /** - * If possible move Robbi one tile towards the direction it is facing. - */ - public final void vor() { - synchronized (territory) { - Tile t; - switch (facing) { - case NORTH: - t = territory.getTile(x, y - 1); - if (t instanceof Hollow) - throw new HollowAheadException(); - else { - y = y - 1; - y += territory.getNumRows(); - y %= territory.getNumRows(); - } - break; - case SOUTH: - t = territory.getTile(x, y + 1); - if (t instanceof Hollow) - throw new HollowAheadException(); - else { - y = y + 1; - y += territory.getNumRows(); - y %= territory.getNumRows(); - } - break; - case EAST: - t = territory.getTile(x + 1, y); - if (t instanceof Hollow) - throw new HollowAheadException(); - else { - x = x + 1; - x += territory.getNumCols(); - x %= territory.getNumCols(); - } - break; - case WEST: - t = territory.getTile(x - 1, y); - if (t instanceof Hollow) - throw new HollowAheadException(); - else { - x = x - 1; - x += territory.getNumCols(); - x %= territory.getNumCols(); - } - break; - default: - break; - } - } - territory.setChanged(); - territory.notifyAllObservers(); - } + /** + * If possible move Robbi one tile towards the direction it is facing. + */ + public final void vor() { + synchronized (territory) { + Tile t; + switch (facing) { + case NORTH: + t = territory.getTile(x, y - 1); + if (t instanceof Hollow) + throw new HollowAheadException(); + else { + y = y - 1; + y += territory.getNumRows(); + y %= territory.getNumRows(); + } + break; + case SOUTH: + t = territory.getTile(x, y + 1); + if (t instanceof Hollow) + throw new HollowAheadException(); + else { + y = y + 1; + y += territory.getNumRows(); + y %= territory.getNumRows(); + } + break; + case EAST: + t = territory.getTile(x + 1, y); + if (t instanceof Hollow) + throw new HollowAheadException(); + else { + x = x + 1; + x += territory.getNumCols(); + x %= territory.getNumCols(); + } + break; + case WEST: + t = territory.getTile(x - 1, y); + if (t instanceof Hollow) + throw new HollowAheadException(); + else { + x = x - 1; + x += territory.getNumCols(); + x %= territory.getNumCols(); + } + break; + default: + break; + } + } + territory.setChanged(); + territory.notifyAllObservers(); + } - /** - * Turn Robbi counterclockwise. - */ - public final void linksUm() { - synchronized (territory) { - facing = facing.next(); - } - territory.setChanged(); - territory.notifyAllObservers(); - } + /** + * Turn Robbi counterclockwise. + */ + public final void linksUm() { + synchronized (territory) { + facing = facing.next(); + } + territory.setChanged(); + territory.notifyAllObservers(); + } - /** - * If possible drop the item that is stored in the bag. - */ - public final void legeAb() { - synchronized (territory) { - if (item == null) - throw new BagIsEmptyException(); - if (territory.placeItem(item, x, y)) - item = null; - else - throw new TileIsFullException(); - } - territory.setChanged(); - territory.notifyAllObservers(); - } + /** + * If possible drop the item that is stored in the bag. + */ + public final void legeAb() { + synchronized (territory) { + if (item == null) + throw new BagIsEmptyException(); + if (territory.placeItem(item, x, y)) + item = null; + else + throw new TileIsFullException(); + } + territory.setChanged(); + territory.notifyAllObservers(); + } - /** - * if possible take the item that occupies the tile Robbi is on. - */ - public final void nehmeAuf() { - synchronized (territory) { - Item i = territory.getItem(x, y); - if (i == null) - throw new NoItemException(); - if (this.item != null) { - throw new BagIsFullException(); - } - this.item = territory.removeItem(x, y); - } - territory.setChanged(); - territory.notifyAllObservers(); - } + /** + * if possible take the item that occupies the tile Robbi is on. + */ + public final void nehmeAuf() { + synchronized (territory) { + Item i = territory.getItem(x, y); + if (i == null) + throw new NoItemException(); + if (this.item != null) { + throw new BagIsFullException(); + } + this.item = territory.removeItem(x, y); + } + territory.setChanged(); + territory.notifyAllObservers(); + } - /** - * If a pile of scrap is ahead of Robbi and afterwards a nonblocking tile, Robbi - * pushes the pile of scrap one tile the direction he is facing. - */ - public final void schiebeSchrotthaufen() { - synchronized (territory) { - territory.deactivateNotification(); - if (!vornSchrotthaufen()) { - territory.activateNotification(); - throw new NoPileOfScrapAheadException(); - } - int dx = switch (facing) { - case EAST -> x + 2; - case WEST -> x - 2; - default -> x; - }; - int dy = switch (facing) { - case NORTH -> y - 2; - case SOUTH -> y + 2; - default -> y; - }; + /** + * If a pile of scrap is ahead of Robbi and afterward a nonblocking tile, Robbi + * pushes the pile of scrap one tile the direction he is facing. + */ + public final void schiebeSchrotthaufen() { + synchronized (territory) { + territory.deactivateNotification(); + if (!vornSchrotthaufen()) { + territory.activateNotification(); + throw new NoPileOfScrapAheadException(); + } + int dx = switch (facing) { + case EAST -> x + 2; + case WEST -> x - 2; + default -> x; + }; + int dy = switch (facing) { + case NORTH -> y - 2; + case SOUTH -> y + 2; + default -> y; + }; - Tile t = territory.getTile(dx, dy); - if (t instanceof Stockpile || t instanceof PileOfScrap) { - territory.activateNotification(); - throw new TileBlockedException(); - } else { - if (territory.getTile(dx, dy) instanceof Hollow) - territory.clearTile(dx, dy); - else - territory.placePileOfScrap(dx, dy); - int px = switch (facing) { - case EAST -> x + 1; - case WEST -> x - 1; - default -> x; - }; - int py = switch (facing) { - case NORTH -> y - 1; - case SOUTH -> y + 1; - default -> y; - }; - territory.clearTile(px, py); - } - vor(); - } - territory.activateNotification(); - } + Tile t = territory.getTile(dx, dy); + if (t instanceof Stockpile || t instanceof PileOfScrap) { + territory.activateNotification(); + throw new TileBlockedException(); + } else { + if (territory.getTile(dx, dy) instanceof Hollow) + territory.clearTile(dx, dy); + else + territory.placePileOfScrap(dx, dy); + int px = switch (facing) { + case EAST -> x + 1; + case WEST -> x - 1; + default -> x; + }; + int py = switch (facing) { + case NORTH -> y - 1; + case SOUTH -> y + 1; + default -> y; + }; + territory.clearTile(px, py); + } + vor(); + } + territory.activateNotification(); + } - /** - * checks if an Item is on the tile Robbi is on. - * - * @return true if an item is on robbis tile, false otherwise - */ - public final boolean gegenstandDa() { - synchronized (territory) { - return territory.getItem(x, y) != null; - } - } + /** + * checks if an Item is on the tile Robbi is on. + * + * @return true if an item is on robbi's tile, false otherwise + */ + public final boolean gegenstandDa() { + synchronized (territory) { + return territory.getItem(x, y) != null; + } + } - /** - * Checks if the tile on which Robbi stands is a stockpile - * - * @return true if the current tile is an instanceof Stockpile, false otherwise - */ - public final boolean istLagerplatz() { - synchronized (territory) { - return territory.getTile(x, y) instanceof Stockpile; - } - } + /** + * Checks if the tile on which Robbi stands is a stockpile + * + * @return true if the current tile is an instanceof Stockpile, false otherwise + */ + public final boolean istLagerplatz() { + synchronized (territory) { + return territory.getTile(x, y) instanceof Stockpile; + } + } - /** - * checks if a hollow is ahead of Robbi. - * - * @return true if an hollow is ahead, false otherwise - */ - public final boolean vornKuhle() { - synchronized (territory) { - int dx = switch (facing) { - case EAST -> x + 1; - case WEST -> x - 1; - default -> x; - }; - int dy = switch (facing) { - case NORTH -> y - 1; - case SOUTH -> y + 1; - default -> y; - }; - return territory.getTile(dx, dy) instanceof Hollow; - } - } + /** + * checks if a hollow is ahead of Robbi. + * + * @return true if a hollow is ahead, false otherwise + */ + public final boolean vornKuhle() { + synchronized (territory) { + int dx = switch (facing) { + case EAST -> x + 1; + case WEST -> x - 1; + default -> x; + }; + int dy = switch (facing) { + case NORTH -> y - 1; + case SOUTH -> y + 1; + default -> y; + }; + return territory.getTile(dx, dy) instanceof Hollow; + } + } - /** - * checks if a pile of scrap is ahead of Robbi. - * - * @return true if a pile of scrap is ahead of robbi, false otherwise - */ - public final boolean vornSchrotthaufen() { - synchronized (territory) { - int dx = switch (facing) { - case EAST -> x + 1; - case WEST -> x - 1; - default -> x; - }; - int dy = switch (facing) { - case NORTH -> y - 1; - case SOUTH -> y + 1; - default -> y; - }; - return territory.getTile(dx, dy) instanceof PileOfScrap; - } - } + /** + * checks if a pile of scrap is ahead of Robbi. + * + * @return true if a pile of scrap is ahead of robbi, false otherwise + */ + public final boolean vornSchrotthaufen() { + synchronized (territory) { + int dx = switch (facing) { + case EAST -> x + 1; + case WEST -> x - 1; + default -> x; + }; + int dy = switch (facing) { + case NORTH -> y - 1; + case SOUTH -> y + 1; + default -> y; + }; + return territory.getTile(dx, dy) instanceof PileOfScrap; + } + } - /** - * checks if an item is in Robbis bag. - * - * @return true if an item is in the bag, false otherwise - */ - public final boolean istTascheVoll() { - synchronized (territory) { - return item != null; - } - } + /** + * checks if an item is in Robbi's bag. + * + * @return true if an item is in the bag, false otherwise + */ + public final boolean istTascheVoll() { + synchronized (territory) { + return item != null; + } + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/RobbiException.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/RobbiException.java index 7cf5bac..5a81d04 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/RobbiException.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/RobbiException.java @@ -2,29 +2,31 @@ import com.JayPi4c.RobbiSimulator.utils.I18nUtils; +import java.io.Serial; + /** * Abstract Exception class for almost all exceptions thrown in this * application. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public abstract class RobbiException extends RuntimeException { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * Constructor for a new RobbiException with the given message key. - * - * @param key a key for a message to provide further details of the exception - */ - protected RobbiException(String key) { - super(key); - } + /** + * Constructor for a new RobbiException with the given message key. + * + * @param key a key for a message to provide further details of the exception + */ + protected RobbiException(String key) { + super(key); + } - @Override - public String getLocalizedMessage() { - return I18nUtils.i18n(getMessage()); - } + @Override + public String getLocalizedMessage() { + return I18nUtils.i18n(getMessage()); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/RobbiState.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/RobbiState.java index 5685ebf..cfa7c77 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/RobbiState.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/RobbiState.java @@ -11,9 +11,8 @@ /** * Robbi State to store all state relevant attributes in order to apply the * Memento-Pattern. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @XmlRootElement @Getter @@ -21,16 +20,16 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class RobbiState { - @XmlElement - private int x; + @XmlElement + private int x; - @XmlElement - private int y; + @XmlElement + private int y; - @XmlElement - private DIRECTION facing; + @XmlElement + private DIRECTION facing; - @XmlAnyElement - private Item item; + @XmlAnyElement + private Item item; } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/Screw.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/Screw.java index 77f3e92..31cb71c 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/Screw.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/Screw.java @@ -2,15 +2,17 @@ import jakarta.xml.bind.annotation.XmlRootElement; +import java.io.Serial; + /** * Class representing the screw-item. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @XmlRootElement public class Screw implements Item { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/Stockpile.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/Stockpile.java index d35a58c..3b525d5 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/Stockpile.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/Stockpile.java @@ -1,79 +1,80 @@ package com.JayPi4c.RobbiSimulator.model; -import java.util.ArrayList; -import java.util.List; - import jakarta.xml.bind.annotation.XmlAnyElement; import jakarta.xml.bind.annotation.XmlElementWrapper; import jakarta.xml.bind.annotation.XmlRootElement; +import java.io.Serial; +import java.util.ArrayList; +import java.util.List; + /** * Class representing a Stockpile tile. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @XmlRootElement public class Stockpile extends Tile { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * Attribute to store all items placed on the tile - */ - @XmlElementWrapper - @XmlAnyElement(lax = true) - private ArrayList items; - // TODO Queue more efficient? - // check if queue would be better, allowing an easier iteration over the - // elements. + /** + * Attribute to store all items placed on the tile + */ + @XmlElementWrapper + @XmlAnyElement(lax = true) + private final List items; + // TODO Queue more efficient? + // check if queue would be better, allowing an easier iteration over the + // elements. - /** - * Constructor to create a new Stockpile and initialize the list of items on the - * stockpile. - */ - public Stockpile() { - items = new ArrayList<>(); - } + /** + * Constructor to create a new Stockpile and initialize the list of items on the + * stockpile. + */ + public Stockpile() { + items = new ArrayList<>(); + } - /** - * Adds the given item to the list of items on the stockpile. - * - * @param item the item to add to the stockpile - */ - @Override - public void setItem(Item item) { - items.add(item); - } + /** + * Adds the given item to the list of items on the stockpile. + * + * @param item the item to add to the stockpile + */ + @Override + public void setItem(Item item) { + items.add(item); + } - /** - * Iterates through the list of items in the stockpile and returns one. - * - * @return the last item added to the stockpile, null if no item is placed on - * the stockpile - */ - @Override - public Item getItem() { - return (items.isEmpty()) ? null : items.get(items.size() - 1); - } + /** + * Iterates through the list of items in the stockpile and returns one. + * + * @return the last item added to the stockpile, null if no item is placed on + * the stockpile + */ + @Override + public Item getItem() { + return (items.isEmpty()) ? null : items.getLast(); + } - /** - * Removes and returns the last item from the stockpile. - * - * @return the last item added to the stockpile, null if no item was added yet - */ - @Override - public Item pickItem() { - return items.remove(items.size() - 1); - } + /** + * Removes and returns the last item from the stockpile. + * + * @return the last item added to the stockpile, null if no item was added yet + */ + @Override + public Item pickItem() { + return items.removeLast(); + } - /** - * Returns the full list of items placed on the stockpile. - * - * @return the arrayList containing all items on this stockpile - */ - public List getAllItems() { - return items; - } + /** + * Returns the full list of items placed on the stockpile. + * + * @return the arrayList containing all items on this stockpile + */ + public List getAllItems() { + return items; + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/Territory.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/Territory.java index 01b9494..58625ff 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/Territory.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/Territory.java @@ -1,26 +1,13 @@ package com.JayPi4c.RobbiSimulator.model; -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Serializable; -import java.util.Optional; - -import javax.xml.XMLConstants; -import javax.xml.stream.FactoryConfigurationError; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLOutputFactory; -import javax.xml.stream.XMLStreamConstants; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; -import javax.xml.stream.XMLStreamWriter; - import com.JayPi4c.RobbiSimulator.utils.Observable; - import lombok.extern.slf4j.Slf4j; +import javax.xml.XMLConstants; +import javax.xml.stream.*; +import java.io.*; +import java.util.Optional; + /** * This class contains all datastructures and utility functions to control the * territory. @@ -30,6 +17,7 @@ @Slf4j public class Territory extends Observable implements Serializable { + @Serial private static final long serialVersionUID = 1L; private transient Robbi robbi; @@ -175,16 +163,16 @@ public synchronized Robbi getRobbi() { } /** - * Getter for robbis current direction. + * Getter for robbi's current direction. * - * @return robbis current direction + * @return robbi's current direction */ public synchronized DIRECTION getRobbiDirection() { return robbi.getFacing(); } /** - * Getter for robbis current item. + * Getter for robbi's current item. * * @return the item robbi is currently holding. null, if robbi does not have * one. @@ -200,7 +188,7 @@ public synchronized Item getRobbiItem() { * @param bound the bound to limit i to * @return the normalized value of i */ - private int normalizeCoord(int i, int bound) { + private int normalizeCoordinate(int i, int bound) { i %= bound; i += bound; i %= bound; @@ -229,7 +217,7 @@ public synchronized boolean robbiOnTile(int col, int row) { */ public boolean placeItem(Item item, int x, int y) { synchronized (this) { - Tile t = tiles[normalizeCoord(x, numberOfColumns)][normalizeCoord(y, numberOfRows)]; + Tile t = tiles[normalizeCoordinate(x, numberOfColumns)][normalizeCoordinate(y, numberOfRows)]; if (t.getItem() != null && !(t instanceof Stockpile)) return false; t.setItem(item); @@ -247,7 +235,7 @@ public boolean placeItem(Item item, int x, int y) { * @return the item, which is placed at the given position */ public synchronized Item getItem(int x, int y) { - Tile t = tiles[normalizeCoord(x, numberOfColumns)][normalizeCoord(y, numberOfRows)]; + Tile t = tiles[normalizeCoordinate(x, numberOfColumns)][normalizeCoordinate(y, numberOfRows)]; return t.getItem(); } @@ -259,9 +247,9 @@ public synchronized Item getItem(int x, int y) { * @return the item that has been removed from the tile */ public Item removeItem(int x, int y) { - Item i = null; + Item i; synchronized (this) { - Tile t = tiles[normalizeCoord(x, numberOfColumns)][normalizeCoord(y, numberOfRows)]; + Tile t = tiles[normalizeCoordinate(x, numberOfColumns)][normalizeCoordinate(y, numberOfRows)]; i = t.pickItem(); } setChanged(); @@ -277,9 +265,9 @@ public Item removeItem(int x, int y) { * * @param territory the new territory * @param item the item robbi has in his bag - * @param x robbis x position - * @param y robbis y positions - * @param facing robbis facing + * @param x robbi's x position + * @param y robbi's y positions + * @param facing robbi's facing * @throws InvalidTerritoryException if it is impossible to create the territory * with the given information */ @@ -355,8 +343,8 @@ public void changeSize(int newCols, int newRows) { /** * move robbi to the given position. * - * @param x robbis new x-position - * @param y robbis new y-position + * @param x robbi's new x-position + * @param y robbi's new y-position */ public void placeRobbi(int x, int y) { synchronized (this) { @@ -372,12 +360,12 @@ public void placeRobbi(int x, int y) { * Place a new Hollow at the given position if it is in bounds and robbi is not * on the tile. * - * @param x Hollows x-Position - * @param y Hollows y-Position + * @param x Hollow's x-Position + * @param y Hollow's y-Position */ public void placeHollow(int x, int y) { - x = normalizeCoord(x, numberOfColumns); - y = normalizeCoord(y, numberOfRows); + x = normalizeCoordinate(x, numberOfColumns); + y = normalizeCoordinate(y, numberOfRows); synchronized (this) { if ((x < numberOfColumns && y < numberOfRows) && !(x == robbi.getX() && y == robbi.getY())) tiles[x][y] = new Hollow(); @@ -395,8 +383,8 @@ public void placeHollow(int x, int y) { * @param y PileOfScrap y-Position */ public void placePileOfScrap(int x, int y) { - x = normalizeCoord(x, numberOfColumns); - y = normalizeCoord(y, numberOfRows); + x = normalizeCoordinate(x, numberOfColumns); + y = normalizeCoordinate(y, numberOfRows); synchronized (this) { tiles[x][y] = new PileOfScrap(); } @@ -412,8 +400,8 @@ public void placePileOfScrap(int x, int y) { * @param y Stockpile y-Position */ public void placeStockpile(int x, int y) { - x = normalizeCoord(x, numberOfColumns); - y = normalizeCoord(y, numberOfRows); + x = normalizeCoordinate(x, numberOfColumns); + y = normalizeCoordinate(y, numberOfRows); synchronized (this) { tiles[x][y] = new Stockpile(); } @@ -428,8 +416,8 @@ public void placeStockpile(int x, int y) { * @param y Accu y-Position */ public void placeAccu(int x, int y) { - x = normalizeCoord(x, numberOfColumns); - y = normalizeCoord(y, numberOfRows); + x = normalizeCoordinate(x, numberOfColumns); + y = normalizeCoordinate(y, numberOfRows); synchronized (this) { tiles[x][y].setItem(new Accu()); } @@ -445,8 +433,8 @@ public void placeAccu(int x, int y) { * @param y Screw y-Position */ public void placeScrew(int x, int y) { - x = normalizeCoord(x, numberOfColumns); - y = normalizeCoord(y, numberOfRows); + x = normalizeCoordinate(x, numberOfColumns); + y = normalizeCoordinate(y, numberOfRows); synchronized (this) { tiles[x][y].setItem(new Screw()); } @@ -461,8 +449,8 @@ public void placeScrew(int x, int y) { * @param y Nut y-Position */ public void placeNut(int x, int y) { - x = normalizeCoord(x, numberOfColumns); - y = normalizeCoord(y, numberOfRows); + x = normalizeCoordinate(x, numberOfColumns); + y = normalizeCoordinate(y, numberOfRows); synchronized (this) { tiles[x][y].setItem(new Nut()); } @@ -477,8 +465,8 @@ public void placeNut(int x, int y) { * @param y Tile y-Position */ public void clearTile(int x, int y) { - x = normalizeCoord(x, numberOfColumns); - y = normalizeCoord(y, numberOfRows); + x = normalizeCoordinate(x, numberOfColumns); + y = normalizeCoordinate(y, numberOfRows); synchronized (this) { tiles[x][y] = new Tile(); } @@ -549,8 +537,7 @@ public boolean fromXML(InputStream stream) { } break; case "facing": - DIRECTION facing = DIRECTION.valueOf(parser.getAttributeValue(null, "facing")); - robbiDirection = facing; + robbiDirection = DIRECTION.valueOf(parser.getAttributeValue(null, "facing")); break; case "robbi": robbiX = Integer.parseInt(parser.getAttributeValue(null, "col")); @@ -579,7 +566,7 @@ public boolean fromXML(InputStream stream) { try { stream.close(); } catch (IOException e) { - e.printStackTrace(); + logger.error("Could not close stream", e); } } } @@ -594,14 +581,12 @@ public boolean fromXML(InputStream stream) { */ private Item getItemFromParser(XMLStreamReader parser) { String type = parser.getAttributeValue(null, "type"); - if (type.equals("Nut")) { - return new Nut(); - } else if (type.equals("Accu")) { - return new Accu(); - } else if (type.equals("Screw")) { - return new Screw(); - } - return null; + return switch (type) { + case "Nut" -> new Nut(); + case "Accu" -> new Accu(); + case "Screw" -> new Screw(); + default -> null; + }; } /** @@ -689,14 +674,13 @@ public ByteArrayOutputStream toXML() { return baos; } catch (FactoryConfigurationError | XMLStreamException e) { - e.printStackTrace(); - logger.error("failed to save as XML."); + logger.error("failed to save as XML.", e); return null; } } /** - * Helper to store an item in an xml-file. + * Helper to store an item in a xml-file. * * @param writer the writer, the item needs to be written to * @param item the item to write @@ -717,7 +701,7 @@ private void writeItem(XMLStreamWriter writer, Item item) throws XMLStreamExcept */ private Optional getDTD() { Optional module = ModuleLayer.boot().findModule("RobbiSimulator"); - if(module.isEmpty()) { + if (module.isEmpty()) { logger.warn("Could not load dtd"); return Optional.empty(); } @@ -736,18 +720,18 @@ private Optional getDTD() { } /** - * Getter for Robbis x Position. + * Getter for Robbi's x Position. * - * @return robbis x Position + * @return robbi's x Position */ public synchronized int getRobbiX() { return robbi.getX(); } /** - * Getter for Robbis y Position. + * Getter for Robbi's y Position. * - * @return robbis y Position + * @return robbi's y Position */ public synchronized int getRobbiY() { return robbi.getY(); diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/TerritoryState.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/TerritoryState.java index 592104f..556a239 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/TerritoryState.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/TerritoryState.java @@ -9,59 +9,58 @@ /** * Territory State to store all state relevant attributes in order to apply the * Memento-Pattern. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @XmlRootElement @Getter @NoArgsConstructor(access = AccessLevel.PRIVATE) public class TerritoryState { - @XmlElement - private int numberOfColumns; - @XmlElement - private int numberOfRows; - - @XmlElement - private Tile[][] tiles; - @XmlElement - private RobbiState robbiState; + @XmlElement + private int numberOfColumns; + @XmlElement + private int numberOfRows; - /** - * Constructor to create a Territory state. Creates an independet copy of the - * tiles array and the robbi. - * - * @param numberOfColumns number of columns in the territory - * @param numberOfRows number of rows in the territory - * @param tiles the tiles array representing the territory - * @param robbi the robbi in the territory - */ - public TerritoryState(int numberOfColumns, int numberOfRows, Tile[][] tiles, Robbi robbi) { - this.numberOfColumns = numberOfColumns; - this.numberOfRows = numberOfRows; - this.tiles = new Tile[tiles.length][tiles[0].length]; - for (int i = 0; i < tiles.length; i++) { - for (int j = 0; j < tiles[i].length; j++) { - Tile t = tiles[i][j]; - Tile newTile; - if (t instanceof Hollow) { - newTile = new Hollow(); - } else if (t instanceof PileOfScrap) { - newTile = new PileOfScrap(); - } else if (t instanceof Stockpile stockpile) { - newTile = new Stockpile(); - for (Item item : stockpile.getAllItems()) - newTile.setItem(item); - } else { - newTile = new Tile(); - newTile.setItem(t.getItem()); - } - this.tiles[i][j] = newTile; + @XmlElement + private Tile[][] tiles; + @XmlElement + private RobbiState robbiState; - } - } - this.robbiState = new RobbiState(robbi.getX(), robbi.getY(), robbi.getFacing(), robbi.getItem()); - } + /** + * Constructor to create a Territory state. Creates an independent copy of the + * tiles array and the robbi. + * + * @param numberOfColumns number of columns in the territory + * @param numberOfRows number of rows in the territory + * @param tiles the tiles array representing the territory + * @param robbi the robbi in the territory + */ + public TerritoryState(int numberOfColumns, int numberOfRows, Tile[][] tiles, Robbi robbi) { + this.numberOfColumns = numberOfColumns; + this.numberOfRows = numberOfRows; + this.tiles = new Tile[tiles.length][tiles[0].length]; + for (int i = 0; i < tiles.length; i++) { + for (int j = 0; j < tiles[i].length; j++) { + Tile newTile = switch (tiles[i][j]) { + case Hollow ignored: + yield new Hollow(); + case PileOfScrap ignored: + yield new PileOfScrap(); + case Stockpile stockpile: + Stockpile s = new Stockpile(); + for (Item item : stockpile.getAllItems()) + s.setItem(item); + yield s; + case Tile t: + Tile t2 = new Tile(); + t2.setItem(t.getItem()); + yield t2; + }; + this.tiles[i][j] = newTile; + } + } + this.robbiState = new RobbiState(robbi.getX(), robbi.getY(), robbi.getFacing(), robbi.getItem()); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/Tile.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/Tile.java index e8f79e6..7f3caba 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/Tile.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/Tile.java @@ -1,7 +1,5 @@ package com.JayPi4c.RobbiSimulator.model; -import java.io.Serializable; - import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAnyElement; @@ -9,35 +7,38 @@ import lombok.Getter; import lombok.Setter; +import java.io.Serial; +import java.io.Serializable; + /** * The default Tile for the territory. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ +@Getter +@Setter @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Tile implements Serializable { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * Attribute to store the item whoch is placed on the tile. - */ - @Getter - @Setter - @XmlAnyElement(lax = true) - private Item item = null; + /** + * Attribute to store the item which is placed on the tile. + */ + @XmlAnyElement(lax = true) + private Item item = null; - /** - * Removes the item from the tile and returns it. - * - * @return the item stored on the tile - */ - public Item pickItem() { - Item it = item; - item = null; - return it; - } + /** + * Removes the item from the tile and returns it. + * + * @return the item stored on the tile + */ + public Item pickItem() { + Item it = item; + item = null; + return it; + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/TileBlockedException.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/TileBlockedException.java index e8e4fe9..50a4728 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/TileBlockedException.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/TileBlockedException.java @@ -1,21 +1,23 @@ package com.JayPi4c.RobbiSimulator.model; +import java.io.Serial; + /** * Exception to be thrown if robbi attempts to move a pile of Scrap while the * tile ahead is not suitable to push a pile to. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public class TileBlockedException extends RobbiException { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * Constructor for a new TileBlockedException with localized message. - */ - public TileBlockedException() { - super("Exception.TileBlocked"); - } + /** + * Constructor for a new TileBlockedException with localized message. + */ + public TileBlockedException() { + super("Exception.TileBlocked"); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/TileIsFullException.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/TileIsFullException.java index 8aeb97b..c3c6efa 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/model/TileIsFullException.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/TileIsFullException.java @@ -1,21 +1,23 @@ package com.JayPi4c.RobbiSimulator.model; +import java.io.Serial; + /** * Exception to be thrown if robbi attempts to place an item on a tile, that has * already an item stored. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public class TileIsFullException extends RobbiException { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * Constructor for a new TileIsFullException with localized message. - */ - public TileIsFullException() { - super("Exception.TileIsFull"); - } + /** + * Constructor for a new TileIsFullException with localized message. + */ + public TileIsFullException() { + super("Exception.TileIsFull"); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/utils/AlertHelper.java b/src/main/java/com/JayPi4c/RobbiSimulator/utils/AlertHelper.java index f020d40..272d006 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/utils/AlertHelper.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/utils/AlertHelper.java @@ -9,104 +9,103 @@ /** * Static class to create Alerts with the necessary information. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class AlertHelper { - /** - * Creates and shows an alert with the given information. - * - * @param type the AlertType of the alert - * @param message the contentText for the alert - * @param owner the alerts owner to place it relative to the parent - */ - public static void showAlertAndWait(AlertType type, String message, Window owner) { - Alert alert = createAlert(type, message, owner); - alert.showAndWait(); - } + /** + * Creates and shows an alert with the given information. + * + * @param type the AlertType of the alert + * @param message the contentText for the alert + * @param owner the alerts owner to place it relative to the parent + */ + public static void showAlertAndWait(AlertType type, String message, Window owner) { + Alert alert = createAlert(type, message, owner); + alert.showAndWait(); + } - /** - * Creates and shows an alert with the given information. - * - * @param type the AlertType of the alert - * @param message the contentText for the alert - * @param owner the alerts owner to place it relative to the parent - * @param modality the modality of the alert - */ - public static void showAlertAndWait(AlertType type, String message, Window owner, Modality modality) { - Alert alert = createAlert(type, message, owner, modality); - alert.showAndWait(); - } + /** + * Creates and shows an alert with the given information. + * + * @param type the AlertType of the alert + * @param message the contentText for the alert + * @param owner the alerts owner to place it relative to the parent + * @param modality the modality of the alert + */ + public static void showAlertAndWait(AlertType type, String message, Window owner, Modality modality) { + Alert alert = createAlert(type, message, owner, modality); + alert.showAndWait(); + } - /** - * Creates and shows an alert with the given information. - * - * @param type the AlertType of the alert - * @param message the contentText for the alert - * @param owner the alerts owner to place it relative to the parent - * @param modality the modality of the alert - * @param title the title for the alert - * @param header the header for the alert - */ - public static void showAlertAndWait(AlertType type, String message, Window owner, Modality modality, String title, - String header) { - Alert alert = createAlert(type, message, owner, modality, title, header); - alert.showAndWait(); - } + /** + * Creates and shows an alert with the given information. + * + * @param type the AlertType of the alert + * @param message the contentText for the alert + * @param owner the alerts owner to place it relative to the parent + * @param modality the modality of the alert + * @param title the title for the alert + * @param header the header for the alert + */ + public static void showAlertAndWait(AlertType type, String message, Window owner, Modality modality, String title, + String header) { + Alert alert = createAlert(type, message, owner, modality, title, header); + alert.showAndWait(); + } - /** - * Creates an alert with the given information. - * - * @param type the AlertType of the alert - * @param message the contentText for the alert - * @param owner the alerts owner to place it relative to the parent - * @param modality the modality of the alert - * @param title the title for the alert - * @param header the header for the alert - * @return the alert with the given information - */ - public static Alert createAlert(AlertType type, String message, Window owner, Modality modality, String title, - String header) { - Alert alert = new Alert(type); - alert.setContentText(message); - if (title != null) - alert.setTitle(title); - if (header != null) - alert.setHeaderText(header); - alert.initModality(modality); - alert.initOwner(owner); - return alert; - } + /** + * Creates an alert with the given information. + * + * @param type the AlertType of the alert + * @param message the contentText for the alert + * @param owner the alerts owner to place it relative to the parent + * @param modality the modality of the alert + * @param title the title for the alert + * @param header the header for the alert + * @return the alert with the given information + */ + public static Alert createAlert(AlertType type, String message, Window owner, Modality modality, String title, + String header) { + Alert alert = new Alert(type); + alert.setContentText(message); + if (title != null) + alert.setTitle(title); + if (header != null) + alert.setHeaderText(header); + alert.initModality(modality); + alert.initOwner(owner); + return alert; + } - /** - * Creates an alert with the given information. - * - * @param type the AlertType of the alert - * @param message the contentText for the alert - * @param owner the alerts owner to place it relative to the parent - * @param modality the modality of the alert - * @return the alert with the given information - */ - public static Alert createAlert(AlertType type, String message, Window owner, Modality modality) { - return createAlert(type, message, owner, modality, null, null); - } + /** + * Creates an alert with the given information. + * + * @param type the AlertType of the alert + * @param message the contentText for the alert + * @param owner the alerts owner to place it relative to the parent + * @param modality the modality of the alert + * @return the alert with the given information + */ + public static Alert createAlert(AlertType type, String message, Window owner, Modality modality) { + return createAlert(type, message, owner, modality, null, null); + } - /** - * Creates an alert with the given information. - * - * @param type the AlertType of the alert - * @param message the contentText for the alert - * @param owner the alerts owner to place it relative to the parent - * @return the alert with the given information - */ - public static Alert createAlert(AlertType type, String message, Window owner) { - Alert alert = new Alert(type); - alert.setContentText(message); - alert.initOwner(owner); - return alert; - } + /** + * Creates an alert with the given information. + * + * @param type the AlertType of the alert + * @param message the contentText for the alert + * @param owner the alerts owner to place it relative to the parent + * @return the alert with the given information + */ + public static Alert createAlert(AlertType type, String message, Window owner) { + Alert alert = new Alert(type); + alert.setContentText(message); + alert.initOwner(owner); + return alert; + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/utils/HibernateUtils.java b/src/main/java/com/JayPi4c/RobbiSimulator/utils/HibernateUtils.java index c6f7174..0289b37 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/utils/HibernateUtils.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/utils/HibernateUtils.java @@ -1,15 +1,14 @@ package com.JayPi4c.RobbiSimulator.utils; -import org.hibernate.SessionFactory; -import org.hibernate.cfg.Configuration; - import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; /** * Utility class to manage the Hibernate SessionFactory. - * + * * @author Jonas Pohl * @since 1.0.0 */ @@ -17,37 +16,36 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class HibernateUtils { - /** - * The session factory to interact with the database via hibernate. - */ - private static SessionFactory sessionFactory; + /** + * The session factory to interact with the database via hibernate. + */ + private static SessionFactory sessionFactory; - /** - * Getter for the SessionFactory. If the SessionFactory is null, it will be - * created. - * - * @return The SessionFactory. - * - */ - public static SessionFactory getSessionFactory() { - if (sessionFactory == null) { - System.setProperty("derby.stream.error.file", "logs/derby.log"); // make sure log-file is in logs folder - try { - sessionFactory = new Configuration().configure().buildSessionFactory(); - } catch (Exception ex) { - logger.error("Initial SessionFactory creation failed: {}", ex); - throw new ExceptionInInitializerError(ex); - } - } - return sessionFactory; - } + /** + * Getter for the SessionFactory. If the SessionFactory is null, it will be + * created. + * + * @return The SessionFactory. + */ + public static SessionFactory getSessionFactory() { + if (sessionFactory == null) { + System.setProperty("derby.stream.error.file", "logs/derby.log"); // make sure log-file is in logs folder + try { + sessionFactory = new Configuration().configure().buildSessionFactory(); + } catch (Exception ex) { + logger.error("Initial SessionFactory creation failed.", ex); + throw new ExceptionInInitializerError(ex); + } + } + return sessionFactory; + } - /** - * Utility function to shutdown the database connection. - */ - public static void shutdown() { - if (sessionFactory != null) - sessionFactory.close(); - } + /** + * Utility function to shut down the database connection. + */ + public static void shutdown() { + if (sessionFactory != null) + sessionFactory.close(); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/utils/I18nUtils.java b/src/main/java/com/JayPi4c/RobbiSimulator/utils/I18nUtils.java index 89c460c..3897137 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/utils/I18nUtils.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/utils/I18nUtils.java @@ -1,9 +1,5 @@ package com.JayPi4c.RobbiSimulator.utils; -import java.text.MessageFormat; -import java.util.Locale; -import java.util.ResourceBundle; - import javafx.beans.binding.Bindings; import javafx.beans.binding.StringBinding; import javafx.beans.property.ObjectProperty; @@ -13,9 +9,13 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.text.MessageFormat; +import java.util.Locale; +import java.util.ResourceBundle; + /** * Utility-Class to support internationalization. - * + * *
* Some research pointed out the eclipse solution for i18n. For further * information have a look in the this * Stack Overflow article. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class I18nUtils { - /** - * The resource bundle on which elements can bind in order to be updated on - * language change. - */ - private static final ObjectProperty bundle = new SimpleObjectProperty<>(); + /** + * The resource bundle on which elements can bind in order to be updated on + * language change. + */ + private static final ObjectProperty bundle = new SimpleObjectProperty<>(); - /** - * The current locale. Will be initially set to {@link Locale#UK} in JavaFX init - * Method. - * - */ - @Getter - private static Locale locale; + /** + * The current locale. Will be initially set to {@link Locale#UK} in JavaFX init + * Method. + */ + @Getter + private static Locale locale; - static { - setLocale(PropertiesLoader.getLocale()); - } + static { + setLocale(PropertiesLoader.getLocale()); + } - /** - * ObjectProperty to allow bindings - * - * @return The ObjectProperty - */ - public static ObjectProperty bundleProperty() { - return bundle; - } + /** + * ObjectProperty to allow bindings + * + * @return The ObjectProperty + */ + public static ObjectProperty bundleProperty() { + return bundle; + } - /** - * Getter for the current locale. - * - * @return the current locale - */ - public static ResourceBundle getBundle() { - return bundle.get(); - } + /** + * Getter for the current locale. + * + * @return the current locale + */ + public static ResourceBundle getBundle() { + return bundle.get(); + } - /** - * Setter for the current resourceBundle. - * - * @param bundle the new bundle - */ - public static void setBundle(ResourceBundle bundle) { - bundleProperty().set(bundle); - } + /** + * Setter for the current resourceBundle. + * + * @param bundle the new bundle + */ + public static void setBundle(ResourceBundle bundle) { + bundleProperty().set(bundle); + } - /** - * Returns the String mapped to the provided key in the current locale. - * - * @param key the key to be mapped - * @param args the arguments to be inserted into the String - * @return the localized String for the key - */ - public static String i18n(String key, final Object... args) { - return MessageFormat.format(getBundle().getString(key), args); - } + /** + * Returns the String mapped to the provided key in the current locale. + * + * @param key the key to be mapped + * @param args the arguments to be inserted into the String + * @return the localized String for the key + */ + public static String i18n(String key, final Object... args) { + return MessageFormat.format(getBundle().getString(key), args); + } - /** - * Helper to create a new String Binding for the provided key. - * - * @param key the key to be mapped on the resources - * @param args the arguments to be inserted into the String - * @return a binding for the provided key - */ - public static StringBinding createBinding(String key, final Object... args) { - return Bindings.createStringBinding(() -> i18n(key, args), bundleProperty()); - } + /** + * Helper to create a new String Binding for the provided key. + * + * @param key the key to be mapped on the resources + * @param args the arguments to be inserted into the String + * @return a binding for the provided key + */ + public static StringBinding createBinding(String key, final Object... args) { + return Bindings.createStringBinding(() -> i18n(key, args), bundleProperty()); + } - /** - * Method to create a tooltip for the given key and object parameters - * - * @param key the key for the language file - * @param args the arguments for the language file - * @return a i18n tooltip - */ - public static Tooltip createTooltip(String key, final Object... args) { - Tooltip tt = new Tooltip(); - tt.textProperty().bind(I18nUtils.createBinding(key, args)); - return tt; - } + /** + * Method to create a tooltip for the given key and object parameters + * + * @param key the key for the language file + * @param args the arguments for the language file + * @return a i18n tooltip + */ + public static Tooltip createTooltip(String key, final Object... args) { + Tooltip tt = new Tooltip(); + tt.textProperty().bind(I18nUtils.createBinding(key, args)); + return tt; + } - /** - * Sets the bundle to the given locale. - * - * @param locale the new locale for the resourceBundle - */ - public static void setLocale(Locale locale) { - I18nUtils.locale = locale; - Locale.setDefault(locale); - setBundle(ResourceBundle.getBundle("lang.messages", locale)); - } + /** + * Sets the bundle to the given locale. + * + * @param locale the new locale for the resourceBundle + */ + public static void setLocale(Locale locale) { + I18nUtils.locale = locale; + Locale.setDefault(locale); + setBundle(ResourceBundle.getBundle("lang.messages", locale)); + } - /** - * Setter for the current locale by the String representation of the locale. - * - * @param locale the locale as String - */ - public static void setLocale(String locale) { - try { - String[] parts = locale.split("_"); - setLocale(new Locale(parts[0], parts[1])); - } catch (IndexOutOfBoundsException | NullPointerException e) { - setLocale(Locale.UK); - } - } + /** + * Setter for the current locale by the String representation of the locale. + * + * @param languageTag the locale defined by a language tag + */ + public static void setLocale(String languageTag) { + try { + setLocale(Locale.forLanguageTag(languageTag)); + } catch (IndexOutOfBoundsException | NullPointerException e) { + setLocale(Locale.UK); + } + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/utils/Observable.java b/src/main/java/com/JayPi4c/RobbiSimulator/utils/Observable.java index 23f16d2..2b85e5e 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/utils/Observable.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/utils/Observable.java @@ -5,83 +5,82 @@ /** * Abstract class to allow the Observer-Pattern. Classes implementing the * Observer Interface can register to classes extending this Observable class. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public abstract class Observable { - private CopyOnWriteArrayList observers; // faster than vector for read access and thread save + private final CopyOnWriteArrayList observers; // faster than vector for read access and thread save - private boolean changed; + private boolean changed; - private boolean notify = true; + private boolean notify = true; - /** - * Constructor for the Observable class which initializes the observer array. - */ - protected Observable() { - observers = new CopyOnWriteArrayList<>(); - changed = false; - } + /** + * Constructor for the Observable class which initializes the observer array. + */ + protected Observable() { + observers = new CopyOnWriteArrayList<>(); + changed = false; + } - /** - * Sets the changed flag to true, in order to allow the notifyAllObservers - * method to inform all observers. - */ - public void setChanged() { - changed = true; - } + /** + * Sets the changed flag to true, in order to allow the notifyAllObservers + * method to inform all observers. + */ + public void setChanged() { + changed = true; + } - /** - * Notifies all observers that the Observable Object has been updated. - */ - public void notifyAllObservers() { - if (notify) { - if (changed) { - for (Observer s : observers) - s.update(this); - } - changed = false; - } - } + /** + * Notifies all observers that the Observable Object has been updated. + */ + public void notifyAllObservers() { + if (notify) { + if (changed) { + for (Observer s : observers) + s.update(this); + } + changed = false; + } + } - /** - * Register a new Observer to the list of Observers. - * - * @param obs the new Observer to add to the list of observers. - */ - public void addObserver(Observer obs) { - observers.add(obs); - } + /** + * Register a new Observer to the list of Observers. + * + * @param obs the new Observer to add to the list of observers. + */ + public void addObserver(Observer obs) { + observers.add(obs); + } - /** - * Removes the given Observer from the list of observers. The Observer will no - * longer be notified about any updates. - * - * @param obs the observer to remove from the list of observers - * @return true if the observer could be removed, false otherwise - */ - public boolean removeObserver(Observer obs) { - return observers.remove(obs); - } + /** + * Removes the given Observer from the list of observers. The Observer will no + * longer be notified about any updates. + * + * @param obs the observer to remove from the list of observers + * @return true if the observer could be removed, false otherwise + */ + public boolean removeObserver(Observer obs) { + return observers.remove(obs); + } - /** - * Allows to send Notifications to all observers and notifies all Observers if - * changes have been made while the notifications had been deactivated. - */ - public void activateNotification() { - this.notify = true; - notifyAllObservers(); - } + /** + * Allows to send Notifications to all observers and notifies all Observers if + * changes have been made while the notifications had been deactivated. + */ + public void activateNotification() { + this.notify = true; + notifyAllObservers(); + } - /** - * Prevents the Observable class from notifying any observers even if - * notifyAllObservers is called. To activate notifications again, call - * activateNotification. - */ - public void deactivateNotification() { - this.notify = false; - } + /** + * Prevents the Observable class from notifying any observers even if + * notifyAllObservers is called. To activate notifications again, call + * activateNotification. + */ + public void deactivateNotification() { + this.notify = false; + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/utils/Observer.java b/src/main/java/com/JayPi4c/RobbiSimulator/utils/Observer.java index 9b78eac..4529ed6 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/utils/Observer.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/utils/Observer.java @@ -3,18 +3,17 @@ /** * Interface to allow to register to Observable classes. The update function * will be called whenever the observable class decides to notify all Observers. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public interface Observer { - /** - * Notifies the observer whenever the Observable class is notifying all - * observers about changes. - * - * @param observable the Observable to call the update function. - */ - public void update(Observable observable); + /** + * Notifies the observer whenever the Observable class is notifying all + * observers about changes. + * + * @param observable the Observable to call the update function. + */ + void update(Observable observable); } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/utils/PropertiesLoader.java b/src/main/java/com/JayPi4c/RobbiSimulator/utils/PropertiesLoader.java index cadcb8f..15bfc8b 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/utils/PropertiesLoader.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/utils/PropertiesLoader.java @@ -1,5 +1,9 @@ package com.JayPi4c.RobbiSimulator.utils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -7,160 +11,154 @@ import java.util.Locale; import java.util.Properties; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; - /** * Utility-class for Properties. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) public class PropertiesLoader { - private static Properties properties; - - private static final String DIR = System.getProperty("user.dir"); - private static final String FILE = "/simulator.properties"; - private static final String COMMENTS = "role=student OR tutor"; - - private static final String DARKMODE_PROPERTY = "darkmode"; - private static final String SOUNDS_PROPERTY = "sounds"; - private static final String TUTORPORT_PROPERTY = "tutorport"; - private static final String ROLE_PROPERTY = "role"; - private static final String LANGUAGE_PROPERTY = "lang"; - private static final String TUTORHOST_PROPERTY = "tutorhost"; - - private static final String TUTORHOST_DEFAULT_VALUE = "localhost"; - private static final String TUTORPORT_DEFAULT_VALUE = "3579"; - - /** - * Loads the properties and stores them in an Object. - * - * If the simulator.properties file can't be found, default values will be - * loaded. - * - * @return true if the initialization was successful, false otherwise - */ - public static boolean initialize() { - properties = new Properties(); - try (InputStream in = new FileInputStream(DIR + FILE)) { - properties.load(in); - return true; - } catch (IOException e) { - loadDefaultProperties(); - return false; - } - } - - /** - * Getter for the role - * - * @return true if the role is set to tutor, false otherwise - */ - public static boolean isTutor() { - try { - return properties.getProperty(ROLE_PROPERTY).equalsIgnoreCase("tutor"); - } catch (NullPointerException e) { - properties.put(ROLE_PROPERTY, "student"); - return false; - } - } - - /** - * Getter for the sounds property. - * - * @return true if the sounds property is set to true - */ - public static boolean getSounds() { - return Boolean.parseBoolean(properties.getProperty(SOUNDS_PROPERTY)); - } - - /** - * Getter for the sounds property. - * - * @return true if the sounds property is set to true - */ - public static boolean getDarkmode() { - return Boolean.parseBoolean(properties.getProperty(DARKMODE_PROPERTY)); - } - - /** - * Getter for the tutorhost. - * - * @return the tutorhost stored in the properties file, localhost if no - * propterty is found - */ - public static String getTutorhost() { - String host = properties.getProperty(TUTORHOST_PROPERTY); - if (host == null) { - host = TUTORHOST_DEFAULT_VALUE; - properties.put(TUTORHOST_PROPERTY, host); - } - return host; - } - - /** - * Getter for the tutorport. - * - * @return the tutorport stored in the properties file - */ - public static int getTutorport() { - try { - return Integer.parseInt(properties.getProperty(TUTORPORT_PROPERTY)); - } catch (NumberFormatException | NullPointerException e) { - properties.put(TUTORPORT_PROPERTY, TUTORPORT_DEFAULT_VALUE); - return Integer.parseInt(TUTORPORT_DEFAULT_VALUE); - } - } - - /** - * Getter for the locale. - * - * @return the locale stored in the properties file - */ - public static Locale getLocale() { - try { - String[] parts = properties.getProperty(LANGUAGE_PROPERTY).split("_"); - return new Locale(parts[0], parts[1]); - } catch (IndexOutOfBoundsException | NullPointerException e) { - logger.debug("Failed to load locale from properties"); - properties.put(LANGUAGE_PROPERTY, Locale.GERMANY.toString()); - return Locale.GERMANY; - } - } - - /** - * Stores all properties back in the properties file. - * - * @return true, if the saving was successful, false otherwise - */ - public static boolean finish() { - properties.put(LANGUAGE_PROPERTY, I18nUtils.getLocale().toString()); - properties.put(SOUNDS_PROPERTY, Boolean.toString(SoundManager.getSound())); - properties.put(DARKMODE_PROPERTY, Boolean.toString(SceneManager.getDarkmode())); - try (FileOutputStream fos = new FileOutputStream(DIR + FILE)) { - properties.store(fos, COMMENTS); - return true; - } catch (IOException e) { - return false; - } - } - - /** - * Loads initial values in the properties object. This is needed if the - * application failed to load properties from the file. - */ - private static void loadDefaultProperties() { - properties.put(LANGUAGE_PROPERTY, Locale.GERMANY.toString()); - properties.put(ROLE_PROPERTY, "student"); - properties.put(TUTORPORT_PROPERTY, TUTORPORT_DEFAULT_VALUE); - properties.put(TUTORHOST_PROPERTY, TUTORHOST_DEFAULT_VALUE); - properties.put(SOUNDS_PROPERTY, Boolean.toString(false)); - properties.put(DARKMODE_PROPERTY, Boolean.toString(false)); - } + private static Properties properties; + + private static final String DIR = System.getProperty("user.dir"); + private static final String FILE = "/simulator.properties"; + private static final String COMMENTS = "role=student OR tutor"; + + private static final String DARKMODE_PROPERTY = "darkmode"; + private static final String SOUNDS_PROPERTY = "sounds"; + private static final String TUTORPORT_PROPERTY = "tutorport"; + private static final String ROLE_PROPERTY = "role"; + private static final String LANGUAGE_PROPERTY = "lang"; + private static final String TUTORHOST_PROPERTY = "tutorhost"; + + private static final String TUTORHOST_DEFAULT_VALUE = "localhost"; + private static final String TUTORPORT_DEFAULT_VALUE = "3579"; + + /** + * Loads the properties and stores them in an Object. + *

+ * If the simulator.properties file can't be found, default values will be + * loaded. + * + * @return true if the initialization was successful, false otherwise + */ + public static boolean initialize() { + properties = new Properties(); + try (InputStream in = new FileInputStream(DIR + FILE)) { + properties.load(in); + return true; + } catch (IOException e) { + loadDefaultProperties(); + return false; + } + } + + /** + * Getter for the role + * + * @return true if the role is set to tutor, false otherwise + */ + public static boolean isTutor() { + try { + return properties.getProperty(ROLE_PROPERTY).equalsIgnoreCase("tutor"); + } catch (NullPointerException e) { + properties.put(ROLE_PROPERTY, "student"); + return false; + } + } + + /** + * Getter for the sounds-property. + * + * @return true if the sounds property is set to true + */ + public static boolean getSounds() { + return Boolean.parseBoolean(properties.getProperty(SOUNDS_PROPERTY)); + } + + /** + * Getter for the darkmode-property. + * + * @return true if the darkmode property is set to true + */ + public static boolean getDarkmode() { + return Boolean.parseBoolean(properties.getProperty(DARKMODE_PROPERTY)); + } + + /** + * Getter for the tutorhost. + * + * @return the tutorhost stored in the properties file, localhost if no + * propterty is found + */ + public static String getTutorhost() { + String host = properties.getProperty(TUTORHOST_PROPERTY); + if (host == null) { + host = TUTORHOST_DEFAULT_VALUE; + properties.put(TUTORHOST_PROPERTY, host); + } + return host; + } + + /** + * Getter for the tutor-port. + * + * @return the tutor-port stored in the properties file + */ + public static int getTutorport() { + try { + return Integer.parseInt(properties.getProperty(TUTORPORT_PROPERTY)); + } catch (NumberFormatException | NullPointerException e) { + properties.put(TUTORPORT_PROPERTY, TUTORPORT_DEFAULT_VALUE); + return Integer.parseInt(TUTORPORT_DEFAULT_VALUE); + } + } + + /** + * Getter for the locale. + * + * @return the locale stored in the properties file + */ + public static Locale getLocale() { + try { + return Locale.forLanguageTag(properties.getProperty(LANGUAGE_PROPERTY)); + } catch (IndexOutOfBoundsException | NullPointerException e) { + logger.debug("Failed to load locale from properties"); + properties.put(LANGUAGE_PROPERTY, Locale.GERMANY.toLanguageTag()); + return Locale.GERMANY; + } + } + + /** + * Stores all properties back in the properties file. + * + * @return true, if the saving was successful, false otherwise + */ + public static boolean finish() { + properties.put(LANGUAGE_PROPERTY, I18nUtils.getLocale().toLanguageTag()); + properties.put(SOUNDS_PROPERTY, Boolean.toString(SoundManager.getSound())); + properties.put(DARKMODE_PROPERTY, Boolean.toString(SceneManager.getDarkmode())); + try (FileOutputStream fos = new FileOutputStream(DIR + FILE)) { + properties.store(fos, COMMENTS); + return true; + } catch (IOException e) { + return false; + } + } + + /** + * Loads initial values in the properties object. This is needed if the + * application failed to load properties from the file. + */ + private static void loadDefaultProperties() { + properties.put(LANGUAGE_PROPERTY, Locale.GERMANY.toLanguageTag()); + properties.put(ROLE_PROPERTY, "student"); + properties.put(TUTORPORT_PROPERTY, TUTORPORT_DEFAULT_VALUE); + properties.put(TUTORHOST_PROPERTY, TUTORHOST_DEFAULT_VALUE); + properties.put(SOUNDS_PROPERTY, Boolean.toString(false)); + properties.put(DARKMODE_PROPERTY, Boolean.toString(false)); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/utils/SceneManager.java b/src/main/java/com/JayPi4c/RobbiSimulator/utils/SceneManager.java index 49b1928..daf24a8 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/utils/SceneManager.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/utils/SceneManager.java @@ -9,55 +9,54 @@ /** * Class to manage the loaded scene. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class SceneManager { - - private static final String DARK_MODE_CSS = Objects.requireNonNull(SceneManager.class.getResource("/css/dark-theme.css")).toExternalForm(); - - /** - * The boolean on which elements can bind in order to be updated on dark-mode - * change. - */ - private static final ObjectProperty darkmode = new SimpleObjectProperty<>(PropertiesLoader.getDarkmode()); - - /** - * ObjectProperty to allow bindings - * - * @return The ObjectProperty - */ - public static ObjectProperty darkmodeProperty() { - return darkmode; - } - - /** - * Getter for the current dark-mode flag. - * - * @return the current dark-mode setting - */ - public static boolean getDarkmode() { - return darkmode.get(); - } - - /** - * Setter for the current dark-mode flag. - * - * @param flag the new value - */ - public static void setDarkmode(boolean flag) { - darkmodeProperty().set(flag); - } - - /** - * Getter for the dark-mode css source. - * - * @return the source for the dark-mode css - */ - public static String getDarkmodeCss() { - return DARK_MODE_CSS; - } + + private static final String DARK_MODE_CSS = Objects.requireNonNull(SceneManager.class.getResource("/css/dark-theme.css")).toExternalForm(); + + /** + * The boolean on which elements can bind in order to be updated on dark-mode + * change. + */ + private static final ObjectProperty darkmode = new SimpleObjectProperty<>(PropertiesLoader.getDarkmode()); + + /** + * ObjectProperty to allow bindings + * + * @return The ObjectProperty + */ + public static ObjectProperty darkmodeProperty() { + return darkmode; + } + + /** + * Getter for the current dark-mode flag. + * + * @return the current dark-mode setting + */ + public static boolean getDarkmode() { + return darkmode.get(); + } + + /** + * Setter for the current dark-mode flag. + * + * @param flag the new value + */ + public static void setDarkmode(boolean flag) { + darkmodeProperty().set(flag); + } + + /** + * Getter for the dark-mode css source. + * + * @return the source for the dark-mode css + */ + public static String getDarkmodeCss() { + return DARK_MODE_CSS; + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/utils/SoundManager.java b/src/main/java/com/JayPi4c/RobbiSimulator/utils/SoundManager.java index fc123ca..b778472 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/utils/SoundManager.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/utils/SoundManager.java @@ -1,75 +1,76 @@ package com.JayPi4c.RobbiSimulator.utils; -import java.net.URISyntaxException; - import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.scene.media.Media; import javafx.scene.media.MediaPlayer; import lombok.AccessLevel; import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.net.URISyntaxException; /** * Manager to load and play sounds. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ +@Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) public class SoundManager { - /** - * The boolean on which elements can bind in order to be updated if sound is - * deactivated /activated - */ - private static final ObjectProperty sound = new SimpleObjectProperty<>(PropertiesLoader.getSounds()); + /** + * The boolean on which elements can bind in order to be updated if sound is + * deactivated /activated + */ + private static final ObjectProperty sound = new SimpleObjectProperty<>(PropertiesLoader.getSounds()); - private static Media warningSound = null; + private static Media warningSound = null; - /** - * Plays a warning sounds. If the sound is not loaded yet, the sound will be - * loaded and stored for further plays. - */ - public static void playWarnSound() { - if (Boolean.FALSE.equals(sound.get())) - return; - if (warningSound == null) { - try { - warningSound = new Media( - SoundManager.class.getResource("/sounds/warning-sound.mp3").toURI().toString()); - } catch (URISyntaxException e) { - e.printStackTrace(); - } - } - MediaPlayer mediaPlayer = new MediaPlayer(warningSound); - mediaPlayer.play(); - } + /** + * Plays a warning sounds. If the sound is not loaded yet, the sound will be + * loaded and stored for further plays. + */ + public static void playWarnSound() { + if (Boolean.FALSE.equals(sound.get())) + return; + if (warningSound == null) { + try { + warningSound = new Media( + SoundManager.class.getResource("/sounds/warning-sound.mp3").toURI().toString()); + } catch (URISyntaxException e) { + logger.error("Could not load sound file.", e); + } + } + MediaPlayer mediaPlayer = new MediaPlayer(warningSound); + mediaPlayer.play(); + } - /** - * ObjectProperty to allow bindings - * - * @return The ObjectProperty - */ - public static ObjectProperty soundProperty() { - return sound; - } + /** + * ObjectProperty to allow bindings + * + * @return The ObjectProperty + */ + public static ObjectProperty soundProperty() { + return sound; + } - /** - * Getter for the current sound flag. - * - * @return the current sound setting - */ - public static boolean getSound() { - return sound.get(); - } + /** + * Getter for the current sound flag. + * + * @return the current sound setting + */ + public static boolean getSound() { + return sound.get(); + } - /** - * Setter for the current sound flag. - * - * @param flag the new value - */ - public static void setSound(boolean flag) { - soundProperty().set(flag); - } + /** + * Setter for the current sound flag. + * + * @param flag the new value + */ + public static void setSound(boolean flag) { + soundProperty().set(flag); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/utils/annotations/Invisible.java b/src/main/java/com/JayPi4c/RobbiSimulator/utils/annotations/Invisible.java index 6015cfd..a2e064b 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/utils/annotations/Invisible.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/utils/annotations/Invisible.java @@ -9,9 +9,8 @@ * Annotation to hide methods from the ContextMenu. Methods annotated with this * annotation will not show up in the ContextMenu. Nonetheless, they still can * be used in the code without any restrictions. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/view/MainStage.java b/src/main/java/com/JayPi4c/RobbiSimulator/view/MainStage.java index 5740cdb..39c7d96 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/view/MainStage.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/view/MainStage.java @@ -1,10 +1,6 @@ package com.JayPi4c.RobbiSimulator.view; -import com.JayPi4c.RobbiSimulator.controller.ButtonState; -import com.JayPi4c.RobbiSimulator.controller.LanguageController; -import com.JayPi4c.RobbiSimulator.controller.MainStageController; -import com.JayPi4c.RobbiSimulator.controller.SnackbarController; -import com.JayPi4c.RobbiSimulator.controller.TerritorySaveController; +import com.JayPi4c.RobbiSimulator.controller.*; import com.JayPi4c.RobbiSimulator.controller.examples.ExamplesController; import com.JayPi4c.RobbiSimulator.controller.program.Program; import com.JayPi4c.RobbiSimulator.controller.simulation.SimulationController; @@ -12,7 +8,6 @@ import com.JayPi4c.RobbiSimulator.controller.tutor.TutorController; import com.JayPi4c.RobbiSimulator.model.Territory; import com.JayPi4c.RobbiSimulator.utils.PropertiesLoader; - import eu.mihosoft.monacofx.MonacoFX; import javafx.scene.Scene; import javafx.scene.control.ScrollPane; @@ -28,249 +23,237 @@ /** * This class is the mainStage of the application and holds all GUI elements * that are visible to the user. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Slf4j @Getter public class MainStage extends Stage { - private Territory territory; - - private ButtonState buttonState; - - // controllers - private MainStageController mainStageController; - private SimulationController simulationController; - private TerritorySaveController territorySaveController; - private ExamplesController examplesController; - private StudentController studenController; - private TutorController tutorController; - private LanguageController languageController; - private SnackbarController snackbarController; - - /** - * Constant for the minimum width of the stage. - */ - public static final int MIN_WIDTH = 500; - /** - * Constant for the minimum height of the stage. - */ - public static final int MIN_HEIGHT = 200; - /** - * Constant for the default width of the stage.
- * Currently not in use. - */ - public static final int WIDTH = 1200; - /** - * Constant for the default height of the stage. - */ - public static final int HEIGHT = 450; - - private Program program; - - private MenuBar menubar; - - private Toolbar toolbar; - - // Content Pane - private MonacoFX textArea; - private ScrollPane territoryScrollPane; - private TerritoryPanel territoryPanel; - private SplitPane splitPane; - - private Scene mainStageScene; - - /** - * Constant Image for open icon. - */ - public static final Image openImage; - /** - * Constant Image for new icon. - */ - public static final Image newImage; - /** - * Constant Image for save icon. - */ - public static final Image saveImage; - /** - * Constant Image for compile icon. - */ - public static final Image compileImage; - /** - * Constant Image for print icon. - */ - public static final Image printImage; - /** - * Constant Image for terrain icon. (Used for changeSize button) - */ - public static final Image terrainImage; - - /** - * Constant Image for robbi icon. - */ - public static final Image menuRobbiImage; - /** - * Constant Image for hollow icon. - */ - public static final Image menuHollowImage; - /** - * Constant Image for pileOfScrap icon. - */ - public static final Image menuPileOfScrapImage; - /** - * Constant Image for stockpile icon. - */ - public static final Image menuStockpileImage; - /** - * Constant Image for accu icon. - */ - public static final Image menuAccuImage; - /** - * Constant Image for screw icon. - */ - public static final Image menuScrewImage; - /** - * Constant Image for nut icon. - */ - public static final Image menuNutImage; - /** - * Constant Image for delete icon. - */ - public static final Image menuDeleteImage; - - /** - * Constant Image for reset icon. - */ - public static final Image resetImage; - /** - * Constant Image for simulation start/resume icon. - */ - public static final Image menuStartImage; - /** - * Constant Image for simulation pause icon. - */ - public static final Image menuPauseImage; - /** - * Constant Image for simulation stop icon. - */ - public static final Image menuStopImage; - - /** - * Constant Image for RobbiMove icon. - */ - public static final Image robbiMove; - /** - * Constant Image for RobbiTurnLeft icon. - */ - public static final Image robbiTurnLeft; - /** - * Constant Image for RobbiPut icon. - */ - public static final Image robbiPut; - /** - * Constant Image for RobbiTake icon. - */ - public static final Image robbiTake; - - /** - * loading images - */ - static { - logger.debug("Loading stage images"); - newImage = new Image(String.valueOf(MainStage.class.getResource("/img/New24.gif"))); - saveImage = new Image(String.valueOf(MainStage.class.getResource("/img/Save24.gif"))); - openImage = new Image(String.valueOf(MainStage.class.getResource("/img/Open24.gif"))); - compileImage = new Image(String.valueOf(MainStage.class.getResource("/img/Compile24.gif"))); - printImage = new Image(String.valueOf(MainStage.class.getResource("/img/Print24.gif"))); - terrainImage = new Image(String.valueOf(MainStage.class.getResource("/img/Terrain24.gif"))); - menuRobbiImage = new Image(String.valueOf(MainStage.class.getResource("/img/Robbi24.png"))); - menuHollowImage = new Image(String.valueOf(MainStage.class.getResource("/img/Hollow24.png"))); - menuPileOfScrapImage = new Image(String.valueOf(MainStage.class.getResource("/img/PileOfScrap24.png"))); - menuStockpileImage = new Image(String.valueOf(MainStage.class.getResource("/img/Stockpile24.png"))); - menuAccuImage = new Image(String.valueOf(MainStage.class.getResource("/img/Accu24.png"))); - menuScrewImage = new Image(String.valueOf(MainStage.class.getResource("/img/Screw24.png"))); - menuNutImage = new Image(String.valueOf(MainStage.class.getResource("/img/Nut24.png"))); - menuDeleteImage = new Image(String.valueOf(MainStage.class.getResource("/img/Delete24.gif"))); - - resetImage = new Image(String.valueOf(MainStage.class.getResource("/img/reset24.png"))); - menuStartImage = new Image(String.valueOf(MainStage.class.getResource("/img/Play24.gif"))); - menuPauseImage = new Image(String.valueOf(MainStage.class.getResource("/img/Pause24.gif"))); - menuStopImage = new Image(String.valueOf(MainStage.class.getResource("/img/Stop24.gif"))); - - robbiMove = new Image(String.valueOf(MainStage.class.getResource("/img/RobbiMove24.png"))); - robbiTurnLeft = new Image(String.valueOf(MainStage.class.getResource("/img/RobbiLeft24.png"))); - robbiPut = new Image(String.valueOf(MainStage.class.getResource("/img/RobbiPut24.png"))); - robbiTake = new Image(String.valueOf(MainStage.class.getResource("/img/RobbiTake24.png"))); - } - - /** - * Constructor for the MainStage. It creates a mainStage for the given Program - * and loads and creates all needed Gui elements and controller.
- * This is the place, where the territory and the buttonState are created - * - * @param program the program this mainStage is for - */ - public MainStage(Program program) { - this.program = program; - - territory = new Territory(); - buttonState = new ButtonState(); - - menubar = new MenuBar(); - toolbar = new Toolbar(menubar); - createContentPane(); - - VBox.setVgrow(splitPane, Priority.ALWAYS); - var vBox = new VBox(menubar, toolbar, splitPane); - - mainStageScene = new Scene(vBox); - setScene(mainStageScene); - - setMinWidth(MIN_WIDTH); - setMinHeight(MIN_HEIGHT); - setHeight(HEIGHT); - getIcons().add(menuRobbiImage); - - snackbarController = new SnackbarController(vBox); - mainStageController = new MainStageController(this, buttonState); - simulationController = new SimulationController(this, territory); - territorySaveController = new TerritorySaveController(this); - examplesController = new ExamplesController(this); - languageController = new LanguageController(this); - - if (PropertiesLoader.isTutor()) - tutorController = new TutorController(this); - else - studenController = new StudentController(this); - - show(); - textArea.requestFocus(); - logger.info("Finished loading '{}'", program.getName()); - } - - /** - * Creates the contentPane in which the text-editor and the territoryPanel take - * place. - */ - private void createContentPane() { - logger.debug("Create content panel"); - - textArea = new MonacoFX(); - textArea.getEditor().getDocument().setText(program.getEditorContent()); - textArea.getEditor().setCurrentLanguage("java"); - textArea.setMinWidth(250); - - territoryPanel = new TerritoryPanel(this.territory, this.buttonState, this); - - territoryScrollPane = new ScrollPane(territoryPanel); - territoryScrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED); - territoryScrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED); - territoryScrollPane.viewportBoundsProperty() - .addListener((observable, oldValue, newValue) -> territoryPanel.center(newValue));// credits: Dibo - - splitPane = new SplitPane(textArea, territoryScrollPane); - } + /** + * Constant for the minimum width of the stage. + */ + public static final int MIN_WIDTH = 500; + /** + * Constant for the minimum height of the stage. + */ + public static final int MIN_HEIGHT = 200; + /** + * Constant for the default width of the stage.
+ * Currently not in use. + */ + public static final int WIDTH = 1200; + /** + * Constant for the default height of the stage. + */ + public static final int HEIGHT = 450; + /** + * Constant Image for open icon. + */ + public static final Image openImage; + /** + * Constant Image for new icon. + */ + public static final Image newImage; + /** + * Constant Image for save icon. + */ + public static final Image saveImage; + /** + * Constant Image for compile icon. + */ + public static final Image compileImage; + /** + * Constant Image for print icon. + */ + public static final Image printImage; + /** + * Constant Image for terrain icon. (Used for changeSize button) + */ + public static final Image terrainImage; + /** + * Constant Image for robbi icon. + */ + public static final Image menuRobbiImage; + /** + * Constant Image for hollow icon. + */ + public static final Image menuHollowImage; + /** + * Constant Image for pileOfScrap icon. + */ + public static final Image menuPileOfScrapImage; + /** + * Constant Image for stockpile icon. + */ + public static final Image menuStockpileImage; + /** + * Constant Image for accu icon. + */ + public static final Image menuAccuImage; + /** + * Constant Image for screw icon. + */ + public static final Image menuScrewImage; + /** + * Constant Image for the nut icon. + */ + public static final Image menuNutImage; + /** + * Constant Image for delete icon. + */ + public static final Image menuDeleteImage; + /** + * Constant Image for reset icon. + */ + public static final Image resetImage; + /** + * Constant Image for simulation start/resume icon. + */ + public static final Image menuStartImage; + /** + * Constant Image for simulation pause icon. + */ + public static final Image menuPauseImage; + /** + * Constant Image for simulation stop icon. + */ + public static final Image menuStopImage; + /** + * Constant Image for RobbiMove icon. + */ + public static final Image robbiMove; + /** + * Constant Image for RobbiTurnLeft icon. + */ + public static final Image robbiTurnLeft; + /** + * Constant Image for RobbiPut icon. + */ + public static final Image robbiPut; + /** + * Constant Image for RobbiTake icon. + */ + public static final Image robbiTake; + + static { + logger.debug("Loading stage images"); + newImage = new Image(String.valueOf(MainStage.class.getResource("/img/New24.gif"))); + saveImage = new Image(String.valueOf(MainStage.class.getResource("/img/Save24.gif"))); + openImage = new Image(String.valueOf(MainStage.class.getResource("/img/Open24.gif"))); + compileImage = new Image(String.valueOf(MainStage.class.getResource("/img/Compile24.gif"))); + printImage = new Image(String.valueOf(MainStage.class.getResource("/img/Print24.gif"))); + terrainImage = new Image(String.valueOf(MainStage.class.getResource("/img/Terrain24.gif"))); + menuRobbiImage = new Image(String.valueOf(MainStage.class.getResource("/img/Robbi24.png"))); + menuHollowImage = new Image(String.valueOf(MainStage.class.getResource("/img/Hollow24.png"))); + menuPileOfScrapImage = new Image(String.valueOf(MainStage.class.getResource("/img/PileOfScrap24.png"))); + menuStockpileImage = new Image(String.valueOf(MainStage.class.getResource("/img/Stockpile24.png"))); + menuAccuImage = new Image(String.valueOf(MainStage.class.getResource("/img/Accu24.png"))); + menuScrewImage = new Image(String.valueOf(MainStage.class.getResource("/img/Screw24.png"))); + menuNutImage = new Image(String.valueOf(MainStage.class.getResource("/img/Nut24.png"))); + menuDeleteImage = new Image(String.valueOf(MainStage.class.getResource("/img/Delete24.gif"))); + + resetImage = new Image(String.valueOf(MainStage.class.getResource("/img/reset24.png"))); + menuStartImage = new Image(String.valueOf(MainStage.class.getResource("/img/Play24.gif"))); + menuPauseImage = new Image(String.valueOf(MainStage.class.getResource("/img/Pause24.gif"))); + menuStopImage = new Image(String.valueOf(MainStage.class.getResource("/img/Stop24.gif"))); + + robbiMove = new Image(String.valueOf(MainStage.class.getResource("/img/RobbiMove24.png"))); + robbiTurnLeft = new Image(String.valueOf(MainStage.class.getResource("/img/RobbiLeft24.png"))); + robbiPut = new Image(String.valueOf(MainStage.class.getResource("/img/RobbiPut24.png"))); + robbiTake = new Image(String.valueOf(MainStage.class.getResource("/img/RobbiTake24.png"))); + } + + private final Territory territory; + private final ButtonState buttonState; + // controllers + private final MainStageController mainStageController; + private final SimulationController simulationController; + private final TerritorySaveController territorySaveController; + private final ExamplesController examplesController; + private StudentController studentController; + private TutorController tutorController; + private final LanguageController languageController; + private final NotificationController notificationController; + private final Program program; + private final MenuBar menubar; + private final Toolbar toolbar; + // Content Pane + private MonacoFX textArea; + private ScrollPane territoryScrollPane; + private TerritoryPanel territoryPanel; + private SplitPane splitPane; + private final Scene mainStageScene; + + /** + * Constructor for the MainStage. It creates a mainStage for the given Program + * and loads and creates all needed Gui elements and controller.
+ * This is the place, where the territory and the buttonState are created + * + * @param program the program this mainStage is for + */ + public MainStage(Program program) { + this.program = program; + + territory = new Territory(); + buttonState = new ButtonState(); + + menubar = new MenuBar(); + toolbar = new Toolbar(menubar); + createContentPane(); + + VBox.setVgrow(splitPane, Priority.ALWAYS); + var vBox = new VBox(menubar, toolbar, splitPane); + + notificationController = new NotificationController(vBox); + + mainStageScene = new Scene(notificationController.getScene()); + setScene(mainStageScene); + + setMinWidth(MIN_WIDTH); + setMinHeight(MIN_HEIGHT); + setHeight(HEIGHT); + getIcons().add(menuRobbiImage); + + + mainStageController = new MainStageController(this, buttonState); + simulationController = new SimulationController(this, territory); + territorySaveController = new TerritorySaveController(this); + examplesController = new ExamplesController(this); + languageController = new LanguageController(this); + + if (PropertiesLoader.isTutor()) + tutorController = new TutorController(this); + else + studentController = new StudentController(this); + + show(); + textArea.requestFocus(); + logger.info("Finished loading '{}'", program.getName()); + notificationController.showMessage(2500, "Snackbar.message.startup"); + } + + /** + * Creates the contentPane in which the text-editor and the territoryPanel take + * place. + */ + private void createContentPane() { + logger.debug("Create content panel"); + + textArea = new MonacoFX(); + textArea.getEditor().getDocument().setText(program.getEditorContent()); + textArea.getEditor().setCurrentLanguage("java"); + textArea.setMinWidth(250); + + territoryPanel = new TerritoryPanel(this.territory, this.buttonState, this); + + territoryScrollPane = new ScrollPane(territoryPanel); + territoryScrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED); + territoryScrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED); + territoryScrollPane.viewportBoundsProperty() + .addListener((observable, oldValue, newValue) -> territoryPanel.center(newValue));// credits: Dibo + + splitPane = new SplitPane(textArea, territoryScrollPane); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/view/MenuBar.java b/src/main/java/com/JayPi4c/RobbiSimulator/view/MenuBar.java index 943ebe0..7dc426b 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/view/MenuBar.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/view/MenuBar.java @@ -1,13 +1,7 @@ package com.JayPi4c.RobbiSimulator.view; import com.JayPi4c.RobbiSimulator.utils.PropertiesLoader; - -import javafx.scene.control.CheckMenuItem; -import javafx.scene.control.Menu; -import javafx.scene.control.MenuItem; -import javafx.scene.control.RadioMenuItem; -import javafx.scene.control.SeparatorMenuItem; -import javafx.scene.control.ToggleGroup; +import javafx.scene.control.*; import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; @@ -16,310 +10,308 @@ import lombok.extern.slf4j.Slf4j; /** - * * The menuBar holds all possible actions for the application in one menuBar. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Slf4j @Getter public class MenuBar extends javafx.scene.control.MenuBar { - // Menu Bar - // editor Menu - private MenuItem newEditorMenuItem; - private MenuItem saveEditorMenuItem; - private MenuItem openEditorMenuItem; - private MenuItem formatSourceCodeMenuItem; - private MenuItem compileEditorMenuItem; - private MenuItem printEditorMenuItem; - - private MenuItem quitEditorMenuItem; - private Menu editorMenu; - - // territory Menu - private MenuItem saveXMLTerritoryMenuItem; - private MenuItem saveJAXBTerritoryMenuItem; - private MenuItem saveSerialTerritoryMenuItem; - - private Menu saveTerritoryMenu; - private MenuItem loadXMLTerritoryMenuItem; - private MenuItem loadJAXBTerritoryMenuItem; - private MenuItem loadSerialTerritoryMenuItem; - - private Menu loadTerritoryMenu; - private Menu saveAsPicMenu; - private MenuItem saveAsPNGMenuItem; - private MenuItem saveAsGifMenuItem; - private MenuItem printTerritoryMenuItem; - private MenuItem changeSizeTerritoryMenuItem; - private RadioMenuItem placeRobbiTerritoryRadioMenuItem; - private RadioMenuItem placeHollowTerritoryRadioMenuItem; - private RadioMenuItem placePileOfScrapTerritoryRadioMenuItem; - private RadioMenuItem placeStockpileTerritoryRadioMenuItem; - private RadioMenuItem placeAccuTerritoryRadioMenuItem; - private RadioMenuItem placeScrewTerritoryRadioMenuItem; - private RadioMenuItem placeNutTerritoryRadioMenuItem; - private RadioMenuItem deleteFieldRadioMenuItem; - private Menu territoryMenu; - private ToggleGroup placeGroupTerritoryMenu; - - // robbi Menu - private MenuItem itemPresentMenuItem; - private MenuItem isStockpileMenuItem; - private MenuItem hollowAheadMenuItem; - private MenuItem pileOfScrapAheadMenuItem; - private MenuItem isBagFullMenuItem; - private MenuItem pushPileOfScrapMenuItem; - private MenuItem moveMenuItem; - private MenuItem turnLeftMenuItem; - private MenuItem putMenuItem; - private MenuItem takeMenuItem; - private Menu robbiMenu; - - // simulation Menu - private MenuItem resetMenuItem; - private MenuItem startMenuItem; - private MenuItem pauseMenuItem; - private MenuItem stopMenuItem; - private Menu simulationMenu; - - // examples Menu - private MenuItem loadExampleMenuItem; - private MenuItem saveExampleMenuItem; - private Menu examplesMenu; - - // window Menu - private Menu languageMenu; - private MenuItem englishLanguageMenuItem; - private MenuItem germanLanguageMenuItem; - private CheckMenuItem changeCursorMenuItem; - private CheckMenuItem darkModeMenuItem; - private CheckMenuItem enableSoundsMenuItem; - private MenuItem infoMenuItem; - private MenuItem libraryMenuItem; - private Menu windowMenu; - - // tutor Menu - private Menu tutorMenu; - private MenuItem sendRequestMenuItem; - private MenuItem receiveAnswerMenuItem; - private MenuItem loadRequestMenuItem; - private MenuItem saveAnswerMenuItem; - - /** - * Creates a new MenuBar - */ - public MenuBar() { - createMenuBar(); - getMenus().addAll(editorMenu, territoryMenu, robbiMenu, simulationMenu, examplesMenu, tutorMenu, windowMenu); - } - - /** - * Creates the editor-related menu-bar elements. - */ - private void createEditor() { - logger.debug("Create editor entry for menubar"); - newEditorMenuItem = new MenuItem(); - newEditorMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.N, KeyCombination.CONTROL_DOWN)); - newEditorMenuItem.setMnemonicParsing(true); - newEditorMenuItem.setGraphic(new ImageView(MainStage.newImage)); - saveEditorMenuItem = new MenuItem(); - saveEditorMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN)); - saveEditorMenuItem.setGraphic(new ImageView(MainStage.saveImage)); - openEditorMenuItem = new MenuItem(); - openEditorMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.O, KeyCombination.CONTROL_DOWN)); - openEditorMenuItem.setGraphic(new ImageView(MainStage.openImage)); - - formatSourceCodeMenuItem = new MenuItem(); - formatSourceCodeMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.T, KeyCombination.CONTROL_DOWN)); // T - // for - // tidy - - compileEditorMenuItem = new MenuItem(); - compileEditorMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.K, KeyCombination.CONTROL_DOWN)); - compileEditorMenuItem.setGraphic(new ImageView(MainStage.compileImage)); - - printEditorMenuItem = new MenuItem(); - printEditorMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.P, KeyCombination.CONTROL_DOWN)); - printEditorMenuItem.setGraphic(new ImageView(MainStage.printImage)); - quitEditorMenuItem = new MenuItem(); - quitEditorMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN)); - editorMenu = new Menu("", null, newEditorMenuItem, openEditorMenuItem, saveEditorMenuItem, - new SeparatorMenuItem(), formatSourceCodeMenuItem, compileEditorMenuItem, printEditorMenuItem, - new SeparatorMenuItem(), quitEditorMenuItem); - editorMenu.setMnemonicParsing(true); - } - - /** - * Creates the territory-related menu-bar elements. - */ - private void createTerritory() { - logger.debug("Create territory entry for menubar"); - saveXMLTerritoryMenuItem = new MenuItem(); - saveJAXBTerritoryMenuItem = new MenuItem(); - saveSerialTerritoryMenuItem = new MenuItem(); - - saveTerritoryMenu = new Menu("", null, saveXMLTerritoryMenuItem, saveJAXBTerritoryMenuItem, - saveSerialTerritoryMenuItem); - loadXMLTerritoryMenuItem = new MenuItem(); - loadJAXBTerritoryMenuItem = new MenuItem(); - loadSerialTerritoryMenuItem = new MenuItem(); - loadTerritoryMenu = new Menu("", null, loadXMLTerritoryMenuItem, loadJAXBTerritoryMenuItem, - loadSerialTerritoryMenuItem); - - saveAsPNGMenuItem = new MenuItem(); - saveAsGifMenuItem = new MenuItem(); - saveAsPicMenu = new Menu("", null, saveAsPNGMenuItem, saveAsGifMenuItem); - - printTerritoryMenuItem = new MenuItem(); - changeSizeTerritoryMenuItem = new MenuItem(); - - placeGroupTerritoryMenu = new ToggleGroup(); - - placeRobbiTerritoryRadioMenuItem = new RadioMenuItem(); - placeRobbiTerritoryRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); - - placeHollowTerritoryRadioMenuItem = new RadioMenuItem(); - placeHollowTerritoryRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); - - placePileOfScrapTerritoryRadioMenuItem = new RadioMenuItem(); - placePileOfScrapTerritoryRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); - - placeStockpileTerritoryRadioMenuItem = new RadioMenuItem(); - placeStockpileTerritoryRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); - - placeAccuTerritoryRadioMenuItem = new RadioMenuItem(); - placeAccuTerritoryRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); - - placeScrewTerritoryRadioMenuItem = new RadioMenuItem(); - placeScrewTerritoryRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); - - placeNutTerritoryRadioMenuItem = new RadioMenuItem(); - placeNutTerritoryRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); - - deleteFieldRadioMenuItem = new RadioMenuItem(); - deleteFieldRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); - - territoryMenu = new Menu("", null, saveTerritoryMenu, loadTerritoryMenu, saveAsPicMenu, printTerritoryMenuItem, - changeSizeTerritoryMenuItem, new SeparatorMenuItem(), placeRobbiTerritoryRadioMenuItem, - placeHollowTerritoryRadioMenuItem, placePileOfScrapTerritoryRadioMenuItem, - placeStockpileTerritoryRadioMenuItem, placeAccuTerritoryRadioMenuItem, placeScrewTerritoryRadioMenuItem, - placeNutTerritoryRadioMenuItem, deleteFieldRadioMenuItem); - } - - /** - * Creates the robbi-related menu-bar elements. - */ - private void createRobbi() { - logger.debug("Create Robbi entry for menubar"); - territoryMenu.setMnemonicParsing(true); - itemPresentMenuItem = new MenuItem(); - isStockpileMenuItem = new MenuItem(); - hollowAheadMenuItem = new MenuItem(); - pileOfScrapAheadMenuItem = new MenuItem(); - isBagFullMenuItem = new MenuItem(); - pushPileOfScrapMenuItem = new MenuItem(); - moveMenuItem = new MenuItem(); - moveMenuItem.setAccelerator( - new KeyCodeCombination(KeyCode.V, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN)); - turnLeftMenuItem = new MenuItem(); - turnLeftMenuItem.setAccelerator( - new KeyCodeCombination(KeyCode.T, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN)); - - putMenuItem = new MenuItem(); - putMenuItem.setAccelerator( - new KeyCodeCombination(KeyCode.L, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN)); - takeMenuItem = new MenuItem(); - takeMenuItem.setAccelerator( - new KeyCodeCombination(KeyCode.N, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN)); - robbiMenu = new Menu("", null, moveMenuItem, turnLeftMenuItem, putMenuItem, takeMenuItem, - pushPileOfScrapMenuItem, itemPresentMenuItem, isStockpileMenuItem, hollowAheadMenuItem, - pileOfScrapAheadMenuItem, isBagFullMenuItem); - robbiMenu.setMnemonicParsing(true); - } - - /** - * Creates the simulation-related menu-bar elements. - */ - private void createSimulation() { - logger.debug("Create simulation entry for menubar"); - - resetMenuItem = new MenuItem(); - resetMenuItem.setGraphic(new ImageView(MainStage.resetImage)); - startMenuItem = new MenuItem(); - startMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.F11, KeyCombination.CONTROL_DOWN)); - startMenuItem.setGraphic(new ImageView(MainStage.menuStartImage)); - pauseMenuItem = new MenuItem(); - pauseMenuItem.setGraphic(new ImageView(MainStage.menuPauseImage)); - stopMenuItem = new MenuItem(); - stopMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.F12, KeyCombination.CONTROL_DOWN)); - stopMenuItem.setGraphic(new ImageView(MainStage.menuStopImage)); - simulationMenu = new Menu("", null, resetMenuItem, startMenuItem, pauseMenuItem, stopMenuItem); - simulationMenu.setMnemonicParsing(true); - } - - /** - * Creates the examples-related menu-bar elements - */ - private void createExamplesMenu() { - saveExampleMenuItem = new MenuItem(); - loadExampleMenuItem = new MenuItem(); - examplesMenu = new Menu("", null, saveExampleMenuItem, loadExampleMenuItem); - } - - /** - * Creates the tutor-related menu-bar elements - */ - private void createTutorMenu() { - if (PropertiesLoader.isTutor()) { - loadRequestMenuItem = new MenuItem(); - saveAnswerMenuItem = new MenuItem(); - - tutorMenu = new Menu("", null, loadRequestMenuItem, saveAnswerMenuItem); - } else { - sendRequestMenuItem = new MenuItem(); - receiveAnswerMenuItem = new MenuItem(); - - tutorMenu = new Menu("", null, sendRequestMenuItem, receiveAnswerMenuItem); - } - } - - /** - * Creates the window-related menu-bar elements. - */ - private void createWindowMenu() { - englishLanguageMenuItem = new MenuItem(); - germanLanguageMenuItem = new MenuItem(); - languageMenu = new Menu("", null, englishLanguageMenuItem, germanLanguageMenuItem); - - changeCursorMenuItem = new CheckMenuItem(); - // https://stackoverflow.com/a/49159612/13670629 - darkModeMenuItem = new CheckMenuItem(); - enableSoundsMenuItem = new CheckMenuItem(); - - infoMenuItem = new MenuItem(); - libraryMenuItem = new MenuItem(); - - windowMenu = new Menu("", null, languageMenu, changeCursorMenuItem, darkModeMenuItem, enableSoundsMenuItem, - new SeparatorMenuItem(), infoMenuItem, libraryMenuItem); - } - - /** - * Creates the entire menuBar. - */ - private void createMenuBar() { - logger.debug("Create menubar"); - - createEditor(); - createTerritory(); - createRobbi(); - createSimulation(); - createExamplesMenu(); - createTutorMenu(); - createWindowMenu(); - - } + // Menu Bar + // editor Menu + private MenuItem newEditorMenuItem; + private MenuItem saveEditorMenuItem; + private MenuItem openEditorMenuItem; + private MenuItem formatSourceCodeMenuItem; + private MenuItem compileEditorMenuItem; + private MenuItem printEditorMenuItem; + + private MenuItem quitEditorMenuItem; + private Menu editorMenu; + + // territory Menu + private MenuItem saveXMLTerritoryMenuItem; + private MenuItem saveJAXBTerritoryMenuItem; + private MenuItem saveSerialTerritoryMenuItem; + + private Menu saveTerritoryMenu; + private MenuItem loadXMLTerritoryMenuItem; + private MenuItem loadJAXBTerritoryMenuItem; + private MenuItem loadSerialTerritoryMenuItem; + + private Menu loadTerritoryMenu; + private Menu saveAsPicMenu; + private MenuItem saveAsPNGMenuItem; + private MenuItem saveAsGifMenuItem; + private MenuItem printTerritoryMenuItem; + private MenuItem changeSizeTerritoryMenuItem; + private RadioMenuItem placeRobbiTerritoryRadioMenuItem; + private RadioMenuItem placeHollowTerritoryRadioMenuItem; + private RadioMenuItem placePileOfScrapTerritoryRadioMenuItem; + private RadioMenuItem placeStockpileTerritoryRadioMenuItem; + private RadioMenuItem placeAccuTerritoryRadioMenuItem; + private RadioMenuItem placeScrewTerritoryRadioMenuItem; + private RadioMenuItem placeNutTerritoryRadioMenuItem; + private RadioMenuItem deleteFieldRadioMenuItem; + private Menu territoryMenu; + private ToggleGroup placeGroupTerritoryMenu; + + // robbi Menu + private MenuItem itemPresentMenuItem; + private MenuItem isStockpileMenuItem; + private MenuItem hollowAheadMenuItem; + private MenuItem pileOfScrapAheadMenuItem; + private MenuItem isBagFullMenuItem; + private MenuItem pushPileOfScrapMenuItem; + private MenuItem moveMenuItem; + private MenuItem turnLeftMenuItem; + private MenuItem putMenuItem; + private MenuItem takeMenuItem; + private Menu robbiMenu; + + // simulation Menu + private MenuItem resetMenuItem; + private MenuItem startMenuItem; + private MenuItem pauseMenuItem; + private MenuItem stopMenuItem; + private Menu simulationMenu; + + // examples Menu + private MenuItem loadExampleMenuItem; + private MenuItem saveExampleMenuItem; + private Menu examplesMenu; + + // window Menu + private Menu languageMenu; + private MenuItem englishLanguageMenuItem; + private MenuItem germanLanguageMenuItem; + private CheckMenuItem changeCursorMenuItem; + private CheckMenuItem darkModeMenuItem; + private CheckMenuItem enableSoundsMenuItem; + private MenuItem infoMenuItem; + private MenuItem libraryMenuItem; + private Menu windowMenu; + + // tutor Menu + private Menu tutorMenu; + private MenuItem sendRequestMenuItem; + private MenuItem receiveAnswerMenuItem; + private MenuItem loadRequestMenuItem; + private MenuItem saveAnswerMenuItem; + + /** + * Creates a new MenuBar + */ + public MenuBar() { + createMenuBar(); + getMenus().addAll(editorMenu, territoryMenu, robbiMenu, simulationMenu, examplesMenu, tutorMenu, windowMenu); + } + + /** + * Creates the editor-related menu-bar elements. + */ + private void createEditor() { + logger.debug("Create editor entry for menubar"); + newEditorMenuItem = new MenuItem(); + newEditorMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.N, KeyCombination.CONTROL_DOWN)); + newEditorMenuItem.setMnemonicParsing(true); + newEditorMenuItem.setGraphic(new ImageView(MainStage.newImage)); + saveEditorMenuItem = new MenuItem(); + saveEditorMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN)); + saveEditorMenuItem.setGraphic(new ImageView(MainStage.saveImage)); + openEditorMenuItem = new MenuItem(); + openEditorMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.O, KeyCombination.CONTROL_DOWN)); + openEditorMenuItem.setGraphic(new ImageView(MainStage.openImage)); + + formatSourceCodeMenuItem = new MenuItem(); + formatSourceCodeMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.T, KeyCombination.CONTROL_DOWN)); // T + // for + // tidy + + compileEditorMenuItem = new MenuItem(); + compileEditorMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.K, KeyCombination.CONTROL_DOWN)); + compileEditorMenuItem.setGraphic(new ImageView(MainStage.compileImage)); + + printEditorMenuItem = new MenuItem(); + printEditorMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.P, KeyCombination.CONTROL_DOWN)); + printEditorMenuItem.setGraphic(new ImageView(MainStage.printImage)); + quitEditorMenuItem = new MenuItem(); + quitEditorMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN)); + editorMenu = new Menu("", null, newEditorMenuItem, openEditorMenuItem, saveEditorMenuItem, + new SeparatorMenuItem(), formatSourceCodeMenuItem, compileEditorMenuItem, printEditorMenuItem, + new SeparatorMenuItem(), quitEditorMenuItem); + editorMenu.setMnemonicParsing(true); + } + + /** + * Creates the territory-related menu-bar elements. + */ + private void createTerritory() { + logger.debug("Create territory entry for menubar"); + saveXMLTerritoryMenuItem = new MenuItem(); + saveJAXBTerritoryMenuItem = new MenuItem(); + saveSerialTerritoryMenuItem = new MenuItem(); + + saveTerritoryMenu = new Menu("", null, saveXMLTerritoryMenuItem, saveJAXBTerritoryMenuItem, + saveSerialTerritoryMenuItem); + loadXMLTerritoryMenuItem = new MenuItem(); + loadJAXBTerritoryMenuItem = new MenuItem(); + loadSerialTerritoryMenuItem = new MenuItem(); + loadTerritoryMenu = new Menu("", null, loadXMLTerritoryMenuItem, loadJAXBTerritoryMenuItem, + loadSerialTerritoryMenuItem); + + saveAsPNGMenuItem = new MenuItem(); + saveAsGifMenuItem = new MenuItem(); + saveAsPicMenu = new Menu("", null, saveAsPNGMenuItem, saveAsGifMenuItem); + + printTerritoryMenuItem = new MenuItem(); + changeSizeTerritoryMenuItem = new MenuItem(); + + placeGroupTerritoryMenu = new ToggleGroup(); + + placeRobbiTerritoryRadioMenuItem = new RadioMenuItem(); + placeRobbiTerritoryRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); + + placeHollowTerritoryRadioMenuItem = new RadioMenuItem(); + placeHollowTerritoryRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); + + placePileOfScrapTerritoryRadioMenuItem = new RadioMenuItem(); + placePileOfScrapTerritoryRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); + + placeStockpileTerritoryRadioMenuItem = new RadioMenuItem(); + placeStockpileTerritoryRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); + + placeAccuTerritoryRadioMenuItem = new RadioMenuItem(); + placeAccuTerritoryRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); + + placeScrewTerritoryRadioMenuItem = new RadioMenuItem(); + placeScrewTerritoryRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); + + placeNutTerritoryRadioMenuItem = new RadioMenuItem(); + placeNutTerritoryRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); + + deleteFieldRadioMenuItem = new RadioMenuItem(); + deleteFieldRadioMenuItem.setToggleGroup(placeGroupTerritoryMenu); + + territoryMenu = new Menu("", null, saveTerritoryMenu, loadTerritoryMenu, saveAsPicMenu, printTerritoryMenuItem, + changeSizeTerritoryMenuItem, new SeparatorMenuItem(), placeRobbiTerritoryRadioMenuItem, + placeHollowTerritoryRadioMenuItem, placePileOfScrapTerritoryRadioMenuItem, + placeStockpileTerritoryRadioMenuItem, placeAccuTerritoryRadioMenuItem, placeScrewTerritoryRadioMenuItem, + placeNutTerritoryRadioMenuItem, deleteFieldRadioMenuItem); + } + + /** + * Creates the robbi-related menu-bar elements. + */ + private void createRobbi() { + logger.debug("Create Robbi entry for menubar"); + territoryMenu.setMnemonicParsing(true); + itemPresentMenuItem = new MenuItem(); + isStockpileMenuItem = new MenuItem(); + hollowAheadMenuItem = new MenuItem(); + pileOfScrapAheadMenuItem = new MenuItem(); + isBagFullMenuItem = new MenuItem(); + pushPileOfScrapMenuItem = new MenuItem(); + moveMenuItem = new MenuItem(); + moveMenuItem.setAccelerator( + new KeyCodeCombination(KeyCode.V, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN)); + turnLeftMenuItem = new MenuItem(); + turnLeftMenuItem.setAccelerator( + new KeyCodeCombination(KeyCode.T, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN)); + + putMenuItem = new MenuItem(); + putMenuItem.setAccelerator( + new KeyCodeCombination(KeyCode.L, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN)); + takeMenuItem = new MenuItem(); + takeMenuItem.setAccelerator( + new KeyCodeCombination(KeyCode.N, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN)); + robbiMenu = new Menu("", null, moveMenuItem, turnLeftMenuItem, putMenuItem, takeMenuItem, + pushPileOfScrapMenuItem, itemPresentMenuItem, isStockpileMenuItem, hollowAheadMenuItem, + pileOfScrapAheadMenuItem, isBagFullMenuItem); + robbiMenu.setMnemonicParsing(true); + } + + /** + * Creates the simulation-related menu-bar elements. + */ + private void createSimulation() { + logger.debug("Create simulation entry for menubar"); + + resetMenuItem = new MenuItem(); + resetMenuItem.setGraphic(new ImageView(MainStage.resetImage)); + startMenuItem = new MenuItem(); + startMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.F11, KeyCombination.CONTROL_DOWN)); + startMenuItem.setGraphic(new ImageView(MainStage.menuStartImage)); + pauseMenuItem = new MenuItem(); + pauseMenuItem.setGraphic(new ImageView(MainStage.menuPauseImage)); + stopMenuItem = new MenuItem(); + stopMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.F12, KeyCombination.CONTROL_DOWN)); + stopMenuItem.setGraphic(new ImageView(MainStage.menuStopImage)); + simulationMenu = new Menu("", null, resetMenuItem, startMenuItem, pauseMenuItem, stopMenuItem); + simulationMenu.setMnemonicParsing(true); + } + + /** + * Creates the examples-related menu-bar elements + */ + private void createExamplesMenu() { + saveExampleMenuItem = new MenuItem(); + loadExampleMenuItem = new MenuItem(); + examplesMenu = new Menu("", null, saveExampleMenuItem, loadExampleMenuItem); + } + + /** + * Creates the tutor-related menu-bar elements + */ + private void createTutorMenu() { + if (PropertiesLoader.isTutor()) { + loadRequestMenuItem = new MenuItem(); + saveAnswerMenuItem = new MenuItem(); + + tutorMenu = new Menu("", null, loadRequestMenuItem, saveAnswerMenuItem); + } else { + sendRequestMenuItem = new MenuItem(); + receiveAnswerMenuItem = new MenuItem(); + + tutorMenu = new Menu("", null, sendRequestMenuItem, receiveAnswerMenuItem); + } + } + + /** + * Creates the window-related menu-bar elements. + */ + private void createWindowMenu() { + englishLanguageMenuItem = new MenuItem(); + germanLanguageMenuItem = new MenuItem(); + languageMenu = new Menu("", null, englishLanguageMenuItem, germanLanguageMenuItem); + + changeCursorMenuItem = new CheckMenuItem(); + // https://stackoverflow.com/a/49159612/13670629 + darkModeMenuItem = new CheckMenuItem(); + enableSoundsMenuItem = new CheckMenuItem(); + + infoMenuItem = new MenuItem(); + libraryMenuItem = new MenuItem(); + + windowMenu = new Menu("", null, languageMenu, changeCursorMenuItem, darkModeMenuItem, enableSoundsMenuItem, + new SeparatorMenuItem(), infoMenuItem, libraryMenuItem); + } + + /** + * Creates the entire menuBar. + */ + private void createMenuBar() { + logger.debug("Create menubar"); + + createEditor(); + createTerritory(); + createRobbi(); + createSimulation(); + createExamplesMenu(); + createTutorMenu(); + createWindowMenu(); + + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/view/RobbiContextMenu.java b/src/main/java/com/JayPi4c/RobbiSimulator/view/RobbiContextMenu.java index eaaf00b..38b9222 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/view/RobbiContextMenu.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/view/RobbiContextMenu.java @@ -1,201 +1,193 @@ package com.JayPi4c.RobbiSimulator.view; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Parameter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import com.JayPi4c.RobbiSimulator.controller.MethodHandler; import com.JayPi4c.RobbiSimulator.model.Robbi; import com.JayPi4c.RobbiSimulator.model.Territory; import com.JayPi4c.RobbiSimulator.utils.I18nUtils; import com.JayPi4c.RobbiSimulator.utils.annotations.Default; import com.JayPi4c.RobbiSimulator.utils.annotations.Invisible; +import javafx.scene.control.*; -import javafx.scene.control.ContextMenu; -import javafx.scene.control.CustomMenuItem; -import javafx.scene.control.Label; -import javafx.scene.control.MenuItem; -import javafx.scene.control.SeparatorMenuItem; -import javafx.scene.control.Tooltip; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.util.ArrayList; +import java.util.List; /** * Class to for the ContextMenu which opens, when clicked on robbi in the * simulator. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ public class RobbiContextMenu extends ContextMenu { - private static final String EDITOR_CONTEXTMENU_TOOLTIP = "Editor.contextMenu.tooltip"; - - /** - * Constructor to create a new RobbiContextMenu. It fills itself with all - * methods that are provided by robbi and allows to run a particular method on - * its own. - * - * @param territory the territory this contextMenu is for - * @param parent the parent window in order to display the alerts relative to - * the calling window - */ - public RobbiContextMenu(Territory territory, MainStage parent) { - - for (Method method : getDefaultMethods()) { - MenuItem item = getMenuItem(method); - - // if method needs arguments and no Parameterized Annotation is set -> disable - if (method.getParameterCount() != 0) - item.setDisable(true); - else - // get Annotation parameters - item.setOnAction(new MethodHandler(method, territory, parent)); - - getItems().add(item); - } - - Method[] methods = getCustomMethods(territory.getRobbi()); - if (methods.length > 0) { - getItems().add(new SeparatorMenuItem()); - - for (Method method : methods) { - CustomMenuItem item = getMenuItem(method); - // if method needs arguments and no parameterized Annotation is set -> disable - if (method.getParameterCount() != 0 && !hasCorrectDefaultAnnotations(method)) { - item.setDisable(true); - // https://stackoverflow.com/a/43053529/13670629 - Tooltip tooltip = I18nUtils.createTooltip(EDITOR_CONTEXTMENU_TOOLTIP); - Tooltip.install(item.getContent(), tooltip); - - } else - item.setOnAction(new MethodHandler(method, territory, parent)); - - getItems().add(item); - } - } - - } - - /** - * This method checks if the given method m has the @Default annotation for all - * parameters set. - * - * @param m the method to check for - * @return true if all parameters have the default Annotation, false otherwise - */ - private boolean hasCorrectDefaultAnnotations(Method m) { - if (m.getParameterCount() != m.getAnnotatedParameterTypes().length) - return false; - - for (Parameter parameter : m.getParameters()) { - boolean valid = false; - for (Annotation anno : parameter.getAnnotations()) { - if (anno instanceof Default) { - valid = true; - break; - } - } - if (!valid) - return false; - } - return true; - } - - /** - * Creates the MenuItem for the given Method.
- * MenuItems will look like this:
- * foo(int, int)
- * bar(long = 7, int = 3) - * - * @param m the method to make a menuItem from - * @return a CustomMenuItem with the correct text - */ - private CustomMenuItem getMenuItem(Method m) { - StringBuilder bobTheBuilder = new StringBuilder(); - bobTheBuilder.append(m.getReturnType().toString()); - bobTheBuilder.append(" "); - bobTheBuilder.append(m.getName()); - bobTheBuilder.append("("); - for (Parameter parameter : m.getParameters()) { - bobTheBuilder.append(parameter.getType()); - List annos = Arrays.asList(parameter.getAnnotations()); - for (Annotation anno : annos) - if (anno instanceof Default a) { - bobTheBuilder.append(" = "); - bobTheBuilder.append(a.value()); - } - bobTheBuilder.append(", "); - } - if (m.getParameterCount() > 0) - bobTheBuilder.delete(bobTheBuilder.length() - 2, bobTheBuilder.length()); - bobTheBuilder.append(")"); - return new CustomMenuItem(new Label(bobTheBuilder.toString())); - } - - /** - * Get all methods that are part of the default implementation, that are - * visibile to the user. - * - * @return all methods that are part of the default implementation - */ - private Method[] getDefaultMethods() { - List methods = new ArrayList<>(); - - for (Method m : Robbi.class.getDeclaredMethods()) { - if (Modifier.isPublic(m.getModifiers()) && !isMainMethod(m)) - methods.add(m); - } - return methods.toArray(Method[]::new); - } - - /** - * Get all methods that are implemented by the user and are not part of the - * default implementation of robbi. - * - * @param robbi the Robbi instance to get the custom methods from - * @return all methods that are not part of the default implementation - */ - private Method[] getCustomMethods(Robbi robbi) { - List methods = new ArrayList<>(); - // if robbi is custom class - if (Robbi.class != robbi.getClass()) { - for (Method m : robbi.getClass().getDeclaredMethods()) { - - int modifiers = m.getModifiers(); - - if (!Modifier.isPrivate(modifiers) && !Modifier.isStatic(modifiers) && !Modifier.isAbstract(modifiers) - && !isInvisible(m)) - methods.add(m); - } - } - - return methods.toArray(Method[]::new); - } - - /** - * Checks if a method has the invisible annotation. - * - * @param m the method to check for - * @return true if the Invisible annotation is set, false otherwise - */ - private boolean isInvisible(Method m) { - for (Annotation anno : m.getAnnotations()) - if (anno instanceof Invisible) - return true; - return false; - } - - /** - * Checks if the method is the main-Method. - * - * @param m method to check - * @return true if the name of the method equals "main", false otherwise - */ - private boolean isMainMethod(Method m) { - return m.getName().equals("main"); - } + private static final String EDITOR_CONTEXTMENU_TOOLTIP = "Editor.contextMenu.tooltip"; + + /** + * Constructor to create a new RobbiContextMenu. It fills itself with all + * methods that are provided by robbi and allows to run a particular method on + * its own. + * + * @param territory the territory this contextMenu is for + * @param parent the parent window in order to display the alerts relative to + * the calling window + */ + public RobbiContextMenu(Territory territory, MainStage parent) { + + for (Method method : getDefaultMethods()) { + MenuItem item = getMenuItem(method); + + // if method needs arguments and no Parameterized Annotation is set -> disable + if (method.getParameterCount() != 0) + item.setDisable(true); + else + // get Annotation parameters + item.setOnAction(new MethodHandler(method, territory, parent)); + + getItems().add(item); + } + + Method[] methods = getCustomMethods(territory.getRobbi()); + if (methods.length > 0) { + getItems().add(new SeparatorMenuItem()); + + for (Method method : methods) { + CustomMenuItem item = getMenuItem(method); + // if method needs arguments and no parameterized Annotation is set -> disable + if (method.getParameterCount() != 0 && !hasCorrectDefaultAnnotations(method)) { + item.setDisable(true); + // https://stackoverflow.com/a/43053529/13670629 + Tooltip tooltip = I18nUtils.createTooltip(EDITOR_CONTEXTMENU_TOOLTIP); + Tooltip.install(item.getContent(), tooltip); + + } else + item.setOnAction(new MethodHandler(method, territory, parent)); + + getItems().add(item); + } + } + + } + + /** + * This method checks if the given method m has the @Default annotation for all + * parameters set. + * + * @param m the method to check for + * @return true if all parameters have the default Annotation, false otherwise + */ + private boolean hasCorrectDefaultAnnotations(Method m) { + if (m.getParameterCount() != m.getAnnotatedParameterTypes().length) + return false; + + for (Parameter parameter : m.getParameters()) { + boolean valid = false; + for (Annotation anno : parameter.getAnnotations()) { + if (anno instanceof Default) { + valid = true; + break; + } + } + if (!valid) + return false; + } + return true; + } + + /** + * Creates the MenuItem for the given Method.
+ * MenuItems will look like this:
+ * foo(int, int)
+ * bar(long = 7, int = 3) + * + * @param m the method to make a menuItem from + * @return a CustomMenuItem with the correct text + */ + private CustomMenuItem getMenuItem(Method m) { + StringBuilder bobTheBuilder = new StringBuilder(); + bobTheBuilder.append(m.getReturnType()); + bobTheBuilder.append(" "); + bobTheBuilder.append(m.getName()); + bobTheBuilder.append("("); + for (Parameter parameter : m.getParameters()) { + bobTheBuilder.append(parameter.getType()); + Annotation[] annotations = parameter.getAnnotations(); + for (Annotation anno : annotations) + if (anno instanceof Default a) { + bobTheBuilder.append(" = "); + bobTheBuilder.append(a.value()); + } + bobTheBuilder.append(", "); + } + if (m.getParameterCount() > 0) + bobTheBuilder.delete(bobTheBuilder.length() - 2, bobTheBuilder.length()); + bobTheBuilder.append(")"); + return new CustomMenuItem(new Label(bobTheBuilder.toString())); + } + + /** + * Get all methods that are part of the default implementation, that are + * visible to the user. + * + * @return all methods that are part of the default implementation + */ + private Method[] getDefaultMethods() { + List methods = new ArrayList<>(); + + for (Method m : Robbi.class.getDeclaredMethods()) { + if (Modifier.isPublic(m.getModifiers()) && !isMainMethod(m)) + methods.add(m); + } + return methods.toArray(Method[]::new); + } + + /** + * Get all methods that are implemented by the user and are not part of the + * default implementation of robbi. + * + * @param robbi the Robbi instance to get the custom methods from + * @return all methods that are not part of the default implementation + */ + private Method[] getCustomMethods(Robbi robbi) { + List methods = new ArrayList<>(); + // if robbi is custom class + if (Robbi.class != robbi.getClass()) { + for (Method m : robbi.getClass().getDeclaredMethods()) { + + int modifiers = m.getModifiers(); + + if (!Modifier.isPrivate(modifiers) && !Modifier.isStatic(modifiers) && !Modifier.isAbstract(modifiers) + && !isInvisible(m)) + methods.add(m); + } + } + + return methods.toArray(Method[]::new); + } + + /** + * Checks if a method has the invisible annotation. + * + * @param m the method to check for + * @return true if the Invisible annotation is set, false otherwise + */ + private boolean isInvisible(Method m) { + for (Annotation anno : m.getAnnotations()) + if (anno instanceof Invisible) + return true; + return false; + } + + /** + * Checks if the method is the main-Method. + * + * @param m method to check + * @return true if the name of the method equals "main", false otherwise + */ + private boolean isMainMethod(Method m) { + return m.getName().equals("main"); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/view/TerritoryPanel.java b/src/main/java/com/JayPi4c/RobbiSimulator/view/TerritoryPanel.java index cb4f187..f05e430 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/view/TerritoryPanel.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/view/TerritoryPanel.java @@ -2,18 +2,9 @@ import com.JayPi4c.RobbiSimulator.controller.ButtonState; import com.JayPi4c.RobbiSimulator.controller.TerritoryEventHandler; -import com.JayPi4c.RobbiSimulator.model.Accu; -import com.JayPi4c.RobbiSimulator.model.Hollow; -import com.JayPi4c.RobbiSimulator.model.Item; -import com.JayPi4c.RobbiSimulator.model.Nut; -import com.JayPi4c.RobbiSimulator.model.PileOfScrap; -import com.JayPi4c.RobbiSimulator.model.Screw; -import com.JayPi4c.RobbiSimulator.model.Stockpile; -import com.JayPi4c.RobbiSimulator.model.Territory; -import com.JayPi4c.RobbiSimulator.model.Tile; +import com.JayPi4c.RobbiSimulator.model.*; import com.JayPi4c.RobbiSimulator.utils.Observable; import com.JayPi4c.RobbiSimulator.utils.Observer; - import javafx.application.Platform; import javafx.geometry.Bounds; import javafx.scene.canvas.Canvas; @@ -23,273 +14,267 @@ import lombok.extern.slf4j.Slf4j; /** - * * This class draws the territory on a panel to allow interacting with the * graphical interface. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Slf4j public class TerritoryPanel extends Canvas implements Observer { - private Territory territory; - - private static final Image[] tileImages = new Image[4]; - private static final Image[] itemImages = new Image[3]; - private static final Image robbiImage; - - private static final int TILE = 0; - private static final int STOCKPILE = 1; - private static final int HOLLOW = 2; - private static final int PILEOFSCRAP = 3; - private static final int NUT = 0; - private static final int SCREW = 1; - private static final int ACCU = 2; - - private static final int CELLSIZE = 32; - private static final int CELLSPACER = 1; - - // store current bounds to allow centering on updated territory size - private Bounds bounds; - - /** - * loading territory images - */ - static { - - logger.debug("Loading territory images"); - - robbiImage = new Image(String.valueOf(MainStage.class.getResource("/img/0Robbi32.png"))); - - tileImages[TILE] = new Image(String.valueOf(MainStage.class.getResource("/img/Tile32.png"))); - tileImages[STOCKPILE] = new Image(String.valueOf(MainStage.class.getResource("/img/Stockpile32.png"))); - tileImages[HOLLOW] = new Image(String.valueOf(MainStage.class.getResource("/img/Hollow32.png"))); - tileImages[PILEOFSCRAP] = new Image(String.valueOf(MainStage.class.getResource("/img/PileOfScrap32.png"))); - - itemImages[NUT] = new Image(String.valueOf(MainStage.class.getResource("/img/Nut32.png"))); - itemImages[SCREW] = new Image(String.valueOf(MainStage.class.getResource("/img/Screw32.png"))); - itemImages[ACCU] = new Image(String.valueOf(MainStage.class.getResource("/img/Accu32.png"))); - } - - /** - * Constructor to create a new territory panel for the given territory. - * - * @param territory the territory this panel is for - * @param buttonState the buttonState to be able to create a - * TerritoryEventHandler - * @param parent the parent Window to show alerts relative to the calling - * window - */ - public TerritoryPanel(Territory territory, ButtonState buttonState, MainStage parent) { - this.territory = territory; - this.territory.addObserver(this); - - TerritoryEventHandler eventHandler = new TerritoryEventHandler(territory, this, buttonState, parent); - this.setOnMousePressed(eventHandler); - this.setOnMouseDragged(eventHandler); - this.setOnMouseReleased(eventHandler); - - drawPanel(); - } - - /** - * Getter for the CELLSIZE. - * - * @return CELLSIZE value - */ - public static int getCellsize() { - return CELLSIZE; - } - - /** - * Getter for the CELLSPACER. - * - * @return CELLSPACER value - */ - public static int getCellspacer() { - return CELLSPACER; - } - - /** - * updates the size of the territory if the size has changed. Paints the - * territory afterwards. - */ - private void drawPanel() { - if (getWidth() != getTerritoryWidth()) - setWidth(getTerritoryWidth()); - if (getHeight() != getTerritoryHeight()) - setHeight(getTerritoryHeight()); - paintTerritory(); - - } - - /** - * Getter for the territory width. - * - * @return the width of the territory, calculated by number of cols, Cellsize - * and cellspacer - */ - private int getTerritoryWidth() { - return (territory.getNumCols()) * (CELLSIZE + CELLSPACER); - } - - /** - * Getter for the territory height. - * - * @return the height of the territory, calculated by number of rows, Cellsize - * and cellspacer - */ - private int getTerritoryHeight() { - return (territory.getNumRows()) * (CELLSIZE + CELLSPACER); - } - - /** - * Paint all graphics for the territory and the robbi on the GUI. - */ - private void paintTerritory() { - - GraphicsContext gc = getGraphicsContext2D(); - for (int i = 0; i < territory.getNumCols(); i++) { - - for (int j = 0; j < territory.getNumRows(); j++) { - Tile t = territory.getTile(i, j); - gc.drawImage(tileImages[TILE], getPos(i), getPos(j), CELLSIZE, CELLSIZE); - if (t instanceof Hollow) { - gc.drawImage(tileImages[HOLLOW], getPos(i), getPos(j), CELLSIZE, CELLSIZE); - - } else if (t instanceof PileOfScrap) { - gc.drawImage(tileImages[PILEOFSCRAP], getPos(i), getPos(j), CELLSIZE, CELLSIZE); - } else if (t instanceof Stockpile stockpile) { - gc.drawImage(tileImages[STOCKPILE], getPos(i), getPos(j), CELLSIZE, CELLSIZE); - boolean nutDrawn = false; - boolean accuDrawn = false; - boolean screwDrawn = false; - for (Item item : stockpile.getAllItems()) { - if (!nutDrawn && item instanceof Nut) { - gc.drawImage(itemImages[NUT], getPos(i) + CELLSIZE / 2d, getPos(j), CELLSIZE / 2d, - CELLSIZE / 2d); - nutDrawn = true; - } else if (!accuDrawn && item instanceof Accu) { - gc.drawImage(itemImages[ACCU], getPos(i), getPos(j), CELLSIZE / 2d, CELLSIZE / 2d); - accuDrawn = true; - } else if (!screwDrawn && item instanceof Screw) { - gc.drawImage(itemImages[SCREW], getPos(i) + CELLSIZE / 3d, getPos(j) + CELLSIZE / 2d, - CELLSIZE / 2d, CELLSIZE / 2d); - screwDrawn = true; - } - if (nutDrawn && accuDrawn && screwDrawn) - break; - } - } - if (!(t instanceof Stockpile)) { - Item item = t.getItem(); - if (item instanceof Nut) { - gc.drawImage(itemImages[NUT], getPos(i), getPos(j), CELLSIZE, CELLSIZE); - } else if (item instanceof Screw) { - gc.drawImage(itemImages[SCREW], getPos(i), getPos(j), CELLSIZE, CELLSIZE); - } else if (item instanceof Accu) { - gc.drawImage(itemImages[ACCU], getPos(i), getPos(j), CELLSIZE, CELLSIZE); - } - - } - } - } - - double angle = switch (territory.getRobbiDirection()) { - case NORTH: - yield 270; - case WEST: - yield 180; - case SOUTH: - yield 90; - case EAST: - default: - yield 0; - }; - drawRotatedImage(gc, robbiImage, angle, (double) territory.getRobbiX() * (CELLSIZE + CELLSPACER) + CELLSPACER, - (double) territory.getRobbiY() * (CELLSIZE + CELLSPACER) + CELLSPACER, CELLSIZE, CELLSIZE); - } - - /** - * Sets the transform for the GraphicsContext to rotate around a pivot point. - * - * @param gc the graphics context the transform to applied to. - * @param angle the angle of rotation. - * @param px the x pivot co-ordinate for the rotation (in canvas - * co-ordinates). - * @param py the y pivot co-ordinate for the rotation (in canvas - * co-ordinates). - * @see Stackoverflow - */ - private void rotate(GraphicsContext gc, double angle, double px, double py) { - Rotate r = new Rotate(angle, px, py); - gc.setTransform(r.getMxx(), r.getMyx(), r.getMxy(), r.getMyy(), r.getTx(), r.getTy()); - } - - /** - * Draws an image on a graphics context. - * - * The image is drawn at (tlpx, tlpy) rotated by angle pivoted around the point: - * (tlpx + image.getWidth() / 2, tlpy + image.getHeight() / 2) - * - * @param gc the graphics context the image is to be drawn on. - * @param angle the angle of rotation. - * @param tlpx the top left x co-ordinate where the image will be plotted (in - * canvas co-ordinates). - * @param tlpy the top left y co-ordinate where the image will be plotted (in - * canvas co-ordinates). - * @see Stackoverflow - */ - private void drawRotatedImage(GraphicsContext gc, Image image, double angle, double tlpx, double tlpy, double w, - double h) { - gc.save(); // saves the current state on stack, including the current transform - rotate(gc, angle, tlpx + image.getWidth() / 2, tlpy + image.getHeight() / 2); - gc.drawImage(image, tlpx, tlpy, w, h); - gc.restore(); // back to original state (before rotation) - } - - private int getPos(int val) { - return (val) * (CELLSIZE + CELLSPACER) + CELLSPACER; - } - - /** - * taken from Dibo
- * Centers the territory-panel in the center of the viewPortBounds. - * - * @param vpb The bounds in which the territory has to be centered - */ - public void center(Bounds vpb) { - this.bounds = vpb; - double w = vpb.getWidth(); - double h = vpb.getHeight(); - if (w > getTerritoryWidth()) { - setTranslateX((w - getTerritoryWidth()) / 2); - } else - setTranslateX(0); - if (h > getTerritoryHeight()) { - setTranslateY((h - getTerritoryHeight()) / 2); - } else - setTranslateY(0); - } - - @Override - public void update(Observable observable) { - Platform.runLater(this::update); - } - - /** - * Centers the territory if the size has changed. Afterwards, it draws the - * territorypanel. - */ - public void update() { - if (territory.hasSizeChanged()) { - center(bounds); - territory.setSizeChanged(false); - } - drawPanel(); - } + private final Territory territory; + + private static final Image[] tileImages = new Image[4]; + private static final Image[] itemImages = new Image[3]; + private static final Image robbiImage; + + private static final int TILE = 0; + private static final int STOCKPILE = 1; + private static final int HOLLOW = 2; + private static final int PILEOFSCRAP = 3; + private static final int NUT = 0; + private static final int SCREW = 1; + private static final int ACCU = 2; + + private static final int CELLSIZE = 32; + private static final int CELLSPACER = 1; + + // store current bounds to allow centering on updated territory size + private Bounds bounds; + + static { + + logger.debug("Loading territory images"); + + robbiImage = new Image(String.valueOf(MainStage.class.getResource("/img/0Robbi32.png"))); + + tileImages[TILE] = new Image(String.valueOf(MainStage.class.getResource("/img/Tile32.png"))); + tileImages[STOCKPILE] = new Image(String.valueOf(MainStage.class.getResource("/img/Stockpile32.png"))); + tileImages[HOLLOW] = new Image(String.valueOf(MainStage.class.getResource("/img/Hollow32.png"))); + tileImages[PILEOFSCRAP] = new Image(String.valueOf(MainStage.class.getResource("/img/PileOfScrap32.png"))); + + itemImages[NUT] = new Image(String.valueOf(MainStage.class.getResource("/img/Nut32.png"))); + itemImages[SCREW] = new Image(String.valueOf(MainStage.class.getResource("/img/Screw32.png"))); + itemImages[ACCU] = new Image(String.valueOf(MainStage.class.getResource("/img/Accu32.png"))); + } + + /** + * Constructor to create a new territory panel for the given territory. + * + * @param territory the territory this panel is for + * @param buttonState the buttonState to be able to create a + * TerritoryEventHandler + * @param parent the parent Window to show alerts relative to the calling + * window + */ + public TerritoryPanel(Territory territory, ButtonState buttonState, MainStage parent) { + this.territory = territory; + this.territory.addObserver(this); + + TerritoryEventHandler eventHandler = new TerritoryEventHandler(territory, this, buttonState, parent); + this.setOnMousePressed(eventHandler); + this.setOnMouseDragged(eventHandler); + this.setOnMouseReleased(eventHandler); + + drawPanel(); + } + + /** + * Getter for the CELLSIZE. + * + * @return CELLSIZE value + */ + public static int getCellsize() { + return CELLSIZE; + } + + /** + * Getter for the CELLSPACER. + * + * @return CELLSPACER value + */ + public static int getCellspacer() { + return CELLSPACER; + } + + /** + * updates the size of the territory if the size has changed. Paints the + * territory afterward. + */ + private void drawPanel() { + if (getWidth() != getTerritoryWidth()) + setWidth(getTerritoryWidth()); + if (getHeight() != getTerritoryHeight()) + setHeight(getTerritoryHeight()); + paintTerritory(); + + } + + /** + * Getter for the territory width. + * + * @return the width of the territory, calculated by number of cols, Cell-size + * and cell-spacer + */ + private int getTerritoryWidth() { + return (territory.getNumCols()) * (CELLSIZE + CELLSPACER); + } + + /** + * Getter for the territory height. + * + * @return the height of the territory, calculated by number of rows, Cellsize + * and cellspacer + */ + private int getTerritoryHeight() { + return (territory.getNumRows()) * (CELLSIZE + CELLSPACER); + } + + /** + * Paint all graphics for the territory and the robbi on the GUI. + */ + private void paintTerritory() { + + GraphicsContext gc = getGraphicsContext2D(); + for (int i = 0; i < territory.getNumCols(); i++) { + + for (int j = 0; j < territory.getNumRows(); j++) { + Tile t = territory.getTile(i, j); + gc.drawImage(tileImages[TILE], getPos(i), getPos(j), CELLSIZE, CELLSIZE); + if (t instanceof Hollow) { + gc.drawImage(tileImages[HOLLOW], getPos(i), getPos(j), CELLSIZE, CELLSIZE); + + } else if (t instanceof PileOfScrap) { + gc.drawImage(tileImages[PILEOFSCRAP], getPos(i), getPos(j), CELLSIZE, CELLSIZE); + } else if (t instanceof Stockpile stockpile) { + gc.drawImage(tileImages[STOCKPILE], getPos(i), getPos(j), CELLSIZE, CELLSIZE); + boolean nutDrawn = false; + boolean accuDrawn = false; + boolean screwDrawn = false; + for (Item item : stockpile.getAllItems()) { + if (!nutDrawn && item instanceof Nut) { + gc.drawImage(itemImages[NUT], getPos(i) + CELLSIZE / 2d, getPos(j), CELLSIZE / 2d, + CELLSIZE / 2d); + nutDrawn = true; + } else if (!accuDrawn && item instanceof Accu) { + gc.drawImage(itemImages[ACCU], getPos(i), getPos(j), CELLSIZE / 2d, CELLSIZE / 2d); + accuDrawn = true; + } else if (!screwDrawn && item instanceof Screw) { + gc.drawImage(itemImages[SCREW], getPos(i) + CELLSIZE / 3d, getPos(j) + CELLSIZE / 2d, + CELLSIZE / 2d, CELLSIZE / 2d); + screwDrawn = true; + } + if (nutDrawn && accuDrawn && screwDrawn) + break; + } + } + if (!(t instanceof Stockpile)) { + Item item = t.getItem(); + if (item instanceof Nut) { + gc.drawImage(itemImages[NUT], getPos(i), getPos(j), CELLSIZE, CELLSIZE); + } else if (item instanceof Screw) { + gc.drawImage(itemImages[SCREW], getPos(i), getPos(j), CELLSIZE, CELLSIZE); + } else if (item instanceof Accu) { + gc.drawImage(itemImages[ACCU], getPos(i), getPos(j), CELLSIZE, CELLSIZE); + } + + } + } + } + + double angle = switch (territory.getRobbiDirection()) { + case NORTH: + yield 270; + case WEST: + yield 180; + case SOUTH: + yield 90; + case EAST: + yield 0; + }; + drawRotatedImage(gc, robbiImage, angle, (double) territory.getRobbiX() * (CELLSIZE + CELLSPACER) + CELLSPACER, + (double) territory.getRobbiY() * (CELLSIZE + CELLSPACER) + CELLSPACER, CELLSIZE, CELLSIZE); + } + + /** + * Sets the transform for the GraphicsContext to rotate around a pivot point. + * + * @param gc the graphics context the transform to applied to. + * @param angle the angle of rotation. + * @param px the x pivot co-ordinate for the rotation (in canvas + * co-ordinates). + * @param py the y pivot co-ordinate for the rotation (in canvas + * co-ordinates). + * @see Stackoverflow + */ + private void rotate(GraphicsContext gc, double angle, double px, double py) { + Rotate r = new Rotate(angle, px, py); + gc.setTransform(r.getMxx(), r.getMyx(), r.getMxy(), r.getMyy(), r.getTx(), r.getTy()); + } + + /** + * Draws an image on a graphics context. + *

+ * The image is drawn at (tlpx, tlpy) rotated by angle pivoted around the point: + * (tlpx + image.getWidth() / 2, tlpy + image.getHeight() / 2) + * + * @param gc the graphics context the image is to be drawn on. + * @param angle the angle of rotation. + * @param tlpx the top left x co-ordinate where the image will be plotted (in + * canvas co-ordinates). + * @param tlpy the top left y co-ordinate where the image will be plotted (in + * canvas co-ordinates). + * @see Stackoverflow + */ + private void drawRotatedImage(GraphicsContext gc, Image image, double angle, double tlpx, double tlpy, double w, + double h) { + gc.save(); // saves the current state on stack, including the current transform + rotate(gc, angle, tlpx + image.getWidth() / 2, tlpy + image.getHeight() / 2); + gc.drawImage(image, tlpx, tlpy, w, h); + gc.restore(); // back to original state (before rotation) + } + + private int getPos(int val) { + return (val) * (CELLSIZE + CELLSPACER) + CELLSPACER; + } + + /** + * taken from Dibo
+ * Centers the territory-panel in the center of the viewPortBounds. + * + * @param vpb The bounds in which the territory has to be centered + */ + public void center(Bounds vpb) { + this.bounds = vpb; + double w = vpb.getWidth(); + double h = vpb.getHeight(); + if (w > getTerritoryWidth()) { + setTranslateX((w - getTerritoryWidth()) / 2); + } else + setTranslateX(0); + if (h > getTerritoryHeight()) { + setTranslateY((h - getTerritoryHeight()) / 2); + } else + setTranslateY(0); + } + + @Override + public void update(Observable observable) { + Platform.runLater(this::update); + } + + /** + * Centers the territory if the size has changed. Afterward, it draws the + * territory-panel. + */ + public void update() { + if (territory.hasSizeChanged()) { + center(bounds); + territory.setSizeChanged(false); + } + drawPanel(); + } } diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/view/Toolbar.java b/src/main/java/com/JayPi4c/RobbiSimulator/view/Toolbar.java index 575beb9..838a801 100644 --- a/src/main/java/com/JayPi4c/RobbiSimulator/view/Toolbar.java +++ b/src/main/java/com/JayPi4c/RobbiSimulator/view/Toolbar.java @@ -1,178 +1,162 @@ package com.JayPi4c.RobbiSimulator.view; -import com.jfoenix.controls.JFXButton; -import com.jfoenix.controls.JFXButton.ButtonType; -import com.jfoenix.controls.JFXSlider; -import com.jfoenix.controls.JFXToggleNode; - -import javafx.scene.control.Button; -import javafx.scene.control.Separator; -import javafx.scene.control.Slider; -import javafx.scene.control.ToggleButton; -import javafx.scene.control.ToggleGroup; -import javafx.scene.control.ToolBar; +import javafx.scene.control.*; import javafx.scene.image.ImageView; import lombok.Getter; import lombok.extern.slf4j.Slf4j; /** * The toolbar allows faster access to the most common actions. - * - * @author Jonas Pohl * + * @author Jonas Pohl */ @Getter @Slf4j public class Toolbar extends ToolBar { - private MenuBar menubar; - - // Tool bar - private Button newButtonToolbar; - private Button loadButtonToolbar; - - private Button saveButtonToolbar; - private Button compileButtonToolbar; - - private Button changeSizeButtonToolbar; - private ToggleButton placeRobbiToggleButtonToolbar; - private ToggleButton placeHollowToggleButtonToolbar; - private ToggleButton placePileOfScrapToggleButtonToolbar; - private ToggleButton placeStockpileToggleButtonToolbar; - private ToggleButton placeAccuToggleButtonToolbar; - private ToggleButton placeScrewToggleButtonToolbar; - private ToggleButton placeNutToggleButtonToolbar; - private ToggleButton deleteFieldToggleButtonToolbar; - - private Button robbiTurnLeftButtonToolbar; - private Button robbiMoveButtonToolbar; - private Button robbiPutButtonToolbar; - private Button robbiTakeButtonToolbar; - - private Button resetButtonToolbar; - private ToggleButton startToggleButtonToolbar; - private ToggleButton pauseToggleButtonToolbar; - private ToggleButton stopToggleButtonToolbar; - - private Slider speedSliderToolbar; - - /** - * Constant for the minimum value for the speed slider. - */ - public static final int MIN_SPEED_VALUE = 1; - /** - * Constant for the maximum value for the speed slider. - */ - public static final int MAX_SPEED_VALUE = 100; - - /** - * Creates a new Toolbar - * - * @param menubar the menubar to link some actions from the menubar to the - * toolbar buttons - */ - public Toolbar(MenuBar menubar) { - this.menubar = menubar; - createToolbar(); - } - - /** - * Creates a toolbar for direct-access to the most important features. - */ - private void createToolbar() { - logger.debug("Create toolbar"); - newButtonToolbar = new JFXButton(null, new ImageView(MainStage.newImage)); - ((JFXButton) newButtonToolbar).setButtonType(ButtonType.RAISED); - - loadButtonToolbar = new JFXButton(null, new ImageView(MainStage.openImage)); - - saveButtonToolbar = new JFXButton(null, new ImageView(MainStage.saveImage)); - - compileButtonToolbar = new JFXButton(null, new ImageView(MainStage.compileImage)); - - changeSizeButtonToolbar = new JFXButton(null, new ImageView(MainStage.terrainImage)); - - var placeGroupToolbar = new ToggleGroup(); - - placeRobbiToggleButtonToolbar = new JFXToggleNode(); - placeRobbiToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuRobbiImage)); - placeRobbiToggleButtonToolbar.setToggleGroup(placeGroupToolbar); - placeRobbiToggleButtonToolbar.selectedProperty() - .bindBidirectional(menubar.getPlaceRobbiTerritoryRadioMenuItem().selectedProperty()); - - placeHollowToggleButtonToolbar = new JFXToggleNode(); - placeHollowToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuHollowImage)); - placeHollowToggleButtonToolbar.setToggleGroup(placeGroupToolbar); - placeHollowToggleButtonToolbar.selectedProperty() - .bindBidirectional(menubar.getPlaceHollowTerritoryRadioMenuItem().selectedProperty()); - - placePileOfScrapToggleButtonToolbar = new JFXToggleNode(); - placePileOfScrapToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuPileOfScrapImage)); - placePileOfScrapToggleButtonToolbar.setToggleGroup(placeGroupToolbar); - placePileOfScrapToggleButtonToolbar.selectedProperty() - .bindBidirectional(menubar.getPlacePileOfScrapTerritoryRadioMenuItem().selectedProperty()); - - placeStockpileToggleButtonToolbar = new JFXToggleNode(); - placeStockpileToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuStockpileImage)); - placeStockpileToggleButtonToolbar.setToggleGroup(placeGroupToolbar); - placeStockpileToggleButtonToolbar.selectedProperty() - .bindBidirectional(menubar.getPlaceStockpileTerritoryRadioMenuItem().selectedProperty()); - - placeAccuToggleButtonToolbar = new JFXToggleNode(); - placeAccuToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuAccuImage)); - placeAccuToggleButtonToolbar.setToggleGroup(placeGroupToolbar); - placeAccuToggleButtonToolbar.selectedProperty() - .bindBidirectional(menubar.getPlaceAccuTerritoryRadioMenuItem().selectedProperty()); - - placeScrewToggleButtonToolbar = new JFXToggleNode(); - placeScrewToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuScrewImage)); - placeScrewToggleButtonToolbar.setToggleGroup(placeGroupToolbar); - placeScrewToggleButtonToolbar.selectedProperty() - .bindBidirectional(menubar.getPlaceScrewTerritoryRadioMenuItem().selectedProperty()); - - placeNutToggleButtonToolbar = new JFXToggleNode(); - placeNutToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuNutImage)); - placeNutToggleButtonToolbar.setToggleGroup(placeGroupToolbar); - placeNutToggleButtonToolbar.selectedProperty() - .bindBidirectional(menubar.getPlaceNutTerritoryRadioMenuItem().selectedProperty()); - - deleteFieldToggleButtonToolbar = new JFXToggleNode(); - deleteFieldToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuDeleteImage)); - deleteFieldToggleButtonToolbar.setToggleGroup(placeGroupToolbar); - deleteFieldToggleButtonToolbar.selectedProperty() - .bindBidirectional(menubar.getDeleteFieldRadioMenuItem().selectedProperty()); - - robbiMoveButtonToolbar = new JFXButton(null, new ImageView(MainStage.robbiMove)); - - robbiTurnLeftButtonToolbar = new JFXButton(null, new ImageView(MainStage.robbiTurnLeft)); - - robbiPutButtonToolbar = new JFXButton(null, new ImageView(MainStage.robbiPut)); - - robbiTakeButtonToolbar = new JFXButton(null, new ImageView(MainStage.robbiTake)); - - resetButtonToolbar = new JFXButton(null, new ImageView(MainStage.resetImage)); - - var simulationGroupToolbar = new ToggleGroup(); - startToggleButtonToolbar = new JFXToggleNode(); - startToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuStartImage)); - startToggleButtonToolbar.setToggleGroup(simulationGroupToolbar); - - pauseToggleButtonToolbar = new JFXToggleNode(); - pauseToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuPauseImage)); - pauseToggleButtonToolbar.setToggleGroup(simulationGroupToolbar); - - stopToggleButtonToolbar = new JFXToggleNode(); - stopToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuStopImage)); - stopToggleButtonToolbar.setToggleGroup(simulationGroupToolbar); - - speedSliderToolbar = new JFXSlider(MIN_SPEED_VALUE, MAX_SPEED_VALUE, (MIN_SPEED_VALUE + MAX_SPEED_VALUE) / 2d); - - getItems().addAll(newButtonToolbar, loadButtonToolbar, new Separator(), saveButtonToolbar, compileButtonToolbar, - new Separator(), changeSizeButtonToolbar, placeRobbiToggleButtonToolbar, placeHollowToggleButtonToolbar, - placePileOfScrapToggleButtonToolbar, placeStockpileToggleButtonToolbar, placeAccuToggleButtonToolbar, - placeScrewToggleButtonToolbar, placeNutToggleButtonToolbar, deleteFieldToggleButtonToolbar, - new Separator(), robbiMoveButtonToolbar, robbiTurnLeftButtonToolbar, robbiPutButtonToolbar, - robbiTakeButtonToolbar, new Separator(), resetButtonToolbar, startToggleButtonToolbar, - pauseToggleButtonToolbar, stopToggleButtonToolbar, new Separator(), speedSliderToolbar); - } + /** + * Constant for the minimum value for the speed slider. + */ + public static final int MIN_SPEED_VALUE = 1; + /** + * Constant for the maximum value for the speed slider. + */ + public static final int MAX_SPEED_VALUE = 10; + private final MenuBar menubar; + // Toolbar + private Button newButtonToolbar; + private Button loadButtonToolbar; + private Button saveButtonToolbar; + private Button compileButtonToolbar; + private Button changeSizeButtonToolbar; + private ToggleButton placeRobbiToggleButtonToolbar; + private ToggleButton placeHollowToggleButtonToolbar; + private ToggleButton placePileOfScrapToggleButtonToolbar; + private ToggleButton placeStockpileToggleButtonToolbar; + private ToggleButton placeAccuToggleButtonToolbar; + private ToggleButton placeScrewToggleButtonToolbar; + private ToggleButton placeNutToggleButtonToolbar; + private ToggleButton deleteFieldToggleButtonToolbar; + private Button robbiTurnLeftButtonToolbar; + private Button robbiMoveButtonToolbar; + private Button robbiPutButtonToolbar; + private Button robbiTakeButtonToolbar; + private Button resetButtonToolbar; + private ToggleButton startToggleButtonToolbar; + private ToggleButton pauseToggleButtonToolbar; + private ToggleButton stopToggleButtonToolbar; + private Slider speedSliderToolbar; + + /** + * Creates a new Toolbar + * + * @param menubar the menubar to link some actions from the menubar to the + * toolbar buttons + */ + public Toolbar(MenuBar menubar) { + this.menubar = menubar; + createToolbar(); + } + + /** + * Creates a toolbar for direct-access to the most important features. + */ + private void createToolbar() { + logger.debug("Create toolbar"); + newButtonToolbar = new Button(null, new ImageView(MainStage.newImage)); + + + loadButtonToolbar = new Button(null, new ImageView(MainStage.openImage)); + + saveButtonToolbar = new Button(null, new ImageView(MainStage.saveImage)); + + compileButtonToolbar = new Button(null, new ImageView(MainStage.compileImage)); + + changeSizeButtonToolbar = new Button(null, new ImageView(MainStage.terrainImage)); + + var placeGroupToolbar = new ToggleGroup(); + + placeRobbiToggleButtonToolbar = new ToggleButton(); + placeRobbiToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuRobbiImage)); + placeRobbiToggleButtonToolbar.setToggleGroup(placeGroupToolbar); + placeRobbiToggleButtonToolbar.selectedProperty() + .bindBidirectional(menubar.getPlaceRobbiTerritoryRadioMenuItem().selectedProperty()); + + placeHollowToggleButtonToolbar = new ToggleButton(); + placeHollowToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuHollowImage)); + placeHollowToggleButtonToolbar.setToggleGroup(placeGroupToolbar); + placeHollowToggleButtonToolbar.selectedProperty() + .bindBidirectional(menubar.getPlaceHollowTerritoryRadioMenuItem().selectedProperty()); + + placePileOfScrapToggleButtonToolbar = new ToggleButton(); + placePileOfScrapToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuPileOfScrapImage)); + placePileOfScrapToggleButtonToolbar.setToggleGroup(placeGroupToolbar); + placePileOfScrapToggleButtonToolbar.selectedProperty() + .bindBidirectional(menubar.getPlacePileOfScrapTerritoryRadioMenuItem().selectedProperty()); + + placeStockpileToggleButtonToolbar = new ToggleButton(); + placeStockpileToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuStockpileImage)); + placeStockpileToggleButtonToolbar.setToggleGroup(placeGroupToolbar); + placeStockpileToggleButtonToolbar.selectedProperty() + .bindBidirectional(menubar.getPlaceStockpileTerritoryRadioMenuItem().selectedProperty()); + + placeAccuToggleButtonToolbar = new ToggleButton(); + placeAccuToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuAccuImage)); + placeAccuToggleButtonToolbar.setToggleGroup(placeGroupToolbar); + placeAccuToggleButtonToolbar.selectedProperty() + .bindBidirectional(menubar.getPlaceAccuTerritoryRadioMenuItem().selectedProperty()); + + placeScrewToggleButtonToolbar = new ToggleButton(); + placeScrewToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuScrewImage)); + placeScrewToggleButtonToolbar.setToggleGroup(placeGroupToolbar); + placeScrewToggleButtonToolbar.selectedProperty() + .bindBidirectional(menubar.getPlaceScrewTerritoryRadioMenuItem().selectedProperty()); + + placeNutToggleButtonToolbar = new ToggleButton(); + placeNutToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuNutImage)); + placeNutToggleButtonToolbar.setToggleGroup(placeGroupToolbar); + placeNutToggleButtonToolbar.selectedProperty() + .bindBidirectional(menubar.getPlaceNutTerritoryRadioMenuItem().selectedProperty()); + + deleteFieldToggleButtonToolbar = new ToggleButton(); + deleteFieldToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuDeleteImage)); + deleteFieldToggleButtonToolbar.setToggleGroup(placeGroupToolbar); + deleteFieldToggleButtonToolbar.selectedProperty() + .bindBidirectional(menubar.getDeleteFieldRadioMenuItem().selectedProperty()); + + robbiMoveButtonToolbar = new Button(null, new ImageView(MainStage.robbiMove)); + + robbiTurnLeftButtonToolbar = new Button(null, new ImageView(MainStage.robbiTurnLeft)); + + robbiPutButtonToolbar = new Button(null, new ImageView(MainStage.robbiPut)); + + robbiTakeButtonToolbar = new Button(null, new ImageView(MainStage.robbiTake)); + + resetButtonToolbar = new Button(null, new ImageView(MainStage.resetImage)); + + var simulationGroupToolbar = new ToggleGroup(); + startToggleButtonToolbar = new ToggleButton(); + startToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuStartImage)); + startToggleButtonToolbar.setToggleGroup(simulationGroupToolbar); + + pauseToggleButtonToolbar = new ToggleButton(); + pauseToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuPauseImage)); + pauseToggleButtonToolbar.setToggleGroup(simulationGroupToolbar); + + stopToggleButtonToolbar = new ToggleButton(); + stopToggleButtonToolbar.setGraphic(new ImageView(MainStage.menuStopImage)); + stopToggleButtonToolbar.setToggleGroup(simulationGroupToolbar); + + speedSliderToolbar = new Slider(MIN_SPEED_VALUE, MAX_SPEED_VALUE, (MIN_SPEED_VALUE + MAX_SPEED_VALUE) / 2d); + speedSliderToolbar.setShowTickLabels(true); + speedSliderToolbar.setShowTickMarks(true); + + getItems().addAll(newButtonToolbar, loadButtonToolbar, new Separator(), saveButtonToolbar, compileButtonToolbar, + new Separator(), changeSizeButtonToolbar, placeRobbiToggleButtonToolbar, placeHollowToggleButtonToolbar, + placePileOfScrapToggleButtonToolbar, placeStockpileToggleButtonToolbar, placeAccuToggleButtonToolbar, + placeScrewToggleButtonToolbar, placeNutToggleButtonToolbar, deleteFieldToggleButtonToolbar, + new Separator(), robbiMoveButtonToolbar, robbiTurnLeftButtonToolbar, robbiPutButtonToolbar, + robbiTakeButtonToolbar, new Separator(), resetButtonToolbar, startToggleButtonToolbar, + pauseToggleButtonToolbar, stopToggleButtonToolbar, new Separator(), speedSliderToolbar); + } } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 087c4f8..1eb5585 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -5,8 +5,8 @@ requires javafx.media; requires javafx.web; - requires com.jfoenix; requires eu.mihosoft.monacofx; + requires org.controlsfx.controls; requires java.desktop; requires java.rmi; @@ -22,8 +22,11 @@ requires org.apache.derby.tools; opens com.JayPi4c.RobbiSimulator.controller.examples to org.hibernate.orm.core; + opens com.JayPi4c.RobbiSimulator.model to jakarta.xml.bind; exports com.JayPi4c.RobbiSimulator.utils.annotations; exports com.JayPi4c.RobbiSimulator.model; exports com.JayPi4c.RobbiSimulator; + + exports com.JayPi4c.RobbiSimulator.controller.tutor to java.rmi; } \ No newline at end of file diff --git a/src/main/resources/css/dark-theme.css b/src/main/resources/css/dark-theme.css index c5f6a3c..262f9b9 100644 --- a/src/main/resources/css/dark-theme.css +++ b/src/main/resources/css/dark-theme.css @@ -1,3 +1,4 @@ +/* Taken from stack overflow: https://stackoverflow.com/a/49159612 */ .root { -fx-base: #3f474f; -fx-accent: #e7eff7; @@ -5,18 +6,18 @@ -fx-focus-color: #efefef; -fx-faint-focus-color: #efefef22; -fx-focused-text-base-color: ladder(-fx-selection-bar, - -fx-light-text-color 45%, - -fx-dark-text-color 46%, - -fx-dark-text-color 59%, - -fx-mid-text-color 60%); + -fx-light-text-color 45%, + -fx-dark-text-color 46%, + -fx-dark-text-color 59%, + -fx-mid-text-color 60%); -fx-focused-mark-color: -fx-focused-text-base-color; } .text-input:focused { -fx-highlight-text-fill: ladder(-fx-highlight-fill, - -fx-light-text-color 45%, - -fx-dark-text-color 46%, - -fx-dark-text-color 59%, - -fx-mid-text-color 60%); + -fx-light-text-color 45%, + -fx-dark-text-color 46%, + -fx-dark-text-color 59%, + -fx-mid-text-color 60%); } \ No newline at end of file diff --git a/src/main/resources/lang/messages_de_DE.properties b/src/main/resources/lang/messages_de_DE.properties index ae903e5..f8ae153 100644 --- a/src/main/resources/lang/messages_de_DE.properties +++ b/src/main/resources/lang/messages_de_DE.properties @@ -1,186 +1,173 @@ - -ChangeSize.dialog.cols = Spalten: -ChangeSize.dialog.header = Bitte w\u00E4hle eine neue Gr\u00F6\u00DFe -ChangeSize.dialog.rows = Reihen: -ChangeSize.dialog.title = Gr\u00F6\u00DFe \u00E4ndern - -Compilation.annotations.header = Annotationen fehlerhaft -Compilation.annotations.msg.default = Aufgrund fehlerhafter Annotationen konnte Robbi nicht in das Territorium geladen werden. -Compilation.annotations.msg.info = "%s" ist nicht anwendbar f\u00FCr den Typ "%s". -Compilation.annotations.title = Annotationenfehler -Compilation.diagnostic = Kompilierungsfehler -Compilation.diagnostic.CodeAndMessage = Code und Nachricht: %s: %s%n -Compilation.diagnostic.kind = Art: %s%n -Compilation.diagnostic.override = Robbi \u00FCberschreibt nicht die main-Methode. -Compilation.diagnostic.row = Zeile: %s%n -Compilation.diagnostic.source = Quelle: %s%n -Compilation.diagnostic.title = Kompilierung Fehlgeschlagen -Compilation.success.header = Kompilierung erfolgreich -Compilation.success.message = Dein Sketch '%s' wurde erfolgreich kompiliert. -Compilation.success.title = Erfolg - -Editor.contextMenu.executionError = Die @Default Annotation hat einen falschen String ('%s') in der Methode '%s'. -Editor.contextMenu.tooltip = Bitte nutze die @Default Annotation. - -Examples.duplication.header = Es existiert bereits ein Programm mit dem Namen des Beispiels. -Examples.duplication.message = Altes Programm umbennen?\n(Das alte Programm wird sonst \u00FCberschrieben.) -Examples.duplication.title = Namenduplikat -Examples.load.dialog.program.header = W\u00E4hle ein Programm aus. -Examples.load.dialog.program.name = Programm -Examples.load.dialog.program.title = Programmauswahl -Examples.load.dialog.tags.fail = Es konnten keine Tags in der Datenbank gefunden werden. -Examples.load.dialog.tags.header = W\u00E4hle einen Tag f\u00FCr ein Beispiel. -Examples.load.dialog.tags.name = Tag -Examples.load.dialog.tags.title = Tagauswahl -Examples.save.tags.header = Gebe leerzeichengetrennt Tags ein. -Examples.save.tags.name = Tags -Examples.save.tags.prompt = Tags f\u00FCr das Beispiel -Examples.save.tags.title = Tageingabe - -Exception.BagIsEmpty = Ooops! Robbi kann kein Item ablegen. Die Tasche ist leer. -Exception.BagIsFull = Ooops! Robbi kann kein Item aufnehmen. Seine Tasche ist voll. -Exception.HollowAhead = Ooops! Robbi kann nicht vorw\u00E4rts gehen. Es ist eine Kuhle voraus. -Exception.NoItem = Ooops! Hier liegt kein Item, welches Robbi aufheben kann. -Exception.NoPileOfScrapAhead = Ooops! Robbi Kann keinen Schrotthaufen zum Schieben finden. -Exception.TileBlocked = Ooops! Robbi kann diesen Schrotthaufen nicht schieben. Die folgende Kachel ist blockiert. -Exception.TileIsFull = Ooops! Es ist schon ein Item auf der Kachel. Robbi kann hier nichts ablegen. - -Execution.information.bag = Tasche voll: {0} -Execution.information.hollow = Kuhle voraus: {0} -Execution.information.itemPresent = Gegenstand da: {0} -Execution.information.pileOfScrap = Schrotthaufen voraus: {0} -Execution.information.result = Das Ergebnis des Aufrufs ist: -Execution.information.stockpile = Lager da: {0} - -Init.error.header = Initialisierungsfehler -Init.error.message = Der Program Controller konnte nicht initialisiert werden. -Init.error.title = Error - -Main.title = Robbi Simulator - -Menu.editor = _Editor -Menu.editor.compile = _Kompilieren -Menu.editor.format = formatieren -Menu.editor.new = _Neu -Menu.editor.open = _\u00D6ffnen -Menu.editor.print = _Drucken -Menu.editor.quit = _Beenden -Menu.editor.save = _Speichern -Menu.examples = Beispiele -Menu.examples.load = Laden -Menu.examples.save = Speichern -Menu.robbi = _Robbi -Menu.robbi.hollowAhead = Vorn Kuhle -Menu.robbi.isBagFull = Ist Tasche voll -Menu.robbi.isStockpile = Ist Lagerplatz -Menu.robbi.itemPresent = Gegenstand da -Menu.robbi.move = vor -Menu.robbi.pileOfScrapAhead = Vorn Schrotthaufen -Menu.robbi.pushPileOfScrap = Schiebe Schrotthaufen -Menu.robbi.put = lege ab -Menu.robbi.take = nehme auf -Menu.robbi.turnLeft = links um -Menu.simulation = _Simulation -Menu.simulation.pause = Pause -Menu.simulation.reset = Zur\u00FCcksetzten -Menu.simulation.start = Start/Fortsetzen -Menu.simulation.stop = Stopp -Menu.territory = _Territorium -Menu.territory.delete = Kachel leeren -Menu.territory.load = _Laden -Menu.territory.load.deserialize = _Deserialisieren -Menu.territory.load.jaxb = _JAXB -Menu.territory.load.xml = _XML -Menu.territory.place.accu = Akku platzieren -Menu.territory.place.hollow = Kuhle platzieren -Menu.territory.place.nut = Mutter platzieren -Menu.territory.place.pileOfScrap = Schrotthaufen platzieren -Menu.territory.place.robbi = Robbi platzieren -Menu.territory.place.screw = Schraube platzieren -Menu.territory.place.stockpile = Lagerplatz platzieren -Menu.territory.print = _Drucken -Menu.territory.print.error = Der Drucker konnte nicht initialisiert werden. -Menu.territory.save = _Speichern -Menu.territory.save.jaxb = _JAXB -Menu.territory.save.serialize = _Serialisieren -Menu.territory.save.xml = _XML -Menu.territory.saveAsPic = Als _Bild speichern -Menu.territory.saveAsPic.error = Das Territorium konnte im angegebenen Format nicht gespeichert werden. -Menu.territory.saveAsPic.gif = Als GIF speichern -Menu.territory.saveAsPic.png = Als PNG speichern -Menu.territory.saveAsPic.png.descripion = png-Bilddateien -Menu.territory.saveAsPic.png.extension = png -Menu.territory.size = _Gr\u00F6\u00DFe \u00E4ndern... -Menu.tutor = Tutor -Menu.tutor.loadRequest = Anfrage laden -Menu.tutor.loadRequest.success = Anfrage #{0} erfolgreich geladen. -Menu.tutor.loadRequest.warning = Es sind keine Anfrage vorhanden. -Menu.tutor.receiveAnswer = Tutorantwort holen -Menu.tutor.receiveAnswer.error = Es konnte keine Antwort vom Tutor geladen werden. -Menu.tutor.receiveAnswer.information = Es ist noch keine Antwort vorhanden. -Menu.tutor.saveAnswer = Anfrage beantworten -Menu.tutor.saveAnswer.information = Die Bearbeitung wurde gespeichert. -Menu.tutor.sendRequest = Tutoranfrage schicken -Menu.tutor.sendRequest.error = Die Anfrage konnte nicht verschickt werden. -Menu.tutor.sendRequest.information = Die Anfrage wurde verschickt. -Menu.window = Fenster -Menu.window.changeCursor = Angepasster Cursor -Menu.window.darkmode = Dunkelmodus -Menu.window.enableSounds = T\u00F6ne aktivieren -Menu.window.info = Info -Menu.window.info.content = Dieses Program wurde geschrieben von JayPi4c.\nDer Robbi-Simulator ist inspiriert an dem Hamster-Simulator von Dibo. -Menu.window.info.header = Informationen zum Robbi-Simulator -Menu.window.info.title = Information -Menu.window.language = Sprache -Menu.window.language.english = English -Menu.window.language.german = Deutsch -Menu.window.libraries = Bibliotheken -Menu.window.libraries.content = Java Version: {0}\nJavaFX Version: {1}\nJFoenix Version: {2}\nMonacoFX Version: {3}\nDerby Version: {4}\nJAXB Version: {5}\nHibernate Version: {6}\nLog4J Version: {7}\nLombok Version: {8} -Menu.window.libraries.header = Verwendete Bibliotheken -Menu.window.libraries.title = Bibliotheken - -New.dialog.header = Neues Programm erstellen -New.dialog.name = Name: -New.dialog.prompt = Dateiname -New.dialog.title = Neue Datei - -Open.dialog.filter = Java Dateien -Open.dialog.title = Program \u00F6ffnen - -Territory.load.dialog.filter.deserial = Serialisierungsdateien -Territory.load.dialog.filter.jaxb = JAXB-Dateien -Territory.load.dialog.filter.xml = XML-Dateien -Territory.load.dialog.title = Territorium laden -Territory.load.failure = Das Laden des Territoriums ist fehlgeschlagen. -Territory.save.dialog.filter.jaxb = JAXB-Dateien -Territory.save.dialog.filter.serial = Serialisierungsdateien -Territory.save.dialog.filter.xml = XML-Dateien -Territory.save.dialog.title = Territorium speichern -Territory.xml.error.dtd = Die dtd Datei konnte nicht geladen werden. - -Toolbar.action.pause = Pausiert die Simulation -Toolbar.action.reset = Zur\u00FCcksetzten -Toolbar.action.speed = Stelle die Simulationsgeschwindigkeit ein -Toolbar.action.start = Starte die Simulation -Toolbar.action.stop = Stoppt die Simulation -Toolbar.control.compile = Kompiliere den aktuellen Code -Toolbar.control.load = \u00D6ffne einen gespeicherten Simulator -Toolbar.control.new = \u00D6ffne einen neuen Simulator -Toolbar.control.save = Speichere den aktuellen Simulator -Toolbar.robbi.move = Bewege Robbi ein Feld in Blickrichtung -Toolbar.robbi.put = Lege den Gegenstand aus der Tasche auf der Kachel ab -Toolbar.robbi.take = Nehme den Gegenstand von der Kachel auf -Toolbar.robbi.turnLeft = Drehe Robbi um 90 Grad im Uhrzeigersinn -Toolbar.territory.delete = Entferne alle Elemente von einer Kachel -Toolbar.territory.placeAccu = Platziere einen Akku auf dem Schrottplatz -Toolbar.territory.placeHollow = Platziere eine Kuhle auf dem Schrottplatz -Toolbar.territory.placeNut = Platziere eine Mutter auf dem Schrottplatz -Toolbar.territory.placePileOfScrap = Platziere einen Schrotthaufen auf dem Schrottplatz -Toolbar.territory.placeRobbi = Platziere Robbi auf dem Schrottplatz -Toolbar.territory.placeScrew = Platziere eine Schraube auf dem Schrottplatz -Toolbar.territory.placeStockpile = Platziere ein Lager auf dem Schrottplatz -Toolbar.territory.size = Konfiguriere die Gr\u00F6\u00DFe des Territoriums - -language.changed = Sprache ge\u00E4ndert! - -not.implemented = Noch nicht implementiert! +ChangeSize.dialog.cols=Spalten: +ChangeSize.dialog.header=Bitte wähle eine neue Größe +ChangeSize.dialog.rows=Reihen: +ChangeSize.dialog.title=Größe ändern +Compilation.annotations.header=Annotationen fehlerhaft +Compilation.annotations.msg.default=Aufgrund fehlerhafter Annotationen konnte Robbi nicht in das Territorium geladen werden. +Compilation.annotations.msg.info="%s" ist nicht anwendbar für den Typ "%s". +Compilation.annotations.title=Annotationenfehler +Compilation.diagnostic=Kompilierungsfehler +Compilation.diagnostic.CodeAndMessage=Code und Nachricht: %s: %s%n +Compilation.diagnostic.kind=Art: %s%n +Compilation.diagnostic.override=Robbi überschreibt nicht die main-Methode. +Compilation.diagnostic.row=Zeile: %s%n +Compilation.diagnostic.source=Quelle: %s%n +Compilation.diagnostic.title=Kompilierung Fehlgeschlagen +Compilation.success.header=Kompilierung erfolgreich +Compilation.success.message=Dein Sketch '%s' wurde erfolgreich kompiliert. +Compilation.success.title=Erfolg +Editor.contextMenu.executionError=Die @Default Annotation hat einen falschen String ('%s') in der Methode '%s'. +Editor.contextMenu.tooltip=Bitte nutze die @Default Annotation. +Examples.duplication.header=Es existiert bereits ein Programm mit dem Namen des Beispiels. +Examples.duplication.message=Altes Programm umbennen?\n(Das alte Programm wird sonst überschrieben.) +Examples.duplication.title=Namenduplikat +Examples.load.dialog.program.header=Wähle ein Programm aus. +Examples.load.dialog.program.name=Programm +Examples.load.dialog.program.title=Programmauswahl +Examples.load.dialog.tags.fail=Es konnten keine Tags in der Datenbank gefunden werden. +Examples.load.dialog.tags.header=Wähle einen Tag für ein Beispiel. +Examples.load.dialog.tags.name=Tag +Examples.load.dialog.tags.title=Tagauswahl +Examples.save.tags.header=Gebe leerzeichengetrennt Tags ein. +Examples.save.tags.name=Tags +Examples.save.tags.prompt=Tags für das Beispiel +Examples.save.tags.title=Tageingabe +Exception.BagIsEmpty=Ooops! Robbi kann kein Item ablegen. Die Tasche ist leer. +Exception.BagIsFull=Ooops! Robbi kann kein Item aufnehmen. Seine Tasche ist voll. +Exception.HollowAhead=Ooops! Robbi kann nicht vorwärts gehen. Es ist eine Kuhle voraus. +Exception.NoItem=Ooops! Hier liegt kein Item, welches Robbi aufheben kann. +Exception.NoPileOfScrapAhead=Ooops! Robbi Kann keinen Schrotthaufen zum Schieben finden. +Exception.TileBlocked=Ooops! Robbi kann diesen Schrotthaufen nicht schieben. Die folgende Kachel ist blockiert. +Exception.TileIsFull=Ooops! Es ist schon ein Item auf der Kachel. Robbi kann hier nichts ablegen. +Execution.information.bag=Tasche voll: {0} +Execution.information.hollow=Kuhle voraus: {0} +Execution.information.itemPresent=Gegenstand da: {0} +Execution.information.pileOfScrap=Schrotthaufen voraus: {0} +Execution.information.result=Das Ergebnis des Aufrufs ist: +Execution.information.stockpile=Lager da: {0} +Init.error.header=Initialisierungsfehler +Init.error.message=Der Program Controller konnte nicht initialisiert werden. +Init.error.title=Error +Main.title=Robbi Simulator +Menu.editor=_Editor +Menu.editor.compile=_Kompilieren +Menu.editor.format=formatieren +Menu.editor.new=_Neu +Menu.editor.open=_Öffnen +Menu.editor.print=_Drucken +Menu.editor.quit=_Beenden +Menu.editor.save=_Speichern +Menu.examples=Beispiele +Menu.examples.load=Laden +Menu.examples.save=Speichern +Menu.robbi=_Robbi +Menu.robbi.hollowAhead=Vorn Kuhle +Menu.robbi.isBagFull=Ist Tasche voll +Menu.robbi.isStockpile=Ist Lagerplatz +Menu.robbi.itemPresent=Gegenstand da +Menu.robbi.move=vor +Menu.robbi.pileOfScrapAhead=Vorn Schrotthaufen +Menu.robbi.pushPileOfScrap=Schiebe Schrotthaufen +Menu.robbi.put=lege ab +Menu.robbi.take=nehme auf +Menu.robbi.turnLeft=links um +Menu.simulation=_Simulation +Menu.simulation.pause=Pause +Menu.simulation.reset=Zurücksetzten +Menu.simulation.start=Start/Fortsetzen +Menu.simulation.stop=Stopp +Menu.territory=_Territorium +Menu.territory.delete=Kachel leeren +Menu.territory.load=_Laden +Menu.territory.load.deserialize=_Deserialisieren +Menu.territory.load.jaxb=_JAXB +Menu.territory.load.xml=_XML +Menu.territory.place.accu=Akku platzieren +Menu.territory.place.hollow=Kuhle platzieren +Menu.territory.place.nut=Mutter platzieren +Menu.territory.place.pileOfScrap=Schrotthaufen platzieren +Menu.territory.place.robbi=Robbi platzieren +Menu.territory.place.screw=Schraube platzieren +Menu.territory.place.stockpile=Lagerplatz platzieren +Menu.territory.print=_Drucken +Menu.territory.print.error=Der Drucker konnte nicht initialisiert werden. +Menu.territory.save=_Speichern +Menu.territory.save.jaxb=_JAXB +Menu.territory.save.serialize=_Serialisieren +Menu.territory.save.xml=_XML +Menu.territory.saveAsPic=Als _Bild speichern +Menu.territory.saveAsPic.error=Das Territorium konnte im angegebenen Format nicht gespeichert werden. +Menu.territory.saveAsPic.gif=Als GIF speichern +Menu.territory.saveAsPic.png=Als PNG speichern +Menu.territory.saveAsPic.png.descripion=png-Bilddateien +Menu.territory.saveAsPic.png.extension=png +Menu.territory.size=_Größe ändern... +Menu.tutor=Tutor +Menu.tutor.loadRequest=Anfrage laden +Menu.tutor.loadRequest.success=Anfrage #{0} erfolgreich geladen. +Menu.tutor.loadRequest.warning=Es sind keine Anfrage vorhanden. +Menu.tutor.receiveAnswer=Tutorantwort holen +Menu.tutor.receiveAnswer.error=Es konnte keine Antwort vom Tutor geladen werden. +Menu.tutor.receiveAnswer.information=Es ist noch keine Antwort vorhanden. +Menu.tutor.saveAnswer=Anfrage beantworten +Menu.tutor.saveAnswer.information=Die Bearbeitung wurde gespeichert. +Menu.tutor.sendRequest=Tutoranfrage schicken +Menu.tutor.sendRequest.error=Die Anfrage konnte nicht verschickt werden. +Menu.tutor.sendRequest.information=Die Anfrage wurde verschickt. +Menu.window=Fenster +Menu.window.changeCursor=Angepasster Cursor +Menu.window.darkmode=Dunkelmodus +Menu.window.enableSounds=Töne aktivieren +Menu.window.info=Info +Menu.window.info.content=Dieses Program wurde geschrieben von JayPi4c.\nDer Robbi-Simulator ist inspiriert an dem Hamster-Simulator von Dibo. +Menu.window.info.header=Informationen zum Robbi-Simulator +Menu.window.info.title=Information +Menu.window.language=Sprache +Menu.window.language.english=English +Menu.window.language.german=Deutsch +Menu.window.libraries=Bibliotheken +Menu.window.libraries.content=Java Version: {0}\nJavaFX Version: {1}\nMonacoFX Version: {3}\nDerby Version: {4}\nJAXB Version: {5}\nHibernate Version: {6}\nLog4J Version: {7}\nLombok Version: {8} +Menu.window.libraries.header=Verwendete Bibliotheken +Menu.window.libraries.title=Bibliotheken +New.dialog.header=Neues Programm erstellen +New.dialog.name=Name: +New.dialog.prompt=Dateiname +New.dialog.title=Neue Datei +Open.dialog.filter=Java Dateien +Open.dialog.title=Program öffnen +Territory.load.dialog.filter.deserial=Serialisierungsdateien +Territory.load.dialog.filter.jaxb=JAXB-Dateien +Territory.load.dialog.filter.xml=XML-Dateien +Territory.load.dialog.title=Territorium laden +Territory.load.failure=Das Laden des Territoriums ist fehlgeschlagen. +Territory.save.dialog.filter.jaxb=JAXB-Dateien +Territory.save.dialog.filter.serial=Serialisierungsdateien +Territory.save.dialog.filter.xml=XML-Dateien +Territory.save.dialog.title=Territorium speichern +Territory.xml.error.dtd=Die dtd Datei konnte nicht geladen werden. +Toolbar.action.pause=Pausiert die Simulation +Toolbar.action.reset=Zurücksetzten +Toolbar.action.speed=Stelle die Simulationsgeschwindigkeit ein +Toolbar.action.start=Starte die Simulation +Toolbar.action.stop=Stoppt die Simulation +Toolbar.control.compile=Kompiliere den aktuellen Code +Toolbar.control.load=Öffne einen gespeicherten Simulator +Toolbar.control.new=Öffne einen neuen Simulator +Toolbar.control.save=Speichere den aktuellen Simulator +Toolbar.robbi.move=Bewege Robbi ein Feld in Blickrichtung +Toolbar.robbi.put=Lege den Gegenstand aus der Tasche auf der Kachel ab +Toolbar.robbi.take=Nehme den Gegenstand von der Kachel auf +Toolbar.robbi.turnLeft=Drehe Robbi um 90 Grad im Uhrzeigersinn +Toolbar.territory.delete=Entferne alle Elemente von einer Kachel +Toolbar.territory.placeAccu=Platziere einen Akku auf dem Schrottplatz +Toolbar.territory.placeHollow=Platziere eine Kuhle auf dem Schrottplatz +Toolbar.territory.placeNut=Platziere eine Mutter auf dem Schrottplatz +Toolbar.territory.placePileOfScrap=Platziere einen Schrotthaufen auf dem Schrottplatz +Toolbar.territory.placeRobbi=Platziere Robbi auf dem Schrottplatz +Toolbar.territory.placeScrew=Platziere eine Schraube auf dem Schrottplatz +Toolbar.territory.placeStockpile=Platziere ein Lager auf dem Schrottplatz +Toolbar.territory.size=Konfiguriere die Größe des Territoriums +language.changed=Sprache geändert! +not.implemented=Noch nicht implementiert! +Snackbar.message.startup=Erfolgreich gestartet! +Snackbar.message.compile.success=Kompilierung erfolgreich! diff --git a/src/main/resources/lang/messages_en_GB.properties b/src/main/resources/lang/messages_en_GB.properties index 053afd5..1798ae5 100644 --- a/src/main/resources/lang/messages_en_GB.properties +++ b/src/main/resources/lang/messages_en_GB.properties @@ -1,188 +1,175 @@ #Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/) - -ChangeSize.dialog.cols = Cols: -ChangeSize.dialog.header = please choose new dimension -ChangeSize.dialog.rows = Rows: -ChangeSize.dialog.title = Change size - -Compilation.annotations.header = Annotations faulty -Compilation.annotations.msg.default = Because of faulty Annotations Robbi could not be loaded into the territory. -Compilation.annotations.msg.info = "%s" is not applicable for the type "%s". -Compilation.annotations.title = Annotations error -Compilation.diagnostic = Compilation Error -Compilation.diagnostic.CodeAndMessage = Code and message: %s: %s%n -Compilation.diagnostic.kind = Kind: %s%n -Compilation.diagnostic.override = Robbi does not override the main-method. -Compilation.diagnostic.row = Row: %s%n -Compilation.diagnostic.source = Source: %s%n -Compilation.diagnostic.title = Compilation Error -Compilation.success.header = Compilation successful -Compilation.success.message = Your sketch '%s' has been compiled successfully. -Compilation.success.title = Success - -Editor.contextMenu.executionError = The @Default annotation uses a wrong String ('%s') in the method '%s'. -Editor.contextMenu.tooltip = Please use the @Default annotation. - -Examples.duplication.header = There is another program with the examples name. -Examples.duplication.message = Rename old program?\n(Otherwise the old program will be overwritten.) -Examples.duplication.title = Name duplication -Examples.load.dialog.program.header = Choose a program. -Examples.load.dialog.program.name = Program -Examples.load.dialog.program.title = Programselection -Examples.load.dialog.tags.fail = No tags could be found in the database. -Examples.load.dialog.tags.header = Choose a tag for an example. -Examples.load.dialog.tags.name = Tag -Examples.load.dialog.tags.title = Tagselection -Examples.save.tags.header = Type in space-separated tags. -Examples.save.tags.name = Tags -Examples.save.tags.prompt = Tags for the example -Examples.save.tags.title = Taginput - -Exception.BagIsEmpty = Ooops! Robbi can't place its item. The bag is empty. -Exception.BagIsFull = Ooops! Robbi can't pick up an item. His bag is full. -Exception.HollowAhead = Ooops! Robbi can't move forward. There is a hollow ahead. -Exception.NoItem = Ooops! There is no item, robbi could pick up. -Exception.NoPileOfScrapAhead = Ooops! Robbi can't find a pile to push. -Exception.TileBlocked = Ooops! Robbi can't push this pile of scrap. The next tile is blocked. -Exception.TileIsFull = Ooops! There is already an item on the tile. Robbi can't place anything here. - -Execution.information.bag = bag full: {0} -Execution.information.hollow = Hollow ahead: {0} -Execution.information.itemPresent = Item present: {0} -Execution.information.pileOfScrap = Pile of scrap ahead: {0} -Execution.information.result = The call's result is: -Execution.information.stockpile = Is stockpile: {0} - -Init.error.header = Initialization error -Init.error.message = Could not initialize Program Controller. -Init.error.title = Error - -Main.title = Robbi Simulator - -Menu.editor = _Editor -Menu.editor.compile = _Compile -Menu.editor.format = format -Menu.editor.new = _New -Menu.editor.open = _Open -Menu.editor.print = _Print -Menu.editor.quit = _Quit -Menu.editor.save = _Save -Menu.examples = Examples -Menu.examples.load = Load -Menu.examples.save = Save -Menu.robbi = _Robbi -Menu.robbi.hollowAhead = hollow ahead -Menu.robbi.isBagFull = is Bag full -Menu.robbi.isStockpile = is stockpile -Menu.robbi.itemPresent = item present -Menu.robbi.move = move -Menu.robbi.pileOfScrapAhead = Pile of scrap ahead -Menu.robbi.pushPileOfScrap = push pile of scrap -Menu.robbi.put = put -Menu.robbi.take = take -Menu.robbi.turnLeft = turn left -Menu.simulation = _Simulation -Menu.simulation.pause = Pause -Menu.simulation.reset = Reset -Menu.simulation.start = Start/Continue -Menu.simulation.stop = Stop -Menu.territory = _Territory -Menu.territory.delete = clear tile -Menu.territory.load = _Load -Menu.territory.load.deserialize = _Deserialize -Menu.territory.load.jaxb = _JAXB -Menu.territory.load.xml = _XML -Menu.territory.place.accu = Place accu -Menu.territory.place.hollow = place Hollow -Menu.territory.place.nut = place nut -Menu.territory.place.pileOfScrap = Place pile of scrap -Menu.territory.place.robbi = Place robbi -Menu.territory.place.screw = Place screw -Menu.territory.place.stockpile = Place Stockpile -Menu.territory.print = _Print -Menu.territory.print.error = Could no initialize printer. -Menu.territory.save = _Save -Menu.territory.save.jaxb = _JAXB -Menu.territory.save.serialize = _Serialize -Menu.territory.save.xml = _XML -Menu.territory.saveAsPic = Save as Pi_cture -Menu.territory.saveAsPic.error = Could not save the territory in the given format. -Menu.territory.saveAsPic.gif = Save as GIF -Menu.territory.saveAsPic.png = Save as PNG -Menu.territory.saveAsPic.png.descripion = png-Imagefiles -Menu.territory.saveAsPic.png.extension = png -Menu.territory.size = change si_ze -Menu.tutor = Tutor\n -Menu.tutor.loadRequest = Load request -Menu.tutor.loadRequest.success = Request #{0} successfully loaded. -Menu.tutor.loadRequest.warning = There are no requests available. -Menu.tutor.receiveAnswer = Fetch tutor-answer -Menu.tutor.receiveAnswer.error = Failed to fetch an answer from the tutor. -Menu.tutor.receiveAnswer.information = There is no answer yet. -Menu.tutor.saveAnswer = Answer request. -Menu.tutor.saveAnswer.information = The editing has been saved. -Menu.tutor.sendRequest = Send tutor-request -Menu.tutor.sendRequest.error = Failed to send the request. -Menu.tutor.sendRequest.information = The request has been sent. -Menu.window = Window -Menu.window.changeCursor = Custom Cursor -Menu.window.darkmode = Darkmode -Menu.window.enableSounds = enable sounds -Menu.window.info = Info -Menu.window.info.content = This program was written by JayPi4c.\nThe Robbi-Simulator is inspired by the Hamster-Simulator by Dibo. -Menu.window.info.header = Information for Robbi-Simulator -Menu.window.info.title = Information -Menu.window.language = Language -Menu.window.language.english = English -Menu.window.language.german = Deutsch -Menu.window.libraries = Libraries -Menu.window.libraries.content = Java Version: {0}\nJavaFX Version: {1}\nJFoenix Version: {2}\nMonacoFX Version: {3}\nDerby Version: {4}\nJAXB Version: {5}\nHibernate Version: {6}\nLog4J Version: {7}\nLombok Version: {8} -Menu.window.libraries.header = Used libraries -Menu.window.libraries.title = Libraries - -New.dialog.header = Create new Program -New.dialog.name = Name: -New.dialog.prompt = filename -New.dialog.title = New file - -Open.dialog.filter = Java Files -Open.dialog.title = Open Program - -Territory.load.dialog.filter = Serializationfiles -Territory.load.dialog.filter.deserial = Serializationfiles -Territory.load.dialog.filter.jaxb = JAXB-Files -Territory.load.dialog.filter.xml = XML-Files -Territory.load.dialog.title = Load territory -Territory.load.failure = Failed to load territory. -Territory.save.dialog.filter.jaxb = JAXB-Files -Territory.save.dialog.filter.serial = Serializationfiles -Territory.save.dialog.filter.xml = XML-Files -Territory.save.dialog.title = Save territory -Territory.xml.error.dtd = Could not laod the dtd-file. - -Toolbar.action.pause = pause the simulation -Toolbar.action.reset = Reset -Toolbar.action.speed = adjust speed of simulation -Toolbar.action.start = start the simulation -Toolbar.action.stop = stop the simulation -Toolbar.control.compile = compile the current code -Toolbar.control.load = load a saved simulator -Toolbar.control.new = load a new simulator -Toolbar.control.save = save the current simulator -Toolbar.robbi.move = Move Robbi one tile forward in the direction of view -Toolbar.robbi.put = Place the item from the bag in the tile -Toolbar.robbi.take = Take the item from the tile -Toolbar.robbi.turnLeft = Rotate Robbi 90 degrees clockwise -Toolbar.territory.delete = Remove all elements from a tile -Toolbar.territory.placeAccu = Place a bettery in the junkyard -Toolbar.territory.placeHollow = Place a hollow in the junkyard -Toolbar.territory.placeNut = Place a nut in the junkyard -Toolbar.territory.placePileOfScrap = Place a pile of Scrap in the junkyard -Toolbar.territory.placeRobbi = Place Robbi in the junkyard -Toolbar.territory.placeScrew = Place a screw in the junkyard -Toolbar.territory.placeStockpile = Place a stockpile in the junkyard -Toolbar.territory.size = configure the size of the territory - -language.changed = Langauge changed! - -not.implemented = Not yet implemented! +ChangeSize.dialog.cols=Cols: +ChangeSize.dialog.header=please choose new dimension +ChangeSize.dialog.rows=Rows: +ChangeSize.dialog.title=Change size +Compilation.annotations.header=Annotations faulty +Compilation.annotations.msg.default=Because of faulty Annotations Robbi could not be loaded into the territory. +Compilation.annotations.msg.info="%s" is not applicable for the type "%s". +Compilation.annotations.title=Annotations error +Compilation.diagnostic=Compilation Error +Compilation.diagnostic.CodeAndMessage=Code and message: %s: %s%n +Compilation.diagnostic.kind=Kind: %s%n +Compilation.diagnostic.override=Robbi does not override the main-method. +Compilation.diagnostic.row=Row: %s%n +Compilation.diagnostic.source=Source: %s%n +Compilation.diagnostic.title=Compilation Error +Compilation.success.header=Compilation successful +Compilation.success.message=Your sketch '%s' has been compiled successfully. +Compilation.success.title=Success +Editor.contextMenu.executionError=The @Default annotation uses a wrong String ('%s') in the method '%s'. +Editor.contextMenu.tooltip=Please use the @Default annotation. +Examples.duplication.header=There is another program with the examples name. +Examples.duplication.message=Rename old program?\n(Otherwise the old program will be overwritten.) +Examples.duplication.title=Name duplication +Examples.load.dialog.program.header=Choose a program. +Examples.load.dialog.program.name=Program +Examples.load.dialog.program.title=Programselection +Examples.load.dialog.tags.fail=No tags could be found in the database. +Examples.load.dialog.tags.header=Choose a tag for an example. +Examples.load.dialog.tags.name=Tag +Examples.load.dialog.tags.title=Tagselection +Examples.save.tags.header=Type in space-separated tags. +Examples.save.tags.name=Tags +Examples.save.tags.prompt=Tags for the example +Examples.save.tags.title=Taginput +Exception.BagIsEmpty=Ooops! Robbi can't place its item. The bag is empty. +Exception.BagIsFull=Ooops! Robbi can't pick up an item. His bag is full. +Exception.HollowAhead=Ooops! Robbi can't move forward. There is a hollow ahead. +Exception.NoItem=Ooops! There is no item, robbi could pick up. +Exception.NoPileOfScrapAhead=Ooops! Robbi can't find a pile to push. +Exception.TileBlocked=Ooops! Robbi can't push this pile of scrap. The next tile is blocked. +Exception.TileIsFull=Ooops! There is already an item on the tile. Robbi can't place anything here. +Execution.information.bag=bag full: {0} +Execution.information.hollow=Hollow ahead: {0} +Execution.information.itemPresent=Item present: {0} +Execution.information.pileOfScrap=Pile of scrap ahead: {0} +Execution.information.result=The call's result is: +Execution.information.stockpile=Is stockpile: {0} +Init.error.header=Initialization error +Init.error.message=Could not initialize Program Controller. +Init.error.title=Error +Main.title=Robbi Simulator +Menu.editor=_Editor +Menu.editor.compile=_Compile +Menu.editor.format=format +Menu.editor.new=_New +Menu.editor.open=_Open +Menu.editor.print=_Print +Menu.editor.quit=_Quit +Menu.editor.save=_Save +Menu.examples=Examples +Menu.examples.load=Load +Menu.examples.save=Save +Menu.robbi=_Robbi +Menu.robbi.hollowAhead=hollow ahead +Menu.robbi.isBagFull=is Bag full +Menu.robbi.isStockpile=is stockpile +Menu.robbi.itemPresent=item present +Menu.robbi.move=move +Menu.robbi.pileOfScrapAhead=Pile of scrap ahead +Menu.robbi.pushPileOfScrap=push pile of scrap +Menu.robbi.put=put +Menu.robbi.take=take +Menu.robbi.turnLeft=turn left +Menu.simulation=_Simulation +Menu.simulation.pause=Pause +Menu.simulation.reset=Reset +Menu.simulation.start=Start/Continue +Menu.simulation.stop=Stop +Menu.territory=_Territory +Menu.territory.delete=clear tile +Menu.territory.load=_Load +Menu.territory.load.deserialize=_Deserialize +Menu.territory.load.jaxb=_JAXB +Menu.territory.load.xml=_XML +Menu.territory.place.accu=Place accu +Menu.territory.place.hollow=place Hollow +Menu.territory.place.nut=place nut +Menu.territory.place.pileOfScrap=Place pile of scrap +Menu.territory.place.robbi=Place robbi +Menu.territory.place.screw=Place screw +Menu.territory.place.stockpile=Place Stockpile +Menu.territory.print=_Print +Menu.territory.print.error=Could no initialize printer. +Menu.territory.save=_Save +Menu.territory.save.jaxb=_JAXB +Menu.territory.save.serialize=_Serialize +Menu.territory.save.xml=_XML +Menu.territory.saveAsPic=Save as Pi_cture +Menu.territory.saveAsPic.error=Could not save the territory in the given format. +Menu.territory.saveAsPic.gif=Save as GIF +Menu.territory.saveAsPic.png=Save as PNG +Menu.territory.saveAsPic.png.descripion=png-Imagefiles +Menu.territory.saveAsPic.png.extension=png +Menu.territory.size=change si_ze +Menu.tutor=Tutor\n +Menu.tutor.loadRequest=Load request +Menu.tutor.loadRequest.success=Request #{0} successfully loaded. +Menu.tutor.loadRequest.warning=There are no requests available. +Menu.tutor.receiveAnswer=Fetch tutor-answer +Menu.tutor.receiveAnswer.error=Failed to fetch an answer from the tutor. +Menu.tutor.receiveAnswer.information=There is no answer yet. +Menu.tutor.saveAnswer=Answer request. +Menu.tutor.saveAnswer.information=The editing has been saved. +Menu.tutor.sendRequest=Send tutor-request +Menu.tutor.sendRequest.error=Failed to send the request. +Menu.tutor.sendRequest.information=The request has been sent. +Menu.window=Window +Menu.window.changeCursor=Custom Cursor +Menu.window.darkmode=Darkmode +Menu.window.enableSounds=enable sounds +Menu.window.info=Info +Menu.window.info.content=This program was written by JayPi4c.\nThe Robbi-Simulator is inspired by the Hamster-Simulator by Dibo. +Menu.window.info.header=Information for Robbi-Simulator +Menu.window.info.title=Information +Menu.window.language=Language +Menu.window.language.english=English +Menu.window.language.german=Deutsch +Menu.window.libraries=Libraries +Menu.window.libraries.content=Java Version: {0}\nJavaFX Version: {1}\nMonacoFX Version: {3}\nDerby Version: {4}\nJAXB Version: {5}\nHibernate Version: {6}\nLog4J Version: {7}\nLombok Version: {8} +Menu.window.libraries.header=Used libraries +Menu.window.libraries.title=Libraries +New.dialog.header=Create new Program +New.dialog.name=Name: +New.dialog.prompt=filename +New.dialog.title=New file +Open.dialog.filter=Java Files +Open.dialog.title=Open Program +Territory.load.dialog.filter=Serializationfiles +Territory.load.dialog.filter.deserial=Serializationfiles +Territory.load.dialog.filter.jaxb=JAXB-Files +Territory.load.dialog.filter.xml=XML-Files +Territory.load.dialog.title=Load territory +Territory.load.failure=Failed to load territory. +Territory.save.dialog.filter.jaxb=JAXB-Files +Territory.save.dialog.filter.serial=Serializationfiles +Territory.save.dialog.filter.xml=XML-Files +Territory.save.dialog.title=Save territory +Territory.xml.error.dtd=Could not laod the dtd-file. +Toolbar.action.pause=pause the simulation +Toolbar.action.reset=Reset +Toolbar.action.speed=adjust speed of simulation +Toolbar.action.start=start the simulation +Toolbar.action.stop=stop the simulation +Toolbar.control.compile=compile the current code +Toolbar.control.load=load a saved simulator +Toolbar.control.new=load a new simulator +Toolbar.control.save=save the current simulator +Toolbar.robbi.move=Move Robbi one tile forward in the direction of view +Toolbar.robbi.put=Place the item from the bag in the tile +Toolbar.robbi.take=Take the item from the tile +Toolbar.robbi.turnLeft=Rotate Robbi 90 degrees clockwise +Toolbar.territory.delete=Remove all elements from a tile +Toolbar.territory.placeAccu=Place a bettery in the junkyard +Toolbar.territory.placeHollow=Place a hollow in the junkyard +Toolbar.territory.placeNut=Place a nut in the junkyard +Toolbar.territory.placePileOfScrap=Place a pile of Scrap in the junkyard +Toolbar.territory.placeRobbi=Place Robbi in the junkyard +Toolbar.territory.placeScrew=Place a screw in the junkyard +Toolbar.territory.placeStockpile=Place a stockpile in the junkyard +Toolbar.territory.size=configure the size of the territory +language.changed=Langauge changed! +not.implemented=Not yet implemented! +Snackbar.message.startup=Started successfully! +Snackbar.message.compile.success=Compilation