package no.ntnu.idatt1002.demo.controller; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Label; import javafx.scene.control.ListView; import java.io.IOException; import java.net.URL; import java.util.Arrays; import java.util.List; import java.util.ResourceBundle; import java.util.stream.Collectors; import javafx.scene.control.Button; import javafx.scene.control.TextField; import no.ntnu.idatt1002.demo.data.recipes.FileHandler; import no.ntnu.idatt1002.demo.data.recipes.FoodItem; import no.ntnu.idatt1002.demo.data.recipes.IngredientsAtHand; /** * The AddIngredientController manages a dialog pane used to display a search-field along with a list of all the * possible food types in the application according to the FoodItem enum class. The food types in the list may be * selected and added to an up-to-date IngredientsAtHand object that is then written to file. The user may add * several food types before pressing 'close' and exit the dialog. The food types that are not already contained * in the IngredientsAtHand are listed underneath the list so that the user can keep track of the newly added * food types. Upon opening the dialog, the search field is already selected and the user can start typing a search. * When pressing 'Enter' on the keyboard, the search is performed and matches are displayed in the list. If the search * field is left blank upon search, all food types are listed again. */ public class AddIngredientController implements Initializable { private ObservableList<String> ingredients; private String[] ingredientsList; @FXML private Button addBtn; @FXML private ListView<String> listView; @FXML private TextField searchBar; @FXML private Button searchBtn; @FXML private Label status; private String statusText = "Added: "; /** * The initialize method of the controller takes in a URL (location) and ResourceBundle(resources) to * initialize the controller once its root element has been processed. * The method then sets the list items of the list, sets the focus on the search-field and makes sure that the * label underneath the list that states the added food types to the user can wrap if it fills the full width * of the window. * @param url The location to resolve the relative paths to the root object. * @param resourceBundle Resources used to localize the root object. */ @Override public void initialize(URL url, ResourceBundle resourceBundle) { listView.setItems(FXCollections.observableArrayList(Arrays.stream(FoodItem.values()).map(value -> value.label).toList())); Platform.runLater(() -> searchBar.requestFocus()); status.setWrapText(true); } /** * The addToFridge method reads an up-to-date instance of the IngredientsAtHand object from file and * gets hold of the FoodItem constant that is currently selected in the list. If the selected FoodItem is not * already at hand, it is added and the IngredientAtHand object is written to file. The label beneath the * list is also updated with the name of the food type to keep track of what has been added. If the * selected food type was already at hand, it is not added again and the label is not updated. * * @throws IOException If the method fails to write or read the Ingredients at hand object to/from file. */ @FXML void addToFridge() throws IOException { IngredientsAtHand ingredientsAtHand = FileHandler.readIngredientsAtHand("Fridge"); FoodItem item = FoodItem.valueOf(listView.getSelectionModel().getSelectedItem().replace(" ", "_").toUpperCase()); if(item != null && ingredientsAtHand!= null ) { if(!ingredientsAtHand.atHand(item)) { ingredientsAtHand.addIngredient(item); FileHandler.writeIngredientsAtHand(ingredientsAtHand, "Fridge"); if(status.isVisible() && status.getText().isBlank()) { statusText += String.format("%s", item.label); } else if (status.isVisible()){ statusText += String.format(", %s", item.label); } status.setText(statusText); } } } /** * The search method is fired whenever the 'Search' button is pressed by the user. It clears the list and then * refills it by adding all the resulting values from the call to the method 'searchList'. */ @FXML void search() { listView.getItems().clear(); listView.getItems().addAll(searchList(searchBar.getText(), Arrays.stream(FoodItem.values()).toList().stream().map(value -> value.label).toArray(String[]::new))); } /** * The searchList method takes in a string of what the user wrote in the search field and an array of * Strings that represents every label of the FoodItem enum class. The search word from the user is * trimmed and split by space and matched against all the constants of the FoodItem enums. Any matches are then * to a List of strings and returned. * @param searchWords A String of what the user wrote in the search field. * @param listOfStrings A list of strings, in this case, each representing a constant of the FoodItem enum class. * @return A list of strings food types of FoodItem that match the se search word(s). */ private List<String> searchList(String searchWords, String[] listOfStrings) { String[] searchWordsArray = searchWords.trim().split(" "); return Arrays.stream(listOfStrings).filter((in) -> Arrays.stream(searchWordsArray).allMatch((word) -> in.toLowerCase().contains(word.toLowerCase()))).collect(Collectors.toList()); } }