Merge branch 'dev' into feature_json-gardenplan_M2
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package ch.zhaw.gartenverwaltung;
|
||||
|
||||
public class HomeController
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
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<String, AnchorPane> 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 {
|
||||
|
||||
AnchorPane 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);
|
||||
}
|
||||
}
|
||||
mainPane.getChildren().setAll(anchorPane);
|
||||
anchorPane.prefWidthProperty().bind(mainPane.widthProperty());
|
||||
anchorPane.prefHeightProperty().bind(mainPane.heightProperty());
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Plant> myPlants = getMyPlants();
|
||||
createPlantView(myPlants);
|
||||
}
|
||||
|
||||
private void createPlantView(List<Plant> myPlants) {
|
||||
//ToDo
|
||||
for(Plant plant : myPlants) {
|
||||
createPlantView();
|
||||
}
|
||||
}
|
||||
|
||||
public void getMainController(MainFXMLController controller) {
|
||||
mainController = controller;
|
||||
}
|
||||
|
||||
private List<Plant> getMyPlants() {
|
||||
//ToDo method to get myPlantList(scheduled)
|
||||
//Method to call all Plants saved
|
||||
List<Plant> myPlantList = new LinkedList<>();
|
||||
return myPlantList;
|
||||
}
|
||||
|
||||
private void createPlantView() {
|
||||
//ToDo FXML Panel with Plant data
|
||||
}
|
||||
}
|
||||
@@ -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<Plant> scheduledPlants_listview;
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
List<Plant> plantList = new LinkedList<>();
|
||||
fillListViewMyPlantsInSchedule(plantList);
|
||||
getSelectedPlantTask();
|
||||
setDayLabels();
|
||||
information_label.setText("");
|
||||
}
|
||||
|
||||
private void getSelectedPlantTask() {
|
||||
scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Plant>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Plant> observable, Plant oldValue, Plant newValue) {
|
||||
if(newValue != null) {
|
||||
selectedPlant = newValue;
|
||||
//ToDo update day<x>_panel with task for the day
|
||||
} else {
|
||||
selectedPlant = null;
|
||||
//ToDo update day<x>_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<Plant> list) {
|
||||
for (Plant plant : list) {
|
||||
scheduledPlants_listview.getItems().add(plant);
|
||||
}
|
||||
scheduledPlants_listview.setCellFactory(param -> new ListCell<Plant>() {
|
||||
@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());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
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 ch.zhaw.gartenverwaltung.types.Seasons;
|
||||
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.Insets;
|
||||
import javafx.geometry.Bounds;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.Image;
|
||||
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<Plant> plantListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||
|
||||
@FXML
|
||||
private VBox seasons;
|
||||
|
||||
@FXML
|
||||
private VBox climate_zones;
|
||||
|
||||
@FXML
|
||||
private Label description_plant;
|
||||
|
||||
@FXML
|
||||
private ImageView img_plant;
|
||||
|
||||
@FXML
|
||||
private ListView<Plant> list_plants;
|
||||
|
||||
@FXML
|
||||
private Button saveToMyPlant_button;
|
||||
|
||||
@FXML
|
||||
private TextField search_plants;
|
||||
|
||||
/**
|
||||
* saves the current selected plant in new JSON database
|
||||
* @param event event
|
||||
*/
|
||||
@FXML
|
||||
void saveToMyPlant(ActionEvent event) {
|
||||
//ToDo model save selectedPlant to mySelectedPlant(IO)
|
||||
}
|
||||
|
||||
/**
|
||||
* fill list view with current hardiness zone
|
||||
* set default values
|
||||
* create filter of season and hardiness zone
|
||||
* create event listener for selected list entry and search by query
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||
setListCellFactory();
|
||||
fillPlantListWithHardinessZone();
|
||||
list_plants.itemsProperty().bind(plantListProperty);
|
||||
|
||||
description_plant.setText("");
|
||||
saveToMyPlant_button.setDisable(true);
|
||||
|
||||
createFilterSeasons();
|
||||
createFilterHardinessZone();
|
||||
lookForSelectedListEntry();
|
||||
try {
|
||||
viewFilteredListBySearch();
|
||||
} catch (HardinessZoneNotSetException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set text of list view to plant name
|
||||
*/
|
||||
private void setListCellFactory() {
|
||||
list_plants.setCellFactory(param -> new ListCell<Plant>() {
|
||||
@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());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* get plant list according to param season and hardiness zone
|
||||
* fill list view with plant list
|
||||
* @param season enum of seasons
|
||||
* @throws HardinessZoneNotSetException throws exception
|
||||
* @throws IOException throws exception
|
||||
*/
|
||||
private void viewFilteredListBySeason(Seasons season) throws HardinessZoneNotSetException, IOException {
|
||||
clearListView();
|
||||
plantListProperty.addAll(plantListModel.getFilteredPlantListBySaisonWithoutGrowthPhase(plantListModel.getCurrentZone(), season.getStartDate(), season.getEndDate()));
|
||||
}
|
||||
|
||||
/**
|
||||
* get plant list filtered by search plant entry and hardiness zone
|
||||
* fill list view with plant list
|
||||
* @throws HardinessZoneNotSetException throws exception when no hardiness zone is defined
|
||||
* @throws IOException throws exception
|
||||
*/
|
||||
private void viewFilteredListBySearch() throws HardinessZoneNotSetException, IOException {
|
||||
search_plants.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue.isEmpty()) {
|
||||
fillPlantListWithHardinessZone();
|
||||
}else {
|
||||
try {
|
||||
List<Plant> filteredPlants = plantListModel.getFilteredPlantListByString(DEFAULT_HARDINESS_ZONE, newValue);
|
||||
clearListView();
|
||||
plantListProperty.addAll(filteredPlants);
|
||||
} catch (HardinessZoneNotSetException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* get plant list of current hardiness zone
|
||||
* fill list view with plant list
|
||||
*/
|
||||
private void fillPlantListWithHardinessZone() {
|
||||
try {
|
||||
clearListView();
|
||||
plantListProperty.addAll(plantListModel.getPlantList(plantListModel.getCurrentZone()));
|
||||
} catch (HardinessZoneNotSetException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* creates radio buttons for the hardiness zones defined in enum HardinessZone
|
||||
* defines default value as selected
|
||||
* when selected filter viewList according to hardiness zone
|
||||
*/
|
||||
private void createFilterHardinessZone() {
|
||||
ToggleGroup hardinessGroup = new ToggleGroup();
|
||||
for (HardinessZone zone : HardinessZone.values()) {
|
||||
RadioButton radioButton = new RadioButton(zone.name());
|
||||
radioButton.setToggleGroup(hardinessGroup);
|
||||
radioButton.setPadding(new Insets(0,0,10,0));
|
||||
if (zone.equals(DEFAULT_HARDINESS_ZONE)) {
|
||||
radioButton.setSelected(true);
|
||||
}
|
||||
radioButton.selectedProperty().addListener(new ChangeListener<Boolean>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
||||
plantListModel.setCurrentZone(zone);
|
||||
fillPlantListWithHardinessZone();
|
||||
}
|
||||
});
|
||||
climate_zones.getChildren().add(radioButton);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* creates radio buttons for the seasons defined in enum Seasons
|
||||
* defines default value as selected
|
||||
* when selected filter viewList according to seasons
|
||||
*/
|
||||
private void createFilterSeasons() {
|
||||
ToggleGroup seasonGroup = new ToggleGroup();
|
||||
for (Seasons season : Seasons.values()) {
|
||||
RadioButton radioButton = new RadioButton(season.name());
|
||||
radioButton.setToggleGroup(seasonGroup);
|
||||
radioButton.setPadding(new Insets(0,0,10,0));
|
||||
if (season.equals(Seasons.AllSEASONS)) {
|
||||
radioButton.setSelected(true);
|
||||
}
|
||||
radioButton.selectedProperty().addListener(new ChangeListener<Boolean>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
||||
if (season.equals(Seasons.AllSEASONS)) {
|
||||
fillPlantListWithHardinessZone();
|
||||
} else {
|
||||
try {
|
||||
viewFilteredListBySeason(season);
|
||||
} catch (HardinessZoneNotSetException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
seasons.getChildren().add(radioButton);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* observes changes in the selectedProperty of ListView and updates:
|
||||
* the description label
|
||||
* image of the plant
|
||||
*/
|
||||
private void lookForSelectedListEntry() {
|
||||
list_plants.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Plant>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Plant> observable, Plant oldValue, Plant newValue) {
|
||||
if(newValue != null) {
|
||||
selectedPlant = newValue;
|
||||
description_plant.setText(selectedPlant.description());
|
||||
saveToMyPlant_button.setDisable(false);
|
||||
Image img;
|
||||
if(selectedPlant.image() != null) {
|
||||
img = selectedPlant.image();
|
||||
} else {
|
||||
img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
|
||||
}
|
||||
img_plant.setImage(img);
|
||||
|
||||
|
||||
} else {
|
||||
selectedPlant = null;
|
||||
description_plant.setText("");
|
||||
saveToMyPlant_button.setDisable(true);
|
||||
Image img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
|
||||
img_plant.setImage(img);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* clears the ListView of entries
|
||||
*/
|
||||
private void clearListView() {
|
||||
plantListProperty.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package ch.zhaw.gartenverwaltung.gardenplan;
|
||||
|
||||
public class Gardenplanmodel {
|
||||
// private JasonGardenplan gardenplan;
|
||||
// liste von crops
|
||||
//task generieren
|
||||
//plant holen, task template mit tasktemplateklasse tasks erstellen
|
||||
}
|
||||
@@ -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,11 +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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,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<Plant> result;
|
||||
result = mapper.readerForListOf(Plant.class).readValue(dataSource);
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package ch.zhaw.gartenverwaltung.io;
|
||||
|
||||
import ch.zhaw.gartenverwaltung.types.Task;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Implements the {@link TaskDatabase} interface for loading and writing {@link Task} objects
|
||||
* from and to a JSON file.
|
||||
* The reads are cached to minimize file-io operations.
|
||||
*/
|
||||
public class JsonTaskDatabase implements TaskDatabase{
|
||||
IdProvider idProvider;
|
||||
private final URL dataSource = getClass().getResource("taskdb.json");
|
||||
|
||||
private Map<Long, Task> taskMap = Collections.emptyMap();
|
||||
|
||||
/**
|
||||
* Creating constant objects required to deserialize the {@link LocalDate} classes
|
||||
*/
|
||||
private final static JavaTimeModule timeModule = new JavaTimeModule();
|
||||
static {
|
||||
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
LocalDateDeserializer dateDeserializer = new LocalDateDeserializer(dateFormat);
|
||||
timeModule.addDeserializer(LocalDate.class, dateDeserializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* If no data is currently loaded, data is loaded from {@link #dataSource}.
|
||||
* In any case, the values of {@link #taskMap} are returned.
|
||||
*
|
||||
* @see TaskDatabase#getTaskList(LocalDate, LocalDate)
|
||||
*/
|
||||
@Override
|
||||
public List<Task> getTaskList(LocalDate start, LocalDate end) throws IOException{
|
||||
if(taskMap.isEmpty()) {
|
||||
loadTaskListFromFile();
|
||||
}
|
||||
return taskMap.values().stream().filter(task -> task.isInTimePeriode(start, end)).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* If no data is currently loaded, data is loaded from {@link #dataSource}.
|
||||
* If the {@link Task} has an id than the task is added to the {@link #taskMap}
|
||||
* otherwise the id is generated with the {@link IdProvider} before adding
|
||||
* it to the {@link #taskMap}. In any case, the {@link #taskMap} is written
|
||||
* to the {@link #dataSource}.
|
||||
*
|
||||
* @see TaskDatabase#saveTask(Task)
|
||||
*/
|
||||
@Override
|
||||
public void saveTask(Task task) throws IOException {
|
||||
if(taskMap.isEmpty()) {
|
||||
loadTaskListFromFile();
|
||||
}
|
||||
if(task.getId() == 0) {
|
||||
task.withId(idProvider.incrementAndGet());
|
||||
}
|
||||
writeTaskListToFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* If no data is currently loaded, data is loaded from {@link #dataSource}.
|
||||
* If the {@link Task}s id is found in the {@link #taskMap}, the Task is removed
|
||||
* from the {@link #taskMap}. Then the Task are written to the {@link #dataSource}.
|
||||
*
|
||||
* @see TaskDatabase#removeTask(Task)
|
||||
*/
|
||||
@Override
|
||||
public void removeTask(Task task) throws IOException {
|
||||
if(taskMap.isEmpty()) {
|
||||
loadTaskListFromFile();
|
||||
}
|
||||
if(taskMap.containsKey(task.getId())){
|
||||
taskMap.remove(task.getId());
|
||||
writeTaskListToFile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes cached data to the {@link #dataSource}.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the database from {@link #dataSource} and updates the cached data.
|
||||
*
|
||||
* @throws IOException If the database cannot be accessed
|
||||
*/
|
||||
private void loadTaskListFromFile() throws IOException {
|
||||
if (dataSource != null) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.registerModule(timeModule);
|
||||
|
||||
List<Task> result;
|
||||
result = mapper.readerForListOf(Task.class).readValue(dataSource);
|
||||
|
||||
taskMap = result.stream()
|
||||
.collect(HashMap::new,
|
||||
(res, task) -> res.put(task.getId(), task),
|
||||
(existing, replacement) -> {});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,42 @@
|
||||
package ch.zhaw.gartenverwaltung.io;
|
||||
|
||||
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
||||
import ch.zhaw.gartenverwaltung.types.Plant;
|
||||
import ch.zhaw.gartenverwaltung.types.Task;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A database of {@link Task}s.
|
||||
* The interface specifies the minimal required operations.
|
||||
*/
|
||||
public interface TaskDatabase {
|
||||
List<Task> getTaskList(Date start, Date end);
|
||||
/**
|
||||
* Yields a list of all {@link Task}s in the database with the start and end date of a period of time.
|
||||
*
|
||||
* @param start The start date of the wanted time period
|
||||
* @param end The end date of the wanted time period
|
||||
* @return A list of {@link Task}s planned in the specified time period
|
||||
* @throws IOException If the database cannot be accessed
|
||||
*/
|
||||
List<Task> getTaskList(LocalDate start, LocalDate end) throws IOException;
|
||||
|
||||
/**
|
||||
* Saves the {@link Task} in the Cache.
|
||||
*
|
||||
* @param task The {@link Task} which is wanted to be saved in the TaskList
|
||||
* @throws IOException If the database cannot be accessed
|
||||
*/
|
||||
void saveTask(Task task) throws IOException;
|
||||
|
||||
/**
|
||||
* Removes the {@link Task}from the Cache.
|
||||
*
|
||||
* @param task The {@link Task} which has to be removed from the {@link Task} list.
|
||||
* @throws IOException If the database cannot be accessed
|
||||
*/
|
||||
void removeTask(Task task) throws IOException;
|
||||
}
|
||||
|
||||
@@ -17,11 +17,12 @@ public class GrowthPhaseTypeDeserializer extends StdDeserializer<GrowthPhaseType
|
||||
@Override
|
||||
public GrowthPhaseType deserialize(JsonParser parser, DeserializationContext context) throws IOException {
|
||||
GrowthPhaseType result = null;
|
||||
String token = parser.getText();
|
||||
try {
|
||||
result = GrowthPhaseType.valueOf(parser.getText().toUpperCase());
|
||||
result = GrowthPhaseType.valueOf(token.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
// TODO: Log
|
||||
System.err.println("bad growth phase type");
|
||||
System.err.printf("Bad growth phase type \"%s\"\n", token);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -18,11 +18,12 @@ public class HardinessZoneDeserializer extends StdDeserializer<HardinessZone> {
|
||||
@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;
|
||||
}
|
||||
|
||||
@@ -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<Image> {
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
@@ -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<Plant> sortByName = (Plant o1, Plant o2) -> o1.name().compareTo(o2.name());
|
||||
static final Comparator<Plant> SortById = (Plant o1, Plant o2) -> Long.compare(o1.id(), o2.id());
|
||||
static final Comparator<Plant> sortByName = Comparator.comparing(Plant::name);
|
||||
static final Comparator<Plant> 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<Plant> 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<Plant> getSortedPlantList(HardinessZone zone, Comparator<Plant> 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<Plant> getFilteredPlantList(HardinessZone zone, Predicate<Plant> 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<Plant> getFilteredPlantListById(HardinessZone zone, Long id) throws HardinessZoneNotSetException, IOException {
|
||||
@@ -97,4 +104,80 @@ 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<Plant> 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<Plant> 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<Plant> 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<Plant> getFilteredPlantListByHarvestSaison(HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException {
|
||||
return getFilteredPlantListBySaison(GrowthPhaseType.HARVEST, 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<Plant> getFilteredPlantListBySaisonWithoutGrowthPhase(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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<GrowthPhase> lifecycleForGroup(int group) {
|
||||
return lifecycle.stream()
|
||||
.filter(growthPhase -> growthPhase.group() != group)
|
||||
.toList();
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public LocalDate sowDateFromHarvestDate(LocalDate harvestDate, int group) {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package ch.zhaw.gartenverwaltung.types;
|
||||
|
||||
import java.time.MonthDay;
|
||||
|
||||
public enum Seasons {
|
||||
AllSEASONS("--01-01", "--12-31"),
|
||||
SPRING("--03-01", "--05-30"),
|
||||
SOMMER("--06-01", "--08-30"),
|
||||
AUTUM("--09-01", "--11-30"),
|
||||
WINTER("--12-01", "--02-28");
|
||||
|
||||
public final String startDate;
|
||||
public final String endDate;
|
||||
|
||||
Seasons(String startDate, String endDate) {
|
||||
this.startDate = startDate;
|
||||
this.endDate = endDate;
|
||||
}
|
||||
|
||||
public MonthDay getStartDate() {
|
||||
return MonthDay.parse(this.startDate);
|
||||
}
|
||||
public MonthDay getEndDate() {
|
||||
return MonthDay.parse(this.endDate);
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,22 @@ public class Task {
|
||||
private Integer interval;
|
||||
private LocalDate endDate;
|
||||
|
||||
/**
|
||||
* default constructor
|
||||
* (used by Json deserializer)
|
||||
*/
|
||||
public Task(){
|
||||
name= "";
|
||||
description= "";
|
||||
startDate = LocalDate.now();
|
||||
}
|
||||
|
||||
public Task(String name, String description, LocalDate startDate) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.startDate = startDate;
|
||||
}
|
||||
|
||||
public Task(String name, String description, LocalDate startDate, LocalDate endDate, int interval) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
@@ -43,6 +54,13 @@ public class Task {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isInTimePeriode(LocalDate searchStartDate, LocalDate searchEndDate){
|
||||
if(startDate.isAfter(searchStartDate) &&startDate.isBefore(searchEndDate)){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Getters
|
||||
public long getId() { return id; }
|
||||
public String getName() { return name; }
|
||||
|
||||
Reference in New Issue
Block a user