diff --git a/src/main/java/ch/zhaw/gartenverwaltung/CropDetailController.java b/src/main/java/ch/zhaw/gartenverwaltung/CropDetailController.java index 1d887b5..bf5a7bf 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/CropDetailController.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/CropDetailController.java @@ -1,5 +1,6 @@ package ch.zhaw.gartenverwaltung; +import ch.zhaw.gartenverwaltung.bootstrap.Inject; import ch.zhaw.gartenverwaltung.io.PlantList; import ch.zhaw.gartenverwaltung.models.Garden; import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; @@ -9,7 +10,6 @@ import ch.zhaw.gartenverwaltung.types.Crop; import ch.zhaw.gartenverwaltung.types.Pest; import ch.zhaw.gartenverwaltung.types.Plant; import ch.zhaw.gartenverwaltung.types.Task; -import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.geometry.Pos; import javafx.scene.control.Button; @@ -26,18 +26,16 @@ import java.util.logging.Logger; public class CropDetailController { private Crop crop; + + @Inject private PlantList plantList; + @Inject private GardenSchedule gardenSchedule; + @Inject private Garden garden; private static final Logger LOG = Logger.getLogger(CropDetailController.class.getName()); - @SuppressWarnings("unused") - public void injectDependencies(Garden garden, PlantList plantList, GardenSchedule gardenSchedule) { - this.garden = garden; - this.plantList = plantList; - this.gardenSchedule = gardenSchedule; - } @FXML private ImageView imageView; @@ -75,23 +73,23 @@ public class CropDetailController { private Label spacing_label; @FXML - void editTaskList(ActionEvent event) { + void editTaskList() { } @FXML - void goBack(ActionEvent event) { + void goBack() { Stage stage = (Stage) imageView.getScene().getWindow(); stage.close(); } @FXML - void setArea(ActionEvent event) { + void setArea() { } @FXML - void setLocation(ActionEvent event) { + void setLocation() { } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/MainFXMLController.java b/src/main/java/ch/zhaw/gartenverwaltung/MainFXMLController.java index 29eaa30..9fe278a 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/MainFXMLController.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/MainFXMLController.java @@ -1,53 +1,21 @@ package ch.zhaw.gartenverwaltung; -import ch.zhaw.gartenverwaltung.io.CropList; -import ch.zhaw.gartenverwaltung.io.JsonCropList; -import ch.zhaw.gartenverwaltung.io.JsonPlantList; -import ch.zhaw.gartenverwaltung.io.JsonTaskList; -import ch.zhaw.gartenverwaltung.io.PlantList; -import ch.zhaw.gartenverwaltung.io.TaskList; -import ch.zhaw.gartenverwaltung.models.Garden; -import ch.zhaw.gartenverwaltung.models.GardenSchedule; -import ch.zhaw.gartenverwaltung.models.PlantListModel; -import javafx.event.ActionEvent; +import ch.zhaw.gartenverwaltung.bootstrap.AppLoader; import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; -import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Pane; -import javafx.stage.Stage; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; import java.util.ResourceBundle; import java.util.logging.Level; import java.util.logging.Logger; public class MainFXMLController implements Initializable { - /** - * Caching the panes - */ - private final Map panes = new HashMap<>(); private static final Logger LOG = Logger.getLogger(MainFXMLController.class.getName()); - - private final PlantList plantList = new JsonPlantList(); - private final CropList cropList = new JsonCropList(); - private final TaskList taskList = new JsonTaskList(); - - private final GardenSchedule gardenSchedule = new GardenSchedule(taskList, plantList); - private final Garden garden = new Garden(gardenSchedule, cropList); - + private final AppLoader appLoader = new AppLoader(this); @FXML private Button home_button; @@ -67,25 +35,25 @@ public class MainFXMLController implements Initializable { } @FXML - void goToHome(ActionEvent event) throws IOException { + void goToHome() throws IOException { showPaneAsMainView("Home.fxml"); styleChangeButton(home_button); } @FXML - void goToMyPlants(ActionEvent event) throws IOException { + void goToMyPlants() throws IOException { showPaneAsMainView("MyGarden.fxml"); styleChangeButton(myGarden_button); } @FXML - void goToMySchedule(ActionEvent event) throws IOException { + void goToMySchedule() throws IOException { showPaneAsMainView("MySchedule.fxml"); styleChangeButton(mySchedule_button); } @FXML - void goToPlants(ActionEvent event) throws IOException { + void goToPlants() throws IOException { showPaneAsMainView("Plants.fxml"); styleChangeButton(plants_button); } @@ -98,67 +66,17 @@ public class MainFXMLController implements Initializable { * @throws IOException exception when file does not exist */ public void showPaneAsMainView(String fxmlFile) throws IOException { - Pane anchorPane = panes.get(fxmlFile); - if (anchorPane == null) { - loadAndCacheFxml(fxmlFile); - anchorPane = panes.get(fxmlFile); - } + Pane anchorPane = appLoader.loadPane(fxmlFile); mainPane.getChildren().setAll(anchorPane); anchorPane.prefWidthProperty().bind(mainPane.widthProperty()); anchorPane.prefHeightProperty().bind(mainPane.heightProperty()); } - public Object loadSceneToStage(String fxmlFile, Stage appendee) throws IOException { - FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile))); - Pane root = loader.load(); - appendee.setScene(new Scene(root)); - Object controller = loader.getController(); - injectDependencies(controller); - return controller; - } - - public FXMLLoader loadAndCacheFxml(String fxmlFile) throws IOException { - FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile))); - AnchorPane anchorPane = loader.load(); - panes.put(fxmlFile, anchorPane); - injectDependencies(loader.getController()); - return loader; - } - - public void injectDependencies(Object controller) { - Optional injectMethod = Arrays.stream(controller.getClass().getMethods()) - .filter(method -> method.getName().equals("injectDependencies") ) - .findFirst(); - if (injectMethod.isPresent()) { - Method injectee = injectMethod.get(); - - List params = new ArrayList<>(); - for (Class parameterType : injectee.getParameterTypes()) { - Object toInject = switch (parameterType.getSimpleName()) { - case "Garden" -> garden; - case "PlantList" -> plantList; - case "PlantListModel" -> new PlantListModel(plantList); - case "GardenSchedule" -> gardenSchedule; - case "MainFXMLController" -> this; - default -> null; - }; - if (toInject != null) { - params.add(toInject); - } - } - try { - injectee.invoke(controller, params.toArray()); - } catch (IllegalAccessException | InvocationTargetException e) { - e.printStackTrace(); - } - } - } - private void preloadPanes() throws IOException { - loadAndCacheFxml("MyGarden.fxml"); - loadAndCacheFxml("MySchedule.fxml"); - loadAndCacheFxml("Plants.fxml").getController(); + appLoader.loadAndCacheFxml("MyGarden.fxml"); + appLoader.loadAndCacheFxml("MySchedule.fxml"); + appLoader.loadAndCacheFxml("Plants.fxml"); } private void styleChangeButton(Button button) { diff --git a/src/main/java/ch/zhaw/gartenverwaltung/MyGardenController.java b/src/main/java/ch/zhaw/gartenverwaltung/MyGardenController.java index afb94a3..8f0d3ae 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/MyGardenController.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/MyGardenController.java @@ -1,5 +1,8 @@ package ch.zhaw.gartenverwaltung; +import ch.zhaw.gartenverwaltung.bootstrap.AfterInject; +import ch.zhaw.gartenverwaltung.bootstrap.AppLoader; +import ch.zhaw.gartenverwaltung.bootstrap.Inject; import ch.zhaw.gartenverwaltung.io.PlantList; import ch.zhaw.gartenverwaltung.models.Garden; import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; @@ -28,19 +31,21 @@ import java.util.logging.Logger; public class MyGardenController { private static final Logger LOG = Logger.getLogger(MyGardenController.class.getName()); + @Inject + AppLoader appLoader; + @Inject MainFXMLController mainController; + @Inject private Garden garden; + @Inject private PlantList plantList; @FXML private VBox myPlants_vbox; + @AfterInject @SuppressWarnings("unused") - public void injectDependencies(Garden garden, PlantList plantList, MainFXMLController mainController) { - this.garden = garden; - this.plantList = plantList; - this.mainController = mainController; - + public void init() { garden.getPlantedCrops().addListener((observable, oldValue, newValue) -> { try { createPlantView(newValue); @@ -96,7 +101,7 @@ public class MyGardenController { return (event) -> { try { Stage stage = new Stage(); - if (mainController.loadSceneToStage("CropDetail.fxml", stage) instanceof CropDetailController controller) { + if (appLoader.loadSceneToStage("CropDetail.fxml", stage) instanceof CropDetailController controller) { controller.setPlantFromCrop(crop); } stage.initModality(Modality.APPLICATION_MODAL); diff --git a/src/main/java/ch/zhaw/gartenverwaltung/MyScheduleController.java b/src/main/java/ch/zhaw/gartenverwaltung/MyScheduleController.java index 6585675..417722a 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/MyScheduleController.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/MyScheduleController.java @@ -1,5 +1,7 @@ package ch.zhaw.gartenverwaltung; +import ch.zhaw.gartenverwaltung.bootstrap.AfterInject; +import ch.zhaw.gartenverwaltung.bootstrap.Inject; import ch.zhaw.gartenverwaltung.io.PlantList; import ch.zhaw.gartenverwaltung.models.Garden; import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; @@ -27,20 +29,16 @@ public class MyScheduleController { private static final Logger LOG = Logger.getLogger(MyScheduleController.class.getName()); private Crop selectedCrop = null; + + @Inject private GardenSchedule gardenSchedule; + @Inject private Garden garden; + @Inject private PlantList plantList; private final ListProperty cropListProperty = new SimpleListProperty<>(FXCollections.observableArrayList()); - @SuppressWarnings("unused") - public void injectDependencies(Garden garden, GardenSchedule gardenSchedule, PlantList plantList) { - this.garden = garden; - this.gardenSchedule = gardenSchedule; - this.plantList = plantList; - - init(); - } @FXML private Label day1_label; @@ -89,6 +87,8 @@ public class MyScheduleController { @FXML private ListView scheduledPlants_listview; + @AfterInject + @SuppressWarnings("unused") public void init() { List cropList; try { diff --git a/src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java b/src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java index 0004f30..5a691dd 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java @@ -1,5 +1,8 @@ package ch.zhaw.gartenverwaltung; +import ch.zhaw.gartenverwaltung.bootstrap.AfterInject; +import ch.zhaw.gartenverwaltung.bootstrap.AppLoader; +import ch.zhaw.gartenverwaltung.bootstrap.Inject; import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; import ch.zhaw.gartenverwaltung.models.PlantListModel; import ch.zhaw.gartenverwaltung.types.HardinessZone; @@ -8,7 +11,6 @@ import ch.zhaw.gartenverwaltung.types.Seasons; import javafx.beans.property.ListProperty; import javafx.beans.property.SimpleListProperty; import javafx.collections.FXCollections; -import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.geometry.Insets; import javafx.scene.control.*; @@ -25,21 +27,18 @@ import java.util.logging.Logger; public class PlantsController { private static final Logger LOG = Logger.getLogger(PlantsController.class.getName()); + + @Inject private PlantListModel plantListModel; - private MainFXMLController mainController; + @Inject + private AppLoader appLoader; + private Plant selectedPlant = null; private final HardinessZone DEFAULT_HARDINESS_ZONE = HardinessZone.ZONE_8A; // TODO: move to model private final ListProperty plantListProperty = new SimpleListProperty<>(FXCollections.observableArrayList()); - @SuppressWarnings("unused") - public void injectDependencies(PlantListModel plantListModel, MainFXMLController mainController) { - this.plantListModel = plantListModel; - this.mainController = mainController; - init(); - } - @FXML private VBox seasons; @@ -63,12 +62,11 @@ public class PlantsController { /** * open new window to select sow or harvest day to save the crop - * @param event event */ @FXML - void selectSowDate(ActionEvent event) throws IOException { + void selectSowDate() throws IOException { Stage stage = new Stage(); - if (mainController.loadSceneToStage("SelectSowDay.fxml", stage) instanceof SelectSowDayController controller) { + if (appLoader.loadSceneToStage("SelectSowDay.fxml", stage) instanceof SelectSowDayController controller) { controller.setSelectedPlant(selectedPlant); } @@ -84,6 +82,8 @@ public class PlantsController { * create event listener for selected list entry and search by query * {@inheritDoc} */ + @AfterInject + @SuppressWarnings("unused") public void init() { setListCellFactory(); fillPlantListWithHardinessZone(); @@ -95,6 +95,7 @@ public class PlantsController { createFilterSeasons(); createFilterHardinessZone(); lookForSelectedListEntry(); + try { viewFilteredListBySearch(); } catch (HardinessZoneNotSetException e) { diff --git a/src/main/java/ch/zhaw/gartenverwaltung/SelectSowDayController.java b/src/main/java/ch/zhaw/gartenverwaltung/SelectSowDayController.java index 7dc9389..5260067 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/SelectSowDayController.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/SelectSowDayController.java @@ -1,11 +1,11 @@ package ch.zhaw.gartenverwaltung; +import ch.zhaw.gartenverwaltung.bootstrap.Inject; import ch.zhaw.gartenverwaltung.models.Garden; import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; import ch.zhaw.gartenverwaltung.models.PlantNotFoundException; import ch.zhaw.gartenverwaltung.types.GrowthPhaseType; import ch.zhaw.gartenverwaltung.types.Plant; -import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; @@ -20,12 +20,9 @@ import java.util.ResourceBundle; public class SelectSowDayController implements Initializable { private Plant selectedPlant = null; - private Garden garden; - @SuppressWarnings("unused") - public void injectDependencies(Garden garden) { - this.garden = garden; - } + @Inject + private Garden garden; @FXML private DatePicker datepicker; @@ -41,20 +38,18 @@ public class SelectSowDayController implements Initializable { /** * close the date selector window - * @param event event */ @FXML - void cancel(ActionEvent event) { + void cancel() { closeWindow(); } /** * get sow date from datePicker or calculate sow date from harvest date * save selected plant and sow date - * @param event event */ @FXML - void save(ActionEvent event) throws HardinessZoneNotSetException, IOException, PlantNotFoundException { + void save() throws HardinessZoneNotSetException, IOException, PlantNotFoundException { LocalDate sowDate = datepicker.getValue(); if (harvest_radio.isSelected()) { //ToDo method to get current lifecycle group in plant diff --git a/src/main/java/ch/zhaw/gartenverwaltung/bootstrap/AfterInject.java b/src/main/java/ch/zhaw/gartenverwaltung/bootstrap/AfterInject.java new file mode 100644 index 0000000..c8e5f3f --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/bootstrap/AfterInject.java @@ -0,0 +1,10 @@ +package ch.zhaw.gartenverwaltung.bootstrap; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface AfterInject { } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/bootstrap/AppLoader.java b/src/main/java/ch/zhaw/gartenverwaltung/bootstrap/AppLoader.java new file mode 100644 index 0000000..e341258 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/bootstrap/AppLoader.java @@ -0,0 +1,112 @@ +package ch.zhaw.gartenverwaltung.bootstrap; + +import ch.zhaw.gartenverwaltung.HelloApplication; +import ch.zhaw.gartenverwaltung.MainFXMLController; +import ch.zhaw.gartenverwaltung.io.CropList; +import ch.zhaw.gartenverwaltung.io.JsonCropList; +import ch.zhaw.gartenverwaltung.io.JsonPlantList; +import ch.zhaw.gartenverwaltung.io.JsonTaskList; +import ch.zhaw.gartenverwaltung.io.PlantList; +import ch.zhaw.gartenverwaltung.io.TaskList; +import ch.zhaw.gartenverwaltung.models.Garden; +import ch.zhaw.gartenverwaltung.models.GardenSchedule; +import ch.zhaw.gartenverwaltung.models.PlantListModel; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.Pane; +import javafx.stage.Stage; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class AppLoader { + /** + * Caching the panes + */ + private final Map panes = new HashMap<>(); + + /** + * Application-wide dependencies + */ + private final PlantList plantList = new JsonPlantList(); + private final CropList cropList = new JsonCropList(); + private final TaskList taskList = new JsonTaskList(); + + private final GardenSchedule gardenSchedule = new GardenSchedule(taskList, plantList); + private final Garden garden = new Garden(gardenSchedule, cropList); + private final MainFXMLController mainController; + + + public AppLoader(MainFXMLController mainController) throws IOException { + this.mainController = mainController; + } + + + public Pane loadPane(String fxmlFile) throws IOException { + Pane pane = panes.get(fxmlFile); + if (pane == null) { + loadAndCacheFxml(fxmlFile); + pane = panes.get(fxmlFile); + } + return pane; + } + + public Object loadSceneToStage(String fxmlFile, Stage appendee) throws IOException { + FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile))); + Pane root = loader.load(); + appendee.setScene(new Scene(root)); + Object controller = loader.getController(); + annotationInject(controller); + return controller; + } + + public void loadAndCacheFxml(String fxmlFile) throws IOException { + FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile))); + AnchorPane anchorPane = loader.load(); + panes.put(fxmlFile, anchorPane); + annotationInject(loader.getController()); + } + + public void annotationInject(Object controller) { + Arrays.stream(controller.getClass().getDeclaredFields()) + .filter(field -> field.isAnnotationPresent(Inject.class)) + .forEach(field -> { + field.setAccessible(true); + try { + field.set(controller, getAppDependency(field.getType())); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + field.setAccessible(false); + }); + + Arrays.stream(controller.getClass().getMethods()) + .filter(method -> method.isAnnotationPresent(AfterInject.class)) + .forEach(afterInjectMethod -> { + if (afterInjectMethod.getParameterCount() == 0) { + try { + afterInjectMethod.invoke(controller); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + }); + } + + private Object getAppDependency(Class type) { + return switch (type.getSimpleName()) { + case "Garden" -> garden; + case "PlantList" -> plantList; + case "PlantListModel" -> new PlantListModel(plantList); + case "GardenSchedule" -> gardenSchedule; + case "MainFXMLController" -> mainController; + case "AppLoader" -> this; + default -> null; + }; + } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/bootstrap/Inject.java b/src/main/java/ch/zhaw/gartenverwaltung/bootstrap/Inject.java new file mode 100644 index 0000000..be044c8 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/bootstrap/Inject.java @@ -0,0 +1,10 @@ +package ch.zhaw.gartenverwaltung.bootstrap; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Inject { }