diff --git a/README.md b/README.md index 5b7f091..8e56120 100644 --- a/README.md +++ b/README.md @@ -27,3 +27,6 @@ These branches are for bugfixes. These branches are for javadoc and project documentation (such as the readme, class diagrams etc.). + +## User Manual +- Search Plant List: if first Char is '#': only exact match in ID. diff --git a/doc/Classdiagramm/layer-diagram.png b/doc/Classdiagramm/layer-diagram.png new file mode 100644 index 0000000..6e6fdfe Binary files /dev/null and b/doc/Classdiagramm/layer-diagram.png differ diff --git a/doc/Classdiagramm/layer-diagram.uxf b/doc/Classdiagramm/layer-diagram.uxf new file mode 100644 index 0000000..6330af5 --- /dev/null +++ b/doc/Classdiagramm/layer-diagram.uxf @@ -0,0 +1,7 @@ +10UMLPackage39060340100UIUMLPackage390180460120DomainUMLPackage390330460120Technical ServicesUMLPackage4009010060Views (JFX)UMLPackage40022010060IOUMLPackage40037010060JacksonUMLPackage51022010060TypesUMLPackage62022010060ModelsUMLPackage5109011060Controllers (JFX)UMLPackage51037010060LoggingUMLPackage62037010060JavaFXUMLPackage73022011060ServicesUMLPackage73037011060HTTP/APIRelation5701509070lt=.> +70;10;10;50Relation57029011080lt=.> +90;10;10;60Relation340120350400lt=.> +60;10;10;10;10;380;330;380;330;310Relation77027030120lt=.> +10;10;10;100Relation36025060180lt=.> +40;10;10;10;10;160;40;160Relation61012070120lt=.> +10;10;50;10;50;100 \ No newline at end of file diff --git a/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java b/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java index 0d70b44..fd31017 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java @@ -10,9 +10,9 @@ import java.io.IOException; public class HelloApplication extends Application { @Override public void start(Stage stage) throws IOException { - FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml")); - Scene scene = new Scene(fxmlLoader.load(), 320, 240); - stage.setTitle("Hello!"); + FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("MainFXML.fxml")); + Scene scene = new Scene(fxmlLoader.load()); + stage.setTitle("Gartenverwaltung"); stage.setScene(scene); stage.show(); } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/HomeController.java b/src/main/java/ch/zhaw/gartenverwaltung/HomeController.java new file mode 100644 index 0000000..7df4661 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/HomeController.java @@ -0,0 +1,5 @@ +package ch.zhaw.gartenverwaltung; + +public class HomeController +{ +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/MainFXMLController.java b/src/main/java/ch/zhaw/gartenverwaltung/MainFXMLController.java new file mode 100644 index 0000000..99a1852 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/MainFXMLController.java @@ -0,0 +1,105 @@ +package ch.zhaw.gartenverwaltung; + +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.layout.AnchorPane; + +import java.io.IOException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.ResourceBundle; + +public class MainFXMLController implements Initializable { + /** + * Caching the panes + */ + private final Map panes = new HashMap<>(); + + @FXML + private Button home_button; + + @FXML + private AnchorPane mainPane; + + @FXML + private Button myPlants_button; + + @FXML + private Button mySchedule_button; + + @FXML + private Button plants_button; + + @FXML + void goToHome(ActionEvent event) throws IOException { + loadPane("Home.fxml"); + styleChangeButton(home_button); + } + + @FXML + void goToMyPlants(ActionEvent event) throws IOException { + loadPane("MyPlants.fxml"); + styleChangeButton(myPlants_button); + } + + @FXML + void goToMySchedule(ActionEvent event) throws IOException { + loadPane("MySchedule.fxml"); + styleChangeButton(mySchedule_button); + } + + @FXML + void goToPlants(ActionEvent event) throws IOException { + loadPane("Plants.fxml"); + styleChangeButton(plants_button); + } + + /** + * Updates the mainPane with the selected fxml file. + * set HGrow and VGrow to parent AnchorPane. + * Sends MainController to other Controllers. + * @param fxmlFile string of fxml file + * @throws IOException exception when file does not exist + */ + public void loadPane(String fxmlFile) throws IOException { + //ToDo HGrow and VGrow of new node + Node node = panes.get(fxmlFile); + System.out.println(node); + if (node == null) { + FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile))); + node = loader.load(); + panes.put(fxmlFile, node); + + if(fxmlFile.equals("MyPlants.fxml")) { + MyPlantsController myPlantsController = loader.getController(); + myPlantsController.getMainController(this); + } + } + mainPane.getChildren().setAll(node); + } + + private void styleChangeButton(Button button) { + //ToDo changeStyle of the menu buttons + } + + /** + * loads the default FXML File + * {@inheritDoc} + */ + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + try { + loadPane("Home.fxml"); + styleChangeButton(home_button); + } catch (IOException e) { + e.printStackTrace(); + } + } +} + diff --git a/src/main/java/ch/zhaw/gartenverwaltung/MyPlantsController.java b/src/main/java/ch/zhaw/gartenverwaltung/MyPlantsController.java new file mode 100644 index 0000000..e223e8b --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/MyPlantsController.java @@ -0,0 +1,58 @@ +package ch.zhaw.gartenverwaltung; + +import ch.zhaw.gartenverwaltung.types.Plant; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.layout.VBox; + +import java.io.IOException; +import java.net.URL; +import java.util.LinkedList; +import java.util.List; +import java.util.ResourceBundle; + +public class MyPlantsController implements Initializable { + MainFXMLController mainController; + + @FXML + private Button addPlant_button; + + @FXML + private VBox myPlants_vbox; + + @FXML + void addPlant(ActionEvent event) throws IOException { + mainController.loadPane("Plants.fxml"); + } + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + //ToDo + List myPlants = getMyPlants(); + createPlantView(myPlants); + } + + private void createPlantView(List myPlants) { + //ToDo + for(Plant plant : myPlants) { + createPlantView(); + } + } + + public void getMainController(MainFXMLController controller) { + mainController = controller; + } + + private List getMyPlants() { + //ToDo method to get myPlantList(scheduled) + //Method to call all Plants saved + List myPlantList = new LinkedList<>(); + return myPlantList; + } + + private void createPlantView() { + //ToDo FXML Panel with Plant data + } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/MyScheduleController.java b/src/main/java/ch/zhaw/gartenverwaltung/MyScheduleController.java new file mode 100644 index 0000000..b16445a --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/MyScheduleController.java @@ -0,0 +1,124 @@ +package ch.zhaw.gartenverwaltung; + +import ch.zhaw.gartenverwaltung.types.Plant; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Pane; + +import java.net.URL; +import java.time.LocalDate; +import java.util.LinkedList; +import java.util.List; +import java.util.ResourceBundle; + +public class MyScheduleController implements Initializable { + private Plant selectedPlant = null; + + @FXML + private Label day1_label; + + @FXML + private Pane day1_pane; + + @FXML + private Label day2_label; + + @FXML + private Pane day2_pane; + + @FXML + private Label day3_label; + + @FXML + private Pane day3_pane; + + @FXML + private Label day4_label; + + @FXML + private Pane day4_pane; + + @FXML + private Label day5_label; + + @FXML + private Pane day5_pane; + + @FXML + private Label day6_label; + + @FXML + private Pane day6_pane; + + @FXML + private Label day7_label; + + @FXML + private Pane day7_pane; + + @FXML + private Label information_label; + + @FXML + private ListView scheduledPlants_listview; + + @Override + public void initialize(URL location, ResourceBundle resources) { + List plantList = new LinkedList<>(); + fillListViewMyPlantsInSchedule(plantList); + getSelectedPlantTask(); + setDayLabels(); + information_label.setText(""); + } + + private void getSelectedPlantTask() { + scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Plant oldValue, Plant newValue) { + if(newValue != null) { + selectedPlant = newValue; + //ToDo update day_panel with task for the day + } else { + selectedPlant = null; + //ToDo update day_panel with task for the day (all plants) + } + } + }); + } + + private void setDayLabels() { + LocalDate today = LocalDate.now(); + day1_label.setText(today.getDayOfWeek().toString()); + day2_label.setText(today.plusDays(1).getDayOfWeek().toString()); + day3_label.setText(today.plusDays(2).getDayOfWeek().toString()); + day4_label.setText(today.plusDays(3).getDayOfWeek().toString()); + day5_label.setText(today.plusDays(4).getDayOfWeek().toString()); + day6_label.setText(today.plusDays(5).getDayOfWeek().toString()); + day7_label.setText(today.plusDays(6).getDayOfWeek().toString()); + } + + private void fillListViewMyPlantsInSchedule(List list) { + for (Plant plant : list) { + scheduledPlants_listview.getItems().add(plant); + } + scheduledPlants_listview.setCellFactory(param -> new ListCell() { + @Override + protected void updateItem(Plant plant, boolean empty) { + super.updateItem(plant, empty); + + if (empty || plant == null || plant.name() == null) { + setText(null); + } else { + setText(plant.name()); + } + } + }); + } + + +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java b/src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java new file mode 100644 index 0000000..a3646fa --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java @@ -0,0 +1,215 @@ +package ch.zhaw.gartenverwaltung; + +import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; +import ch.zhaw.gartenverwaltung.plantList.PlantListModel; +import ch.zhaw.gartenverwaltung.types.HardinessZone; +import ch.zhaw.gartenverwaltung.types.Plant; +import javafx.application.Platform; +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.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.geometry.Bounds; +import javafx.scene.control.*; +import javafx.scene.image.ImageView; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.VBox; + +import java.io.IOException; +import java.net.URL; +import java.util.List; +import java.util.ResourceBundle; + +public class PlantsController implements Initializable { + private final PlantListModel plantListModel = new PlantListModel(); + 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()); + + @FXML + private CheckBox autum_filter; + + @FXML + private VBox climate_zones; + + @FXML + private Label description_plant; + + @FXML + private ImageView img_plant; + + @FXML + private ListView list_plants; + + @FXML + private Button saveToMyPlant_button; + + @FXML + private TextField search_plants; + + @FXML + private CheckBox sommer_filter; + + @FXML + private CheckBox spring_filter; + + @FXML + private CheckBox winter_filter; + + @FXML + void filterAutum(ActionEvent event) { + //ToDo + + } + + @FXML + void filterSommer(ActionEvent event) { + //ToDo + + } + + @FXML + void filterSpring(ActionEvent event) { + //ToDo + + } + + @FXML + void filterWinter(ActionEvent event) { + //ToDo + + } + + @FXML + void saveToMyPlant(ActionEvent event) { + //ToDo model save selectedPlant to mySelectedPlant(IO) + } + + @FXML + void searchForPlant(KeyEvent event) { + viewFilteredListBySearch(search_plants.getText()); + } + + /** + * {@inheritDoc} + */ + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + setListCellFactory(); + try { + plantListProperty.addAll(plantListModel.getPlantList(DEFAULT_HARDINESS_ZONE)); + } catch (HardinessZoneNotSetException | IOException e) { + e.printStackTrace(); + } + + list_plants.itemsProperty().bind(plantListProperty); + list_plants.getSelectionModel().selectedItemProperty() + .addListener((observable, oldPlant, newPlant) -> { + if (newPlant != null) { + img_plant.setImage(newPlant.image()); + } + }); + + description_plant.setText(""); + saveToMyPlant_button.setDisable(true); + + createFilterHardinessZone(); + lookForSelectedListEntry(); + } + + private void centerImage() { + + //img_plant.setX(0.3); + //img_plant.setX(-100); + } + private void setListCellFactory() { + list_plants.setCellFactory(param -> new ListCell() { + @Override + protected void updateItem(Plant plant, boolean empty) { + super.updateItem(plant, empty); + + if (empty || plant == null || plant.name() == null) { + setText(null); + } else { + setText(plant.name()); + } + } + }); + } + + /** + * update the ListView according to the plant list provided + * Entry in ListView is plant name + * @param list plantList which fill the ListView + */ + private void fillListViewWithData(List list) { + clearListView(); + for (Plant plant : list) { + list_plants.getItems().add(plant); + } + + } + + private void viewFilteredListByFilters() { + boolean springValue = spring_filter.isSelected(); + boolean sommerValue = sommer_filter.isSelected(); + boolean autumValue = autum_filter.isSelected(); + boolean winterValue = winter_filter.isSelected(); + //ToDo getFilteredPlantList with (plantListModel.getFilteredPlantList(DEFAULT_HARDINESS_ZONE, )) + //List plantList = new LinkedList<>(); + //fillListViewWithData(plantList); + } + + private void viewFilteredListBySearch(String query) { + //ToDo getFilteredPlantList with (plantListModel.getFilteredPlantList(DEFAULT_HARDINESS_ZONE, )) + try { + List filteredPlants = plantListModel.getFilteredPlantListByString(DEFAULT_HARDINESS_ZONE, query); + clearListView(); + plantListProperty.addAll(filteredPlants); + + } catch (HardinessZoneNotSetException | IOException e) { + e.printStackTrace(); + } + } + + private void createFilterHardinessZone() { + //ToDo create radioList of hardinessZone in VBox climate_zones + } + + /** + * observes changes in the selectedProperty of ListView and updates the description label + */ + private void lookForSelectedListEntry() { + list_plants.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Plant oldValue, Plant newValue) { + //ToDo + if(newValue != null) { + selectedPlant = newValue; + description_plant.setText(selectedPlant.description()); + saveToMyPlant_button.setDisable(false); + //update img plant + } else { + selectedPlant = null; + description_plant.setText(""); + saveToMyPlant_button.setDisable(true); + //update img when null placeholder PNG + } + } + }); + } + + + /** + * clears the ListView of entries + */ + private void clearListView() { + plantListProperty.clear(); + } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java index aa06e65..b7c982a 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java @@ -1,10 +1,13 @@ package ch.zhaw.gartenverwaltung.io; +import ch.zhaw.gartenverwaltung.json.PlantImageDeserializer; import ch.zhaw.gartenverwaltung.types.HardinessZone; import ch.zhaw.gartenverwaltung.types.Plant; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.MonthDayDeserializer; +import javafx.scene.image.Image; import java.io.IOException; import java.net.URL; @@ -31,10 +34,14 @@ public class JsonPlantDatabase implements PlantDatabase { * Creating constant objects required to deserialize the {@link MonthDay} classes */ private final static JavaTimeModule timeModule = new JavaTimeModule(); + private final static SimpleModule imageModule = new SimpleModule(); + static { DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("MM-dd"); MonthDayDeserializer dateDeserializer = new MonthDayDeserializer(dateFormat); timeModule.addDeserializer(MonthDay.class, dateDeserializer); + + imageModule.addDeserializer(Image.class, new PlantImageDeserializer()); } /** @@ -75,8 +82,9 @@ public class JsonPlantDatabase implements PlantDatabase { } if (dataSource != null) { currentZone = zone; - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(timeModule); + ObjectMapper mapper = new ObjectMapper() + .registerModule(timeModule) + .registerModule(imageModule); List result; result = mapper.readerForListOf(Plant.class).readValue(dataSource); diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java index 82d5812..a849bbc 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java @@ -9,6 +9,7 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; import java.io.File; import java.io.IOException; +import java.net.URISyntaxException; import java.net.URL; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -25,6 +26,7 @@ import java.util.Map; public class JsonTaskDatabase implements TaskDatabase{ IdProvider idProvider; private final URL dataSource = getClass().getResource("taskdb.json"); + private final static String INVALID_DATASOURCE_MSG = "Invalid datasource specified!"; private Map taskMap = Collections.emptyMap(); @@ -35,9 +37,10 @@ public class JsonTaskDatabase implements TaskDatabase{ static { DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd"); LocalDateDeserializer dateDeserializer = new LocalDateDeserializer(dateFormat); - //TODO Serializer LocalDateSerializer dateSerializer = new LocalDateSerializer(dateFormat); + timeModule.addDeserializer(LocalDate.class, dateDeserializer); + timeModule.addSerializer(LocalDate.class, dateSerializer); } /** @@ -98,12 +101,16 @@ public class JsonTaskDatabase implements TaskDatabase{ * @throws IOException If the database cannot be accessed */ private void writeTaskListToFile() throws IOException { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(timeModule) - .registerModule(new Jdk8Module()); - if(dataSource != null) { - mapper.writeValue(new File(dataSource.getFile()), taskMap); + try { + new ObjectMapper() + .registerModule(timeModule) + .registerModule(new Jdk8Module()) + .writeValue(new File(dataSource.toURI()), taskMap.values()); + + } catch (URISyntaxException e) { + throw new IOException(INVALID_DATASOURCE_MSG, e); + } } } @@ -115,7 +122,8 @@ public class JsonTaskDatabase implements TaskDatabase{ private void loadTaskListFromFile() throws IOException { if (dataSource != null) { ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(timeModule); + mapper.registerModule(timeModule) + .registerModule(new Jdk8Module()); List result; result = mapper.readerForListOf(Task.class).readValue(dataSource); diff --git a/src/main/java/ch/zhaw/gartenverwaltung/json/GrowthPhaseTypeDeserializer.java b/src/main/java/ch/zhaw/gartenverwaltung/json/GrowthPhaseTypeDeserializer.java index f3bb60f..285536a 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/json/GrowthPhaseTypeDeserializer.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/json/GrowthPhaseTypeDeserializer.java @@ -17,11 +17,12 @@ public class GrowthPhaseTypeDeserializer extends StdDeserializer { @Override public HardinessZone deserialize(JsonParser parser, DeserializationContext context) throws IOException { HardinessZone result = null; + String token = parser.getText(); try { - result = HardinessZone.valueOf(parser.getText().toUpperCase()); + result = HardinessZone.valueOf(token.toUpperCase()); } catch (IllegalArgumentException e) { // TODO: Log - System.err.println("bad growth phase type"); + System.err.printf("Unknown Hardiness Zone \"%s\"\n", token); } return result; } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/json/PlantImageDeserializer.java b/src/main/java/ch/zhaw/gartenverwaltung/json/PlantImageDeserializer.java new file mode 100644 index 0000000..f22cb35 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/json/PlantImageDeserializer.java @@ -0,0 +1,33 @@ +package ch.zhaw.gartenverwaltung.json; + +import ch.zhaw.gartenverwaltung.io.PlantDatabase; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import javafx.scene.image.Image; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; + +public class PlantImageDeserializer extends JsonDeserializer { + + @Override + public Image deserialize(JsonParser parser, DeserializationContext context) throws IOException { + Image result = null; + URL imageUrl = PlantDatabase.class.getResource(String.format("images/%s", parser.getText())); + if (imageUrl != null) { + try (InputStream is = new FileInputStream(new File(imageUrl.toURI()))) { + result = new Image(is); + } catch (IllegalArgumentException | URISyntaxException e) { + // TODO: Log + e.printStackTrace(); + System.err.printf("Cannot find Image \"%s\"\n", imageUrl.getFile()); + } + } + return result; + } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java index bd12a28..ad02864 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java @@ -3,14 +3,17 @@ package ch.zhaw.gartenverwaltung.plantList; import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; import ch.zhaw.gartenverwaltung.io.JsonPlantDatabase; import ch.zhaw.gartenverwaltung.io.PlantDatabase; +import ch.zhaw.gartenverwaltung.types.GrowthPhaseType; import ch.zhaw.gartenverwaltung.types.HardinessZone; import ch.zhaw.gartenverwaltung.types.Plant; import java.io.IOException; +import java.time.MonthDay; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.function.Predicate; +import java.util.stream.Collectors; public class PlantListModel { private PlantDatabase plantDatabase; @@ -19,8 +22,8 @@ public class PlantListModel { /** * Comparators to create sorted Plant List */ - static final Comparator sortByName = (Plant o1, Plant o2) -> o1.name().compareTo(o2.name()); - static final Comparator SortById = (Plant o1, Plant o2) -> Long.compare(o1.id(), o2.id()); + static final Comparator sortByName = Comparator.comparing(Plant::name); + static final Comparator SortById = Comparator.comparingLong(Plant::id); /** * Constructor to create Database Object. @@ -30,12 +33,12 @@ public class PlantListModel { setDefaultZone(); } - public PlantListModel(PlantDatabase plantDatabase){ + public PlantListModel(PlantDatabase plantDatabase) { this.plantDatabase = plantDatabase; setDefaultZone(); } - private void setDefaultZone(){ + private void setDefaultZone() { currentZone = HardinessZone.ZONE_8A; // TODO: get Default Zone from Config } @@ -49,6 +52,7 @@ public class PlantListModel { /** * Method to get actual Plant List in alphabetic Order + * * @return actual Plant List in alphabetic Order */ public List getPlantList(HardinessZone zone) throws HardinessZoneNotSetException, IOException { @@ -59,36 +63,39 @@ public class PlantListModel { /** * Method to get the actual Plant list in custom Order - * @param zone selected hardiness zone + * + * @param zone selected hardiness zone * @param comparator comparator to sort the list * @return sorted list with plants in the given hardiness zone - * @throws IOException If the database cannot be accessed + * @throws IOException If the database cannot be accessed * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified */ public List getSortedPlantList(HardinessZone zone, Comparator comparator) throws HardinessZoneNotSetException, IOException { setCurrentZone(zone); - return plantDatabase.getPlantList(zone).stream().sorted(comparator).toList(); + return plantDatabase.getPlantList(zone).stream().sorted(comparator).collect(Collectors.toList()); } /** * Method to get Filtered plant list + * * @param predicate predicate to filter the list - * @param zone selected hardiness zone + * @param zone selected hardiness zone * @return filterd list with plants in the hardinness zone - * @throws IOException If the database cannot be accessed + * @throws IOException If the database cannot be accessed * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified */ public List getFilteredPlantList(HardinessZone zone, Predicate predicate) throws HardinessZoneNotSetException, IOException { setCurrentZone(zone); - return getPlantList(zone).stream().filter(predicate).toList(); + return getPlantList(zone).stream().filter(predicate).collect(Collectors.toList()); } /** * Method to get Filtered plant list by id by exact match + * * @param zone selected hardiness zone - * @param id id of plant + * @param id id of plant * @return if id doesn't exist: empty List, else list with 1 plant entry. - * @throws IOException If the database cannot be accessed + * @throws IOException If the database cannot be accessed * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified */ public List getFilteredPlantListById(HardinessZone zone, Long id) throws HardinessZoneNotSetException, IOException { @@ -97,4 +104,67 @@ public class PlantListModel { plantDatabase.getPlantById(zone, id).ifPresent(plantList::add); return plantList; } + + /** + * @param zone selected hardiness zone + * @param searchString the string to search plant List, set '#' as first char the search by id. + * @return List of plants found in Plant List which contain the search String in the name or description + * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified + * @throws IOException If the database cannot be accessed + */ + public List getFilteredPlantListByString(HardinessZone zone, String searchString) throws HardinessZoneNotSetException, IOException { + if (searchString.length() == 0) { + return getPlantList(zone); + } else if (searchString.charAt(0) == '#') { + try { + return getFilteredPlantListById(zone, Long.parseLong(searchString.substring(1))); + } catch (NumberFormatException e) { + return new ArrayList<>(); + } + } else { + String caseInsensitiveSearchString = searchString.toLowerCase(); + return getFilteredPlantList(zone, plant -> + plant.name().toLowerCase().contains(caseInsensitiveSearchString) || + plant.description().toLowerCase().contains(caseInsensitiveSearchString) + ); + } + } + + + /** + * @param type GrowPhaseType to filter + * @param zone selected hardiness zone + * @param from the earliest date to for the filter + * @param to the lastest date for the filter + * @return List of Plants with selected saison + * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified + * @throws IOException If the database cannot be accessed + */ + private List getFilteredPlantListBySaison(GrowthPhaseType type, HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException { + return getFilteredPlantList(zone, plant -> plant.lifecycle().stream().anyMatch(growthPhase -> growthPhase.startDate().compareTo(from) >= 0 && (growthPhase.startDate().compareTo(to) <= 0) && growthPhase.type() == type)); + } + + /** + * @param zone selected hardiness zone + * @param from the earliest date to for the filter + * @param to the lastest date for the filter + * @return List of Plants with selected saison + * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified + * @throws IOException If the database cannot be accessed + */ + public List getFilteredPlantListByPlantingSaison(HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException { + return getFilteredPlantListBySaison(GrowthPhaseType.PLANT, zone, from, to); + } + + /** + * @param zone selected hardiness zone + * @param from the earliest date to for the filter + * @param to the lastest date for the filter + * @return List of Plants with selected saison + * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified + * @throws IOException If the database cannot be accessed + */ + public List getFilteredPlantListByHarvestSaison(HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException { + return getFilteredPlantListBySaison(GrowthPhaseType.HARVEST, zone, from, to); + } } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java index 13652a6..3250b4f 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java @@ -1,7 +1,10 @@ package ch.zhaw.gartenverwaltung.types; +import javafx.scene.image.Image; + import java.time.LocalDate; import java.util.List; +import java.util.stream.Collectors; import static java.time.temporal.ChronoUnit.DAYS; @@ -9,6 +12,7 @@ public record Plant( long id, String name, String description, + Image image, String spacing, int light, String soil, @@ -22,7 +26,7 @@ public record Plant( public List lifecycleForGroup(int group) { return lifecycle.stream() .filter(growthPhase -> growthPhase.group() != group) - .toList(); + .collect(Collectors.toList()); } public LocalDate sowDateFromHarvestDate(LocalDate harvestDate, int group) { diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/Home.fxml b/src/main/resources/ch/zhaw/gartenverwaltung/Home.fxml new file mode 100644 index 0000000..2bd24b3 --- /dev/null +++ b/src/main/resources/ch/zhaw/gartenverwaltung/Home.fxml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/MainFXML.fxml b/src/main/resources/ch/zhaw/gartenverwaltung/MainFXML.fxml new file mode 100644 index 0000000..569a89d --- /dev/null +++ b/src/main/resources/ch/zhaw/gartenverwaltung/MainFXML.fxml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/MySchedule.fxml b/src/main/resources/ch/zhaw/gartenverwaltung/MySchedule.fxml new file mode 100644 index 0000000..b778d50 --- /dev/null +++ b/src/main/resources/ch/zhaw/gartenverwaltung/MySchedule.fxml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/Plants.fxml b/src/main/resources/ch/zhaw/gartenverwaltung/Plants.fxml new file mode 100644 index 0000000..9abee94 --- /dev/null +++ b/src/main/resources/ch/zhaw/gartenverwaltung/Plants.fxml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +