refactor: first attempt at dependency injection

also some more renaming and improving date-picker dialog
This commit is contained in:
David Guler 2022-11-14 20:00:01 +01:00
parent 4f80a0a3e0
commit 15279838b7
14 changed files with 378 additions and 333 deletions

View File

@ -1,9 +1,10 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.PlantListModel;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Pest;
import ch.zhaw.gartenverwaltung.types.Plant;
@ -20,13 +21,23 @@ import javafx.stage.Stage;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class CropDetailController {
private Crop crop = null;
private final PlantListModel plantListModel = new PlantListModel();
private final GardenSchedule gardenSchedule = new GardenSchedule();
private final Garden garden = new Garden(gardenSchedule);
private Crop crop;
private PlantList plantList;
private GardenSchedule gardenSchedule;
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;
@ -63,9 +74,6 @@ public class CropDetailController {
@FXML
private Label spacing_label;
public CropDetailController() throws IOException {
}
@FXML
void editTaskList(ActionEvent event) {
@ -87,9 +95,11 @@ public class CropDetailController {
}
public void setPlantFromCrop(Crop crop) throws HardinessZoneNotSetException, IOException {
public void setPlantFromCrop(Crop crop) throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
this.crop = crop;
Plant plant = plantListModel.getFilteredPlantListById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).get(0);
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId())
.orElseThrow(PlantNotFoundException::new);
cropName_label.setText(plant.name());
description_label.setText(plant.description());
light_label.setText(String.valueOf(plant.light()));
@ -104,12 +114,20 @@ public class CropDetailController {
createPestList(plant);
}
private void createTaskLists(Crop crop) throws IOException {
List<Task> taskList = gardenSchedule.getTaskListForCrop(crop.getCropId().get());
for (Task task : taskList) {
Label label = new Label(task.getDescription());
growthPhases_vbox.getChildren().add(label);
}
private void createTaskLists(Crop crop) {
crop.getCropId().ifPresent(id -> {
List<Task> taskList;
try {
taskList = gardenSchedule.getTaskListForCrop(id);
for (Task task : taskList) {
Label label = new Label(task.getDescription());
growthPhases_vbox.getChildren().add(label);
}
} catch (IOException e) {
// TODO: Alert
LOG.log(Level.SEVERE, "Could not get task list for crop", e.getCause());
}
});
}
private void createPestList(Plant plant) {

View File

@ -1,17 +1,35 @@
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 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;
@ -20,9 +38,16 @@ public class MainFXMLController implements Initializable {
/**
* Caching the panes
*/
private final Map<String, AnchorPane> panes = new HashMap<>();
private final Map<String, Pane> 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);
@FXML
private Button home_button;
@ -30,7 +55,7 @@ public class MainFXMLController implements Initializable {
private AnchorPane mainPane;
@FXML
private Button myPlants_button;
private Button myGarden_button;
@FXML
private Button mySchedule_button;
@ -38,27 +63,30 @@ public class MainFXMLController implements Initializable {
@FXML
private Button plants_button;
public MainFXMLController() throws IOException {
}
@FXML
void goToHome(ActionEvent event) throws IOException {
loadPane("Home.fxml");
showPaneAsMainView("Home.fxml");
styleChangeButton(home_button);
}
@FXML
void goToMyPlants(ActionEvent event) throws IOException {
loadPane("MyPlants.fxml");
styleChangeButton(myPlants_button);
showPaneAsMainView("MyGarden.fxml");
styleChangeButton(myGarden_button);
}
@FXML
void goToMySchedule(ActionEvent event) throws IOException {
loadPane("MySchedule.fxml");
showPaneAsMainView("MySchedule.fxml");
styleChangeButton(mySchedule_button);
}
@FXML
void goToPlants(ActionEvent event) throws IOException {
loadPane("Plants.fxml");
showPaneAsMainView("Plants.fxml");
styleChangeButton(plants_button);
}
@ -69,23 +97,68 @@ public class MainFXMLController implements Initializable {
* @param fxmlFile string of fxml file
* @throws IOException exception when file does not exist
*/
public void loadPane(String fxmlFile) throws IOException {
AnchorPane anchorPane = panes.get(fxmlFile);
public void showPaneAsMainView(String fxmlFile) throws IOException {
Pane anchorPane = panes.get(fxmlFile);
if (anchorPane == null) {
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile)));
anchorPane = loader.load();
panes.put(fxmlFile, anchorPane);
if(fxmlFile.equals("MyPlants.fxml")) {
MyPlantsController myPlantsController = loader.getController();
myPlantsController.getMainController(this);
}
loadAndCacheFxml(fxmlFile);
anchorPane = panes.get(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<Method> injectMethod = Arrays.stream(controller.getClass().getMethods())
.filter(method -> method.getName().equals("injectDependencies") )
.findFirst();
if (injectMethod.isPresent()) {
Method injectee = injectMethod.get();
List<Object> 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();
}
private void styleChangeButton(Button button) {
@ -99,7 +172,8 @@ public class MainFXMLController implements Initializable {
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
try {
loadPane("Home.fxml");
preloadPanes();
showPaneAsMainView("Home.fxml");
styleChangeButton(home_button);
} catch (IOException e) {
LOG.log(Level.SEVERE, "Failed to load FXML-Pane!", e);

View File

@ -0,0 +1,140 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Plant;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MyGardenController {
private static final Logger LOG = Logger.getLogger(MyGardenController.class.getName());
MainFXMLController mainController;
private Garden garden;
private PlantList plantList;
@FXML
private VBox myPlants_vbox;
@SuppressWarnings("unused")
public void injectDependencies(Garden garden, PlantList plantList, MainFXMLController mainController) {
this.garden = garden;
this.plantList = plantList;
this.mainController = mainController;
garden.getPlantedCrops().addListener((observable, oldValue, newValue) -> {
try {
createPlantView(newValue);
} catch (HardinessZoneNotSetException | IOException e) {
LOG.log(Level.SEVERE, "Could not update view of croplist!", e);
}
});
try {
createPlantView(garden.getPlantedCrops());
} catch (HardinessZoneNotSetException | IOException e) {
LOG.log(Level.SEVERE, "Could not update view of croplist!", e);
}
}
@FXML
void addPlant(ActionEvent event) throws IOException {
mainController.showPaneAsMainView("Plants.fxml");
}
private void createPlantView(List<Crop> crops) throws HardinessZoneNotSetException, IOException {
myPlants_vbox.getChildren().clear();
for (Crop crop : crops) {
HBox hBox = createPlantView(crop);
myPlants_vbox.getChildren().add(hBox);
}
}
private HBox createPlantView(Crop crop) throws HardinessZoneNotSetException, IOException {
//ToDo add better design
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).get();
HBox hBox = new HBox(10);
ImageView imageView = new ImageView();
imageView.setPreserveRatio(false);
imageView.setFitHeight(100);
imageView.setFitWidth(100);
imageView.maxHeight(100);
if (plant.image() != null) {
imageView.setImage(plant.image());
}
hBox.setMinHeight(100);
Label label = new Label(plant.name());
label.setMaxWidth(2000);
HBox.setHgrow(label, Priority.ALWAYS);
Button details = new Button("Details");
Button delete = new Button("delete");
details.setOnAction(getGoToCropDetailEvent(crop));
delete.setOnAction(getDeleteCropEvent(crop));
hBox.getChildren().addAll(imageView, label, details, delete);
return hBox;
}
private EventHandler<ActionEvent> getGoToCropDetailEvent(Crop crop) {
return (event) -> {
try {
Stage stage = new Stage();
if (mainController.loadSceneToStage("CropDetail.fxml", stage) instanceof CropDetailController controller) {
controller.setPlantFromCrop(crop);
}
stage.initModality(Modality.APPLICATION_MODAL);
stage.setResizable(true);
stage.showAndWait();
} catch (IOException | HardinessZoneNotSetException | PlantNotFoundException e) {
// TODO: show error alert
LOG.log(Level.SEVERE, "Could not load plant details.", e);
}
};
}
private EventHandler<ActionEvent> getDeleteCropEvent(Crop crop) {
return (event) -> {
try {
showConfirmation(crop);
} catch (IOException | HardinessZoneNotSetException e) {
e.printStackTrace();
}
};
}
private void showConfirmation(Crop crop) throws IOException, HardinessZoneNotSetException {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Delete Crop");
alert.setHeaderText("Are you sure want to delete this Crop?");
alert.setContentText("placeholder");
alert.showAndWait()
.ifPresent(buttonType -> {
if (buttonType == ButtonType.OK) {
try {
garden.removeCrop(crop);
} catch (IOException e) {
// TODO: Show error alert
LOG.log(Level.SEVERE, "Could not remove crop.", e);
}
}
});
}
}

View File

@ -1,160 +0,0 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.PlantListModel;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Plant;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.*;
public class MyPlantsController implements Initializable {
MainFXMLController mainController;
private final GardenSchedule gardenSchedule = new GardenSchedule();
private final Garden garden = new Garden(gardenSchedule);
private final PlantListModel plantListModel = new PlantListModel();
@FXML
private VBox myPlants_vbox;
public MyPlantsController() throws IOException {
}
@FXML
void addPlant(ActionEvent event) throws IOException {
mainController.loadPane("Plants.fxml");
}
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
//ToDo update, when new crops are added
try {
loadCropList();
} catch (HardinessZoneNotSetException | IOException e) {
e.printStackTrace();
}
}
private void loadCropList() throws HardinessZoneNotSetException, IOException {
List<Crop> cropList = new LinkedList<>();
try {
cropList = getCropList();
} catch (IOException e) {
e.printStackTrace();
}
createPlantView(cropList);
}
private void createPlantView(List<Crop> crops) throws HardinessZoneNotSetException, IOException {
myPlants_vbox.getChildren().clear();
for(Crop crop : crops) {
HBox hBox = createPlantView(crop);
myPlants_vbox.getChildren().add(hBox);
}
}
public void getMainController(MainFXMLController controller) {
mainController = controller;
}
private List<Crop> getCropList() throws IOException {
List<Crop> cropList;
cropList = garden.getCrops();
return cropList;
}
private HBox createPlantView(Crop crop) throws HardinessZoneNotSetException, IOException {
//ToDo add better design
Plant plant = plantListModel.getFilteredPlantListById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).get(0);
HBox hBox = new HBox(10);
ImageView imageView = new ImageView();
imageView.setPreserveRatio(false);
imageView.setFitHeight(100);
imageView.setFitWidth(100);
imageView.maxHeight(100);
if (plant.image() != null) {
imageView.setImage(plant.image());
}
hBox.setMinHeight(100);
Label label = new Label(plant.name());
label.setMaxWidth(2000);
HBox.setHgrow(label, Priority.ALWAYS);
Button details = new Button("Details");
Button delete = new Button("delete");
details.setOnAction(getGoToCropDetailEvent(crop));
delete.setOnAction(getDeleteCropEvent(crop));
hBox.getChildren().addAll(imageView, label, details, delete);
return hBox;
}
private EventHandler<ActionEvent> getGoToCropDetailEvent(Crop crop) {
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Parent root;
FXMLLoader fxmlLoader = new FXMLLoader(Objects.requireNonNull(getClass().getResource("CropDetail.fxml")));
try {
root = fxmlLoader.load();
CropDetailController controller = fxmlLoader.getController();
controller.setPlantFromCrop(crop);
Stage stage = new Stage();
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
stage.setResizable(true);
stage.showAndWait();
} catch (IOException | HardinessZoneNotSetException e) {
e.printStackTrace();
}
}
};
return event;
}
private EventHandler<ActionEvent> getDeleteCropEvent(Crop crop) {
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
try {
showConfirmation(crop);
} catch (IOException | HardinessZoneNotSetException e) {
e.printStackTrace();
}
}
};
return event;
}
private void showConfirmation(Crop crop) throws IOException, HardinessZoneNotSetException {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Delete Crop");
alert.setHeaderText("Are you sure want to delete this Crop?");
alert.setContentText("placeholder");
Optional<ButtonType> option = alert.showAndWait();
if (option.get() == ButtonType.OK) {
garden.removeCrop(crop);
loadCropList();
}
}
}

View File

@ -1,18 +1,16 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.PlantListModel;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
@ -20,20 +18,29 @@ import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import java.io.IOException;
import java.net.URL;
import java.time.LocalDate;
import java.util.LinkedList;
import java.util.List;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MyScheduleController {
private static final Logger LOG = Logger.getLogger(MyScheduleController.class.getName());
public class MyScheduleController implements Initializable {
private Crop selectedCrop = null;
private final GardenSchedule gardenSchedule = new GardenSchedule();
private final Garden garden = new Garden(gardenSchedule);
private final PlantListModel plantListModel = new PlantListModel();
private GardenSchedule gardenSchedule;
private Garden garden;
private PlantList plantList;
private final ListProperty<Crop> 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;
@ -82,11 +89,7 @@ public class MyScheduleController implements Initializable {
@FXML
private ListView<Crop> scheduledPlants_listview;
public MyScheduleController() throws IOException {
}
@Override
public void initialize(URL location, ResourceBundle resources) {
public void init() {
List<Crop> cropList;
try {
cropList = garden.getCrops();
@ -107,15 +110,12 @@ public class MyScheduleController implements Initializable {
}
private void lookForSelectedListEntries() {
scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Crop>() {
@Override
public void changed(ObservableValue<? extends Crop> observable, Crop oldValue, Crop newValue) {
selectedCrop = newValue;
try {
loadTaskList();
} catch (IOException e) {
e.printStackTrace();
}
scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
selectedCrop = newValue;
try {
loadTaskList();
} catch (IOException e) {
e.printStackTrace();
}
});
}
@ -132,7 +132,7 @@ public class MyScheduleController implements Initializable {
}
private void setCellFactoryListView() {
scheduledPlants_listview.setCellFactory(param -> new ListCell<Crop>() {
scheduledPlants_listview.setCellFactory(param -> new ListCell<>() {
@Override
protected void updateItem(Crop crop, boolean empty) {
super.updateItem(crop, empty);
@ -141,9 +141,12 @@ public class MyScheduleController implements Initializable {
setText(null);
} else {
try {
setText(plantListModel.getFilteredPlantListById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).get(0).name());
String text = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId())
.map(Plant::name)
.orElse("");
setText(text);
} catch (HardinessZoneNotSetException | IOException e) {
e.printStackTrace();
LOG.log(Level.WARNING, "Could not get plant for Cell", e);
}
}
}
@ -151,7 +154,7 @@ public class MyScheduleController implements Initializable {
}
private void loadTaskList() throws IOException {
List<List<Task>> taskLists = new LinkedList<>();
List<List<Task>> taskLists;
if (selectedCrop != null) {
taskLists = gardenSchedule.getTasksUpcomingWeekForCrop(selectedCrop.getCropId().get());
} else {

View File

@ -10,11 +10,7 @@ import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
@ -23,22 +19,27 @@ import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
public class PlantsController implements Initializable {
public class PlantsController {
private static final Logger LOG = Logger.getLogger(PlantsController.class.getName());
private final PlantListModel plantListModel = new PlantListModel();
private PlantListModel plantListModel;
private MainFXMLController mainController;
private Plant selectedPlant = null;
private final HardinessZone DEFAULT_HARDINESS_ZONE = HardinessZone.ZONE_8A;
// TODO: move to model
private final ListProperty<Plant> 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;
@ -66,13 +67,11 @@ public class PlantsController implements Initializable {
*/
@FXML
void selectSowDate(ActionEvent event) throws IOException {
Parent root;
FXMLLoader fxmlLoader = new FXMLLoader(Objects.requireNonNull(getClass().getResource("SelectSowDay.fxml")));
root = fxmlLoader.load();
SelectSowDayController controller = fxmlLoader.getController();
controller.getSelectedPlant(selectedPlant);
Stage stage = new Stage();
stage.setScene(new Scene(root));
if (mainController.loadSceneToStage("SelectSowDay.fxml", stage) instanceof SelectSowDayController controller) {
controller.setSelectedPlant(selectedPlant);
}
stage.initModality(Modality.APPLICATION_MODAL);
stage.setResizable(false);
stage.showAndWait();
@ -85,8 +84,7 @@ public class PlantsController implements Initializable {
* create event listener for selected list entry and search by query
* {@inheritDoc}
*/
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
public void init() {
setListCellFactory();
fillPlantListWithHardinessZone();
list_plants.itemsProperty().bind(plantListProperty);

View File

@ -3,7 +3,6 @@ package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.types.GrowthPhaseType;
import ch.zhaw.gartenverwaltung.types.Plant;
import javafx.event.ActionEvent;
@ -21,8 +20,12 @@ import java.util.ResourceBundle;
public class SelectSowDayController implements Initializable {
private Plant selectedPlant = null;
private final GardenSchedule gardenSchedule = new GardenSchedule();
private final Garden garden = new Garden(gardenSchedule);
private Garden garden;
@SuppressWarnings("unused")
public void injectDependencies(Garden garden) {
this.garden = garden;
}
@FXML
private DatePicker datepicker;
@ -34,9 +37,7 @@ public class SelectSowDayController implements Initializable {
private Button save_button;
@FXML
private RadioButton sow_radio;
public SelectSowDayController() throws IOException {}
private RadioButton harvest_radio;
/**
* close the date selector window
@ -54,10 +55,8 @@ public class SelectSowDayController implements Initializable {
*/
@FXML
void save(ActionEvent event) throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
LocalDate sowDate;
if (sow_radio.isSelected()) {
sowDate = datepicker.getValue();
} else {
LocalDate sowDate = datepicker.getValue();
if (harvest_radio.isSelected()) {
//ToDo method to get current lifecycle group in plant
sowDate = selectedPlant.sowDateFromHarvestDate(datepicker.getValue(), 0);
}
@ -69,9 +68,9 @@ public class SelectSowDayController implements Initializable {
* save the plant which will be planted and update label
* @param plant Plant
*/
public void getSelectedPlant(Plant plant) {
public void setSelectedPlant(Plant plant) {
selectedPlant = plant;
popup_label.setText("Select Harvest/Sow Date for" + selectedPlant.name());
popup_label.setText(String.format("Select Harvest/Sow Date for %s:", selectedPlant.name()));
}
/**
@ -86,16 +85,16 @@ public class SelectSowDayController implements Initializable {
Callback<DatePicker, DateCell> dayCellFactory= getDayCellFactory();
datepicker.setDayCellFactory(dayCellFactory);
datepicker.getEditor().setEditable(false);
datepicker.setEditable(false);
enableDisableSaveButton();
save_button.disableProperty().bind(datepicker.valueProperty().isNull());
}
/**
* clear date picker editor when radio button is changed
*/
private void clearDatePickerEntries() {
sow_radio.selectedProperty().addListener((observable, oldValue, isNowSelected) -> datepicker.getEditor().clear());
harvest_radio.selectedProperty().addListener((observable, oldValue, isNowSelected) -> datepicker.setValue(null));
}
/**
@ -104,40 +103,33 @@ public class SelectSowDayController implements Initializable {
*/
private Callback<DatePicker, DateCell> getDayCellFactory() {
final Callback<DatePicker, DateCell> dayCellFactory = new Callback<DatePicker, DateCell>() {
return (datePicker) -> new DateCell() {
@Override
public DateCell call(final DatePicker datePicker) {
return new DateCell() {
@Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
setDisable(true);
setStyle("-fx-background-color: #ffc0cb;");
List<LocalDate> dates;
LocalDate today = LocalDate.now();
if (sow_radio.isSelected()) {
dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.SOW);
} else {
dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.HARVEST);
}
for (LocalDate date : dates) {
if (item.getMonth() == date.getMonth()
&& item.getDayOfMonth() == date.getDayOfMonth()
&& item.compareTo(today) > 0) {
setDisable(false);
setStyle("-fx-background-color: #32CD32;");
}
}
if ((!sow_radio.isSelected() && selectedPlant.sowDateFromHarvestDate(item, 0).compareTo(today) < 0)) {
setDisable(true);
setStyle("-fx-background-color: #ffc0cb;");
}
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
setDisable(true);
setStyle("-fx-background-color: #ffc0cb;");
List<LocalDate> dates;
LocalDate today = LocalDate.now();
if (harvest_radio.isSelected()) {
dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.HARVEST);
} else {
dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.SOW);
}
for (LocalDate date : dates) {
if (item.getMonth() == date.getMonth()
&& item.getDayOfMonth() == date.getDayOfMonth()
&& item.compareTo(today) > 0) {
setDisable(false);
setStyle("-fx-background-color: #32CD32;");
}
};
}
if ((harvest_radio.isSelected() && selectedPlant.sowDateFromHarvestDate(item, 0).compareTo(today) < 0)) {
setDisable(true);
setStyle("-fx-background-color: #ffc0cb;");
}
}
};
return dayCellFactory;
}
/**
@ -147,18 +139,4 @@ public class SelectSowDayController implements Initializable {
Stage stage = (Stage) save_button.getScene().getWindow();
stage.close();
}
/**
* disable save button, when there is no date selected in date picker
*/
private void enableDisableSaveButton() {
save_button.setDisable(true);
datepicker.getEditor().textProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == null || newValue.equals("")) {
save_button.setDisable(true);
} else {
save_button.setDisable(false);
}
});
}
}

View File

@ -2,12 +2,12 @@ package ch.zhaw.gartenverwaltung.models;
import ch.zhaw.gartenverwaltung.io.CropList;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.io.JsonCropList;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.io.IOException;
import java.time.LocalDate;
@ -19,21 +19,25 @@ import java.util.Optional;
* The Gardenplan model manages the crops in the gardenplan.
*/
public class Garden {
private CropList cropList;
private final ObservableList<Crop> plantedCrops = FXCollections.observableArrayList();
private GardenSchedule gardenSchedule;
private final CropList cropList;
private final ListProperty<Crop> plantedCrops = new SimpleListProperty<>(FXCollections.observableArrayList());
private final GardenSchedule gardenSchedule;
/**
* Constructor of Gardenplan model
*
* @param gardenSchedule holds a reference to the task list object.
*/
public Garden(GardenSchedule gardenSchedule) throws IOException {
public Garden(GardenSchedule gardenSchedule, CropList cropList) throws IOException {
this.gardenSchedule = gardenSchedule;
cropList = new JsonCropList();
this.cropList = cropList;
plantedCrops.addAll(cropList.getCrops());
}
public ListProperty<Crop> getPlantedCrops() {
return plantedCrops;
}
/**
* Creates a Crop with a {@link Plant} and the planting date of the plant. Then let the Tasklistmodel create the
* gardening {@link Task} for the crop. Store the crop in the gardenplan file and the cache.

View File

@ -12,19 +12,14 @@ import java.util.List;
import java.util.stream.Collectors;
public class GardenSchedule {
private TaskList taskList;
private PlantList plantList;
private final TaskList taskList;
private final PlantList plantList;
/**
* Comparators to create sorted Task List
*/
static final Comparator<Task> sortByStartDate = Comparator.comparing(Task::getStartDate);
public GardenSchedule(){
taskList = new JsonTaskList();
plantList = new JsonPlantList();
}
/**
* Constructor to create Database Objects.
*/

View File

@ -1,7 +1,6 @@
package ch.zhaw.gartenverwaltung.models;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.io.JsonPlantList;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.types.GrowthPhaseType;
import ch.zhaw.gartenverwaltung.types.HardinessZone;
@ -16,7 +15,7 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
public class PlantListModel {
private PlantList plantList;
private final PlantList plantList;
private HardinessZone currentZone;
/**
@ -28,11 +27,6 @@ public class PlantListModel {
/**
* Constructor to create Database Object.
*/
public PlantListModel() {
plantList = new JsonPlantList();
setDefaultZone();
}
public PlantListModel(PlantList plantList) {
this.plantList = plantList;
setDefaultZone();

View File

@ -9,6 +9,8 @@ module ch.zhaw.gartenverwaltung {
opens ch.zhaw.gartenverwaltung to javafx.fxml;
opens ch.zhaw.gartenverwaltung.types to com.fasterxml.jackson.databind;
exports ch.zhaw.gartenverwaltung;
exports ch.zhaw.gartenverwaltung.io;
exports ch.zhaw.gartenverwaltung.types;
exports ch.zhaw.gartenverwaltung.models;
exports ch.zhaw.gartenverwaltung.json;
}

View File

@ -11,8 +11,8 @@
<children>
<Button fx:id="home_button" mnemonicParsing="false" onAction="#goToHome" prefHeight="38.0" prefWidth="121.0" text="Home" HBox.hgrow="NEVER" />
<Button fx:id="plants_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToPlants" prefHeight="38.0" prefWidth="121.0" text="Plants" />
<Button fx:id="myPlants_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMyPlants" prefHeight="38.0" prefWidth="121.0" text="MyPlants" />
<Button fx:id="mySchedule_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMySchedule" prefHeight="38.0" prefWidth="121.0" text="MySchedule" />
<Button fx:id="myGarden_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMyPlants" prefHeight="38.0" prefWidth="121.0" text="My Garden" />
<Button fx:id="mySchedule_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMySchedule" prefHeight="38.0" prefWidth="121.0" text="My Schedule" />
<Pane maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS" />
</children>
</HBox>

View File

@ -7,11 +7,11 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="655.0" prefWidth="1175.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.MyPlantsController">
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="655.0" prefWidth="1175.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.MyGardenController">
<children>
<VBox layoutY="49.0" prefHeight="655.0" prefWidth="1175.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Label text="MyPlants">
<Label text="My Garden">
<font>
<Font name="System Bold" size="28.0" />
</font>

View File

@ -1,9 +1,6 @@
package ch.zhaw.gartenverwaltung.models;
import ch.zhaw.gartenverwaltung.io.*;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.types.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -13,6 +10,7 @@ import java.time.LocalDate;
import java.time.MonthDay;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -70,7 +68,7 @@ public class GardenPlanModelTest {
exampleCrop2 = new Crop(1,LocalDate.of(2023,3,1));
exampleCrop2.withId(1);
exampleCrop2.withArea(0.5);
exampleCrop3 = new Crop(0,LocalDate.of(2023,3,01));
exampleCrop3 = new Crop(0,LocalDate.of(2023,3,1));
exampleCrop3.withId(2);
exampleCrop3.withArea(1.0);
@ -81,7 +79,7 @@ public class GardenPlanModelTest {
cropList = mockGardenPlan(exampleCrops);
GardenSchedule gardenSchedule = new GardenSchedule(new JsonTaskList(), new JsonPlantList());
model = new Garden(gardenSchedule);
model = new Garden(gardenSchedule, cropList);
}
CropList mockGardenPlan(List<Crop> cropList) throws IOException {
@ -96,15 +94,16 @@ public class GardenPlanModelTest {
void plantAsCrop() throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
model.plantAsCrop(examplePlantOnion, LocalDate.of(2023,3,1));
exampleCropOnion = model.getCrop(2L).get();
assertEquals(model.getCrops().get(2),exampleCropOnion);
Optional<Crop> exampleCrop = model.getCrop(2L);
assertTrue(exampleCrop.isPresent());
assertEquals(model.getCrops().get(2), exampleCrop.get());
}
@Test
void removeCrop() throws IOException {
exampleCrop1.withId(2);
exampleCrop1.withArea(1.500000);
exampleCrop1.withArea(1.5);
model.removeCrop(exampleCrop1);
assertEquals(2,model.getCrops().size());
}