Introduction

The aim of the assignment is to create an application to maintain a collection of groceries stored in your fridge. The groceries and a list of items are stored in a MySQL database.

A SQL script is provided to create the tables in the mysql database. Note:

  • 2 tables will be created upon execution of the script: a grocery table and an item table
  • you should change the name of the database in the script if you are using the MySQL database on latcs7. In that case, the name of the database will be your username not fridge.
  • Sample records will also be added to these 2 tables

The application consists of the following classes:

  • Grocery
  • Item
  • FridgeDSC
  • FridgeFX

These classes are either provided as complete java files or as templates that you must complete. A css file is also provided.

Grocery

The Grocery class represents a grocery item bought and stored in your fridge. You do not have to add anything to this class, the complete class code is provided. As can be seen from the provided code, the class has the following attributes:

  • id: the unique identifier of a grocery.
  • item: an instance of the Item class.
  • Item class, (provided) has the following attributes:
    • name: the name of an item from which you can pick to make a Grocery object
    • expires: whether or not this item can expire
  • date: the date the grocery item was purchased/added to the fridge.
  • quantity: quantity of such grocery item bought/added to the fridge at the same time.
  • section: which section of the fridge is the grocery stored.

The id attribute of a grocery is of type int and is auto generated by the database, as can be seen in the SQL script. The Grocery class has a main method so that you can compile it on its own and test to make sure that it works. See the code.

Item

The second class, Item, has a one to one mapping with the Grocery class where Grocery class has one (and only one) instance of Item. You do not have to add anything to this class, the complete class code is provided. As can be seen from the provided code, the class has following attributes:

  • name: the unique identifier for an Item
  • expires: a Boolean specifying if an Item has an expiration date or not. The default is False.

The Item class also has a main method for testing.

FridgeDSC

The third class, FridgeDSC, is the data source controller. A skeleton of this class is provided.

You should use this class to create methods to send SQL commands to database. You will send and receive data.

FridgeFX

The fourth class, FridgeFX, provides the graphical user interface for the users to interact with the system. This class is implemented in JavaFX. A skeleton for this class is provided.

You should use this class to create methods to create the GUI aspects of the system.

Task 1

Implement the FridgeDSC class. A skeleton of the class is provided, which indicates the methods that you are required to implement. Note the following database management methods

DriverManager
Connection
Statement
PreparedStatement

There are method stubs that you will need to connect to a database/table and perform the required task - the SQL queries (String variable) for each of the method will be provided in the code.

NOTE: Carefully read the comments provided throughout the skeleton class - they provide useful hints on how to proceed with each method stubs/tasks. You are required to implement each part labelled with

/* TODO 1-xx - TO COMPLETE ****************************************

Task 2

Implement the FridgeFX class. When the system is started, a screen similar to the one shown in Figure 1 is displayed. Your FrigdeFX class should create an instance of FridgeDSC class and use that object instance methods to perform the create, read, update, delete (CRUD) operations described below;

NOTE: Carefully read the comments provided throughout the skeleton class - they provide useful hints on how to proceed with each method stubs/tasks. You are required to implement each part labelled with

/* TODO 2-xx - TO COMPLETE ****************************************

Describing the User Interface Flow/Requirements

Figure 1.1 shows the main interactive controls at the top of the application.

Figure 1.1 - "Filter By:" ChoiceBox in action see image.

  • A TextField - this allows the user to filter the TableView data
  • A ChoiceBox - this allows the user to select which Column of the TableView to use as filter target
    • When ITEM option (default selection) is selected, the TextField filter will target the values in the TableView "Item" column.
    • When SECTION option is selected, the TextField filter will target the values in the TableView "Section" column.
    • When BOUGHT_DAYS_AGO is selected, it does the following:
    • Enables the "Show Expire Only" CheckBox
    • Sets the TextField filter to the TableView "Bought" column (only it's numerical value), listing all groceries bought on the filtered value days and prior
  • A CheckBox - this is currently disabled; this control is enabled when the "Filter By:" BOUGHT_DAYS_AGO option is selected.

NOTE: the Grocery class has an attribute of type Item class (both classes are provided to you); Item class has a boolean expires attribute; The "Show Expire Only" Checkbox, when selected, will also filter out those groceries elements having an item with attribute expires set to true;

Figure 1.2 - BOUGHT_DAYS AGO filter, "Show Expire Only" not selected. Paddle Pops don't Expire: see image.

Figure 1.3 - BOUGHT_DAYS AGO filter, "Show Expire Only" selected. Paddle Pops are now not displayed: see image.

The next control is the TableView.

The TableView displays the groceries in the collection, one grocery per row. Each row of the TableView is selectable; In order to use the "UPDATE ONE" or the DELETE button, the relevant TableView row (a grocery) must be selected by the user;

Adding a new grocery:

Clicking the "ADD" button reveals a hidden container (see Figure 2)

Figure 2 - Adding a grocery: see image.

You are then provided with 3 controls:

  • A ComboBox - lists all Items available by item name.
  • A ChoiceBox - listing the possible sections in the fridge; the section values are defined as an enum in FridgeDSC class. (See Figure 2.2). Look at the code in FridgeDSC.java
  • A TextField - user input for quantity of selected item the user is about to add to the fridge.

Figure 2.1 - the "Item" ComboBox listing: see image.

Figure 2.2 - the "Section" ChoiceBox listing: see image.

After selecting/entering some grocery information in the add controls, click the SAVE button, to create a new grocery entry (see Figure 2.3 and 2.4)

Figure 2.3 - entering new grocery information: see image.

Figure 2.4 - saved new grocery, listed in the TableView: see image.

Update One grocery

The UPDATE ONE button decreases the quantity of a selected grocery in the TableView by one. If the selected grocery is already one it will prompt a relevant error message.

Figure 2.5 - try to UPDATE ONE grocery with quantity = 1 see image.

Figure 2.4 - successfully UPDATE ONE action on grocery (id: 33) with quantity = 3, now quantity = 2: see image.

Delete (one) Grocery

The DELETE button prompts user with a confirmation, and if user accepts (clicks OK button) deletes the selected grocery.

Figure 2.5 - DELETE selected grocery, user prompted for confirmation: see image.

Figure 2.6 - successful DELETE of grocery (id: 19) confirmation: see image.

Exit from the application

The Exit button prompts user with a confirmation, and if user accepts (clicks OK button) exits the application. The groceries seen in the application should be synced to the mysql database so that when the application is started again the grocery state is maintained. Note that this could be happening all the time

Figure 2.7 - EXIT user prompted for confirmation: see image.

Figure 2.8 - The application when restarted: see image.

Use adequate means of alerting users of relevant events

Make sure you add a notification mechanism (example: Alert boxes) to Prompt user before executing a critical action (an add, an update or a delete) Inform user when an error has occurred; see Exception Handling for more requirements on error handling

Exception Handling

Make sure you add the exception handling code so that whenever an exception occurs, the program displays an alert which shows a brief message about the exception.

Implementation Suggestion

There are two approaches.

1. Start with the FridgeDSC.java, this has all the required interaction with the database, and includes all methods needed for the UI class (FridgeFX). Examine the main method in your FridgeDSC class and test all the needed required methods; see your lab sample solutions for some examples. Then Work on the FridgeFX.java. The disadvantage of this approach is that you need to wait until the lectures/labs on SQL are done.

2. In the JavaFX GUI class (FridgeFX), the is a commented-out section that includes the code for creating a list of groceries. If you use this commented out section for the TableView you can get the control and container elements done. Add Lambda function code stubs for the Buttons setOnAction methods and add the relevant Alert boxes. Then later when you have done the FridgeDSC.java you can replace the hard coded grocery list with the list that comes back from the DSC. The advantage of this is that you can start earlier, and don't have to wait for the SQL lectures.

Starter Code

CreateDatabaseScript.sql

-- MySQL dump 10.13 Distrib 5.7.16, for Win64 (x86_64)
--
-- Host: localhost Database: fridgedb
-- ------------------------------------------------------
-- Server version 5.7.16-log

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;


/* If running on a local mysql install use fridgedb as the database name */

DROP DATABASE IF EXISTS `fridgedb`;
CREATE DATABASE `fridgedb` ;
USE `fridgedb`;


/*
* the URL for a local mysql install is jdbc:mysql://localhost:3306/fridgedb
*/

--
-- Table structure for table `item`
--

DROP TABLE IF EXISTS `item`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `item` (
`name` varchar(20) NOT NULL,
`expires` tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`name`),
UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `item`
--

LOCK TABLES `item` WRITE;
/*!40000 ALTER TABLE `item` DISABLE KEYS */;
INSERT INTO `item` VALUES ('Beef',1),('Broccoli',1),('Cabbage',1),('Fish',1),('Frozen Yogurt',0),('Cottage Cheese',1),('Milk',0),('Oranges',0),('Paddle Pop',0),('Pecorino',1),('Tangerines',0),('Tofu',0);
/*!40000 ALTER TABLE `item` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;


--
-- Table structure for table `grocery`
--

DROP TABLE IF EXISTS `grocery`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `grocery` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`itemName` varchar(20) NOT NULL,
`date` varchar(10) DEFAULT NULL,
`quantity` int(11) DEFAULT NULL,
`section` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`),
KEY `itemName_idx` (`itemName`),
CONSTRAINT `fk_grocery_item` FOREIGN KEY (`itemName`) REFERENCES `item` (`name`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `grocery`
--

LOCK TABLES `grocery` WRITE;
/*!40000 ALTER TABLE `grocery` DISABLE KEYS */;
INSERT INTO `grocery` VALUES (6,'Paddle Pop','17/07/2020',1,'FREEZER'),(19,'Fish','20/07/2020',1,'MEAT'),(33,'Beef','24/07/2020',3,'MEAT');
/*!40000 ALTER TABLE `grocery` ENABLE KEYS */;
UNLOCK TABLES;


/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2019-08-18 9:42:47

fridge.css

.root {
-fx-base: #29c39f; /* mint green */
-fx-font-size: 20;
-fx-alignment: center;
}

.theme {
-fx-background-color: derive(-fx-base, 50%);
-fx-font-size: 14px;
}

.split-pane {
-fx-padding: 0;
-fx-border-width: 0;
-fx-background-color: derive(-fx-base, 100%);
}

.text-dropshadow {
-fx-background-radius: 4, 3;
-fx-background-insets: 0, 1;
-fx-effect: dropshadow( three-pass-box , rgba(41,195,159,1.0) , 10, 0.0 , 0 , 1 );
-fx-text-fill: derive(-fx-base, -60%);
-fx-padding: 3 11 3 11;
}

.label {
-fx-text-fill: black;
-fx-font-size: 12px;
}

.error{
-fx-background-color: red,linear-gradient(to bottom, derive(red,60%) 5%,derive(red,90%) 40%);
}

FridgeDSC.java

import java.sql.*;
import java.util.*;
import java.io.File;
import java.io.PrintWriter;
import java.util.Scanner;
import java.time.LocalDate;
import java.time.Duration;
import java.time.format.DateTimeFormatter;

public class FridgeDSC {

// the date format we will be using across the application
public static final String DATE_FORMAT = "dd/MM/yyyy";

/*
FREEZER, // freezing cold
MEAT, // MEAT cold
COOLING, // general fridge area
CRISPER // veg and fruits section

note: Enums are implicitly public static final
*/
public enum SECTION {
FREEZER,
MEAT,
COOLING,
CRISPER
};

private static Connection connection;
private static Statement statement;
private static PreparedStatement preparedStatement;

public static void connect() throws SQLException {
try {
Class.forName("com.mysql.jdbc.Driver"); // You may need to comment out this line


/* TODO 1-01 - TO COMPLETE ****************************************
* set the value of the string for the following 3 lines:
* - url
* - user
* - password
*
* i.e. for latcs7 with a user id of 12342344
*
* String url = "jdbc:mysql://latcs7.cs.latrobe.edu.au:3306/12342344"; // the database is the user id
* String user = "12342344"; // user is the student id
* String password = "JKJSFTRWVGV&"; // look in your unix account for a text file with your password details
*
* For a local mysql installation use something like
*
* String url = "jdbc:mysql://localhost:3306/fridgedb";
* String user = "root";
* String password = "1234";
*
*
*/

connection = DriverManager.getConnection(url, user, password);
statement = connection.createStatement();
} catch(Exception e) {
System.out.println(e);
e.printStackTrace();
}
}

public static void disconnect() throws SQLException {
if(preparedStatement != null) preparedStatement.close();
if(statement != null) statement.close();
if(connection != null) connection.close();
}



public Item searchItem(String name) throws Exception {
String queryString = "SELECT * FROM item WHERE name = ?";


/* TODO 1-02 - TO COMPLETE ****************************************
* - preparedStatement to add argument name to the queryString
* - resultSet to execute the preparedStatement query
* - iterate through the resultSet result
*/


Item item = null;

if (rs.next()) { // i.e. the item exists

/* TODO 1-03 - TO COMPLETE ****************************************
* - if resultSet has result, get data and create an Item instance
*/

}


return item;
}

public Grocery searchGrocery(int id) throws Exception {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(DATE_FORMAT);
String queryString = "SELECT * FROM grocery WHERE id = ?";

/* TODO 1-04 - TO COMPLETE ****************************************
* - preparedStatement to add argument name to the queryString
* - resultSet to execute the preparedStatement query
* - iterate through the resultSet result
*/


Grocery grocery = null;

if (rs.next()) { // i.e. the grocery exists

/* TODO 1-05 - TO COMPLETE ****************************************
* - if resultSet has result, get data and create a Grocery instance
* - making sure that the item name from grocery exists in
* item table (use searchItem method)
* - pay attention about parsing the date string to LocalDate
*/

}

return grocery;
}

public List getAllItems() throws Exception {
String queryString = "SELECT * FROM item";

/* TODO 1-06 - TO COMPLETE ****************************************
* - resultSet to execute the statement query
*/

List items = new ArrayList();

/* TODO 1-07 - TO COMPLETE ****************************************
* - iterate through the resultSet result, create intance of Item
* and add to list items
*/

return items;
}

public List getAllGroceries() throws Exception {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(DATE_FORMAT);
String queryString = "SELECT * FROM grocery";

/* TODO 1-08 - TO COMPLETE ****************************************
* - resultSet to execute the statement query
*/

List groceries = new ArrayList();

/* TODO 1-09 - TO COMPLETE ****************************************
* - iterate through the resultSet result, create intance of Item
* and add to list items
* - making sure that the item name from each grocery exists in
* item table (use searchItem method)
* - pay attention about parsing the date string to LocalDate
*/


return groceries;
}


public int addGrocery(String name, int quantity, SECTION section) throws Exception {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(DATE_FORMAT);
LocalDate date = LocalDate.now();
String dateStr = date.format(dtf);

// NOTE: should we check if itemName (argument name) exists in item table?
// --> adding a groceries with a non-existing item name should through an exception

String command = "INSERT INTO grocery VALUES(?, ?, ?, ?, ?)";

/* TODO 1-10 - TO COMPLETE ****************************************
* - preparedStatement to add arguments to the queryString
* - resultSet to executeUpdate the preparedStatement query
*/

// retrieving & returning last inserted record id
ResultSet rs = statement.executeQuery("SELECT LAST_INSERT_ID()");
rs.next();
int newId = rs.getInt(1);

return newId;
}

public Grocery useGrocery(int id) throws Exception {

/* TODO 1-11 - TO COMPLETE ****************************************
* - search grocery by id
* - check if has quantity is greater one; if not throw exception
* with adequate error message
*/


String queryString =
"UPDATE grocery " +
"SET quantity = quantity - 1 " +
"WHERE quantity > 1 " +
"AND id = " + id + ";";


/* TODO 1-12 - TO COMPLETE ****************************************
* - statement execute update on queryString
* - should the update affect a row search grocery by id and
* return it; else throw exception with adequate error message
*
* NOTE: method should return instance of grocery
*/

}

public int removeGrocery(int id) throws Exception {
String queryString = "DELETE FROM grocery WHERE id = " + id + ";";

/* TODO 1-13 - TO COMPLETE ****************************************
* - search grocery by id
* - if grocery exists, statement execute update on queryString
* return the value value of that statement execute update
* - if grocery does not exist, throw exception with adequate
* error message
*
* NOTE: method should return int: the return value of a
* stetement.executeUpdate(...) on a DELETE query
*/

}

// STATIC HELPERS -------------------------------------------------------

public static long calcDaysAgo(LocalDate date) {
return Math.abs(Duration.between(LocalDate.now().atStartOfDay(), date.atStartOfDay()).toDays());
}

public static String calcDaysAgoStr(LocalDate date) {
String formattedDaysAgo;
long diff = calcDaysAgo(date);

if (diff == 0)
formattedDaysAgo = "today";
else if (diff == 1)
formattedDaysAgo = "yesterday";
else formattedDaysAgo = diff + " days ago";

return formattedDaysAgo;
}

// To perform some quick tests
public static void main(String[] args) throws Exception {
FridgeDSC myFridgeDSC = new FridgeDSC();

myFridgeDSC.connect();

System.out.println("nSYSTEM:n");

System.out.println("nnshowing all of each:");
System.out.println(myFridgeDSC.getAllItems());
System.out.println(myFridgeDSC.getAllGroceries());

int addedId = myFridgeDSC.addGrocery("Milk", 40, SECTION.COOLING);
System.out.println("added: " + addedId);
System.out.println("deleting " + (addedId - 1) + ": " + (myFridgeDSC.removeGrocery(addedId - 1) > 0 ? "DONE" : "FAILED"));
System.out.println("using " + (addedId) + ": " + myFridgeDSC.useGrocery(addedId));
System.out.println(myFridgeDSC.searchGrocery(addedId));

myFridgeDSC.disconnect();
}
}

FridgeFX.java

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;

import java.util.*;
import java.io.*;
import javafx.collections.*;
import javafx.collections.transformation.*;
import javafx.scene.control.cell.*;
import javafx.beans.property.*;

public class FridgeFX extends Application {

// used as ChoiceBox value for filter
public enum FILTER_COLUMNS {
ITEM,
SECTION,
BOUGHT_DAYS_AGO
};

// the data source controller
private FridgeDSC fridgeDSC;


public void init() throws Exception {
// creating an instance of the data source controller to be used
// in this application
fridgeDSC = new FridgeDSC();

/* TODO 2-01 - TO COMPLETE ****************************************
* call the data source controller database connect method
* NOTE: that database connect method throws exception
*/
}

public void start(Stage stage) throws Exception {

/* TODO 2-02 - TO COMPLETE ****************************************
* - this method is the start method for your application
* - set application title
* - show the stage
*/


/* TODO 2-03 - TO COMPLETE ****************************************
* currentThread uncaught exception handler
*/
}

public void build(Stage stage) throws Exception {


/* If you are getting the FX up and running before the DSC then you need to comment out the DSC calls above
* and manually create an array of groceries to add to the tableview array.
*
* LocalDate date = LocalDate.now();
* Item myItem = new Item("Mars Bar", true);
* Grocery mygr1 = new Grocery(1,myItem,date,10,FridgeDSC.SECTION.COOLING);
* Grocery mygr2 = new Grocery(2,myItem,date,15,FridgeDSC.SECTION.COOLING);
* ArrayList< Grocery> mygrs = new ArrayList< >();
* mygrs.add(mygr1);
* mygrs.add(mygr2);
*
* then below *after* the TableView has been set up
*
* tableView.setItems(tableData);
*
* add the manually created array
* tableData.addAll(mygrs);
*/


// Create table data (an observable list of objects)
ObservableList< Grocery> tableData = FXCollections.observableArrayList();

// Define table columns
TableColumn< Grocery, String> idColumn = new TableColumn< Grocery, String>("Id");
TableColumn< Grocery, String> itemNameColumn = new TableColumn< Grocery, String>("Item");
TableColumn< Grocery, Integer> quantityColumn = new TableColumn< Grocery, Integer>("QTY");
TableColumn< Grocery, String> sectionColumn = new TableColumn< Grocery, String>("Section");
TableColumn< Grocery, String> daysAgoColumn = new TableColumn< Grocery, String>("Bought");

/* TODO 2-04 - TO COMPLETE ****************************************
* for each column defined, call their setCellValueFactory method
* using an instance of PropertyValueFactory
*/


// Create the table view and add table columns to it
TableView< Grocery> tableView = new TableView< Grocery>();


/* TODO 2-05 - TO COMPLETE ****************************************
* add table columns to the table view create above
*/


// Attach table data to the table view
tableView.setItems(tableData);


/* TODO 2-06 - TO COMPLETE ****************************************
* set minimum and maximum width to the table view and each columns
*/


/* TODO 2-07 - TO COMPLETE ****************************************
* call data source controller get all groceries method to add
* all groceries to table data observable list
*/


// =====================================================
// ADD the remaining UI elements
// NOTE: the order of the following TODO items can be
// changed to satisfy your UI implementation goals
// =====================================================

/* TODO 2-08 - TO COMPLETE ****************************************
* filter container - part 1
* add all filter related UI elements you identified
*/

/* TODO 2-09 - TO COMPLETE ****************************************
* filter container - part 2:
* - addListener to the "Filter By" ChoiceBox to clear the filter
* text field vlaue and to enable the "Show Expire Only" CheckBox
* if "BOUGHT_DAYS_AGO" is selected
*/

/* TODO 2-10 - TO COMPLETE ****************************************
* filter container - part 2:
* - addListener to the "Filter By" ChoiceBox to clear and set focus
* to the filter text field and to enable the "Show Expire Only"
* CheckBox if "BOUGHT_DAYS_AGO" is selected
*
* - setOnAction on the "Show Expire Only" Checkbox to clear and
* set focus to the filter text field
*/

/* TODO 2-11 - TO COMPLETE ****************************************
* filter container - part 3:
* - create a filtered list
* - create a sorted list from the filtered list
* - bind comparators of sorted list with that of table view
* - set items of table view to be sorted list
* - set a change listener to text field to set the filter predicate
* of filtered list
*/


/* TODO 2-12 - TO COMPLETE ****************************************
* ACTION buttons: ADD, UPDATE ONE, DELETE, EXIT
* - ADD button sets the add UI elements to visible;
* NOTE: the add input controls and container may have to be
* defined before these action controls & container(s)
* - UPDATE ONE and DELETE buttons action need to check if a
* table view row has been selected first before doing their
* action; hint: should you also use an Alert confirmation?
* - EXIT button. Use stage.close() after making sure that data is synced
*/

/* TODO 2-13 - TO COMPLETE ****************************************
* add input controls and container(s)
* - Item will list item data from the data source controller list
* all items method
* - Section will list all sections defined in the data source
* controller SECTION enum
* - Quantity: a texf field, self descriptive
* - CANCEL button: clears all input controls
* - SAVE button: sends the new grocery information to the data source
* controller add grocery method; be mindful of exceptions when any
* or all of the input controls are empty upon SAVE button click
*/

// =====================================================================
// SET UP the Stage
// =====================================================================
//

/* TODO 2-14 - TO COMPLETE ****************************************
* - Create primary VBox container, add it to the scene add external style sheet to the scene
* - VBox root = new VBox(...);
* - add all your containers, controls to the root VBox
* - add root container to the scene
*/
VBox root = new VBox(); // modify
Scene scene = new Scene(root);
/*
* - add external style sheet to the scene - scene.getStylesheets().add(..);
* - add scene to stage
*/

stage.setScene(scene);
}

public void stop() throws Exception {

/* TODO 2-15 - TO COMPLETE ****************************************
* call the data source controller database disconnect method
* NOTE: that database disconnect method throws exception
*/
}
}

Grocery.java

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Grocery {

public static final int MINIMUM_QUANTITY = 1;

// name is the unique id
private int id;
private Item item;
private LocalDate date; // when was it bought or added to fridge; read-only will be set in constructor
private int quantity; // read-only, set in constructor, defaults to 1
private FridgeDSC.SECTION section;

// constructor
public Grocery(int id, Item item, LocalDate date, int quantity, FridgeDSC.SECTION section) throws Exception {
if (id < 1)
throw new Exception("[ERROR] id value cannot be less than 1");
if (item == null)
throw new Exception("[ERROR] Item cannot be null value");
if (section == null)
throw new Exception("[ERROR] Section cannot be null value");
if (quantity < MINIMUM_QUANTITY)
throw new Exception("[ERROR] Quantity value cannot be less than 1");

this.id = id;
this.item = item;
this.date = date != null ? date : LocalDate.now();
this.quantity = quantity;
this.section = section;
}

// constructor
public Grocery(int id, Item item, LocalDate date, FridgeDSC.SECTION section) throws Exception {
this(id, item, date, MINIMUM_QUANTITY, section);
}

// constructor
public Grocery(int id, Item item, int quantity, FridgeDSC.SECTION section) throws Exception {
this(id, item, LocalDate.now(), quantity, section);
}

// constructor
public Grocery(int id, Item item, FridgeDSC.SECTION section) throws Exception {
this(id, item, LocalDate.now(), MINIMUM_QUANTITY, section);
}

public int getId() {
return this.id;
}

public Item getItem() {
return this.item;
}

public String getItemName() {
return this.item.getName();
}

public LocalDate getDate() {
return this.date;
}

public String getDateStr() {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(FridgeDSC.DATE_FORMAT);

return this.date.format(dtf);
}

public String getDaysAgo() {
return FridgeDSC.calcDaysAgoStr(date);
}

public int getQuantity() {
return this.quantity;
}

public void updateQuantity() throws Exception {
if (this.quantity > MINIMUM_QUANTITY)
this.quantity--;
else
throw new Exception("[ERROR] Quantity value cannot be less than 1");
}

public FridgeDSC.SECTION getSection() {
return this.section;
}


public String toString() {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(FridgeDSC.DATE_FORMAT);
String daysAgo = FridgeDSC.calcDaysAgoStr(date);

return "[ id: " + this.id
+ ", item: " + this.item.getName() + (item.canExpire() ? " (EXP)":"")
+ ", date: " + this.date.format(dtf) + " (" + daysAgo + ")"
+ ", quantity: " + this.quantity
+ ", section: " + this.section
+ " ]";
}

// To perform some quick tests
public static void main(String [] args) throws Exception {
Item i1 = new Item("Milk", false);
System.out.println(i1);

Item i2 = new Item("Fish", true);
System.out.println(i2);

Grocery g1 = new Grocery(1, i1, FridgeDSC.SECTION.COOLING);
System.out.println(g1);

Grocery g2 = new Grocery(1, i2, 10, FridgeDSC.SECTION.COOLING);
System.out.println(g2);
}
}

Item.java

public class Item {

// name is the unique id
private String name;
private boolean expires; // defaults to false

// constructor
public Item(String name, boolean expires) {
this.name = name;
this.expires = expires;
}

// constructor
public Item(String name) {
this(name, false);
}

public String getName() {
return this.name;
}

public boolean canExpire()
{
return this.expires;
}

public String toString() {
return "[ name: " + this.name
+ ", expires: " + this.expires
+ " ]";
}

// To perform some quick tests
public static void main(String [] args)
{
Item i1 = new Item("Milk", false);
System.out.println(i1);

Item i2 = new Item("Fish", true);
System.out.println(i2);
}
}
Academic Honesty!
It is not our intention to break the school's academic policy. Posted solutions are meant to be used as a reference and should not be submitted as is. We are not held liable for any misuse of the solutions. Please see the frequently asked questions page for further questions and inquiries.
Kindly complete the form. Please provide a valid email address and we will get back to you within 24 hours. Payment is through PayPal, Buy me a Coffee or Cryptocurrency. We are a nonprofit organization however we need funds to keep this organization operating and to be able to complete our research and development projects.