diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 00000000000..1e1862b0e82
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,25 @@
+name: MarkBind Action
+
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ build_and_deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Install Graphviz
+ run: sudo apt-get install graphviz
+ - name: Install Java
+ uses: actions/setup-java@v3
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+ - name: Build & Deploy MarkBind site
+ uses: MarkBind/markbind-action@v2
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ rootDirectory: './docs'
+ baseUrl: '/tp' # replace with your repo name
+ version: '^5.1.0'
diff --git a/.gitignore b/.gitignore
index 284c4ca7cd9..eab4c7db6a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,4 @@ src/test/data/sandbox/
# MacOS custom attributes files created by Finder
.DS_Store
docs/_site/
+docs/_markbind/logs/
diff --git a/LICENSE b/LICENSE
index 39b3478982c..276ff96c6e0 100644
--- a/LICENSE
+++ b/LICENSE
@@ -2,11 +2,11 @@ MIT License
Copyright (c) 2016 Software Engineering Education - FOSS Resources
-Permission is hereby granted, free of charge, to any person obtaining a copy
+Permission is hereby granted, free of charge, to any ingredient obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
+copies of the Software, and to permit ingredients to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
diff --git a/README.md b/README.md
index 13f5c77403f..5a9910355c3 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,18 @@
-[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
+![CI Status](https://github.com/AY2324S1-CS2103T-F10-3/tp/actions/workflows/gradle.yml/badge.svg)
+
+[![codecov](https://codecov.io/gh/AY2324S1-CS2103T-F10-3/tp/graph/badge.svg?token=VOTP39W8U3)](https://codecov.io/gh/AY2324S1-CS2103T-F10-3/tp)
![Ui](docs/images/Ui.png)
-* This is **a sample project for Software Engineering (SE) students**.
- Example usages:
- * as a starting point of a course project (as opposed to writing everything from scratch)
- * as a case study
-* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details.
- * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big.
- * It comes with a **reasonable level of user and developer documentation**.
-* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...).
-* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**.
-* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info.
+
+* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org).
+* This is **an ingredient and recipe application for home bakers and cooks**.
+* Example usages:
+ * as a medium for home bakers to store their current ingredients.
+ * as a way for bakers and cooks to find the corresponding recipes.
+ * as a central for check whether they have the necessary ingredients for a specific recipe.
+* The project simulates an ingredient stock as well as recipe viewer.
+ * It is **written in OOP fashion**. It provides a **reasonably well-written** code base.
+ * It comes with a **reasonable level of user and developer documentation.
+* It is named `[Ba]king [Br]ead` as a pun to the show breaking bad and also to tie in with the idea of food.
+* For a detailed documentation, visit [[Ba]king [Br]ead](https://ay2324s1-cs2103t-f10-3.github.io/tp/)
diff --git a/build.gradle b/build.gradle
index a2951cc709e..493da2dbe6e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -66,7 +66,11 @@ dependencies {
}
shadowJar {
- archiveFileName = 'addressbook.jar'
+ archiveFileName = 'bakingbread.jar'
+}
+
+run {
+ enableAssertions = true
}
defaultTasks 'clean', 'test'
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 00000000000..1748e487fbd
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,23 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+_markbind/logs/
+
+# Dependency directories
+node_modules/
+
+# Production build files (change if you output the build to a different directory)
+_site/
+
+# Env
+.env
+.env.local
+
+# IDE configs
+.vscode/
+.idea/*
+*.iml
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 1c9514e966a..88202651df7 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -1,59 +1,62 @@
---
-layout: page
-title: About Us
+ layout: default.md
+ title: "About Us"
---
+# About Us
+
We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg).
You can reach us at the email `seer[at]comp.nus.edu.sg`
## Project team
-### John Doe
+### Tan Tze Young
-
+
-[[homepage](http://www.comp.nus.edu.sg/~damithch)]
-[[github](https://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/ItsTYtan)]
+[[portfolio](team/itstytan.md)]
-* Role: Project Advisor
+* Role: Team Lead
+* Responsibilities: UI
-### Jane Doe
+### Toh Li Yuan
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/TohLiYuan)] [[portfolio](team/tohliyuan.md)]
-* Role: Team Lead
-* Responsibilities: UI
+* Role: Developer
+* Responsibilities: Storage
-### Johnny Doe
+### Thaddeus Ong
-
+
-[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)]
+[[github](http://github.com/thaddeusong)]
+[[portfolio](team/thaddeusong.md)]
* Role: Developer
-* Responsibilities: Data
+* Responsibilities: Logic and Model
-### Jean Doe
+### Seah Zi Xiang
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/prawnzyy)]
+[[portfolio](team/prawnzyy.md)]
* Role: Developer
-* Responsibilities: Dev Ops + Threading
+* Responsibilities: Admin and UI
-### James Doe
-
+### Dhruvi Ketan Rathod
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+
+
+[[github](http://github.com/dhruvir29)]
+[[portfolio](team/dhruvir29.md)]
* Role: Developer
-* Responsibilities: UI
+* Responsibilities: Logic
diff --git a/docs/Configuration.md b/docs/Configuration.md
index 13cf0faea16..32f6255f3b9 100644
--- a/docs/Configuration.md
+++ b/docs/Configuration.md
@@ -1,6 +1,8 @@
---
-layout: page
-title: Configuration guide
+ layout: default.md
+ title: "Configuration guide"
---
+# Configuration guide
+
Certain properties of the application can be controlled (e.g user preferences file location, logging level) through the configuration file (default: `config.json`).
diff --git a/docs/DevOps.md b/docs/DevOps.md
index d2fd91a6001..8228c845e86 100644
--- a/docs/DevOps.md
+++ b/docs/DevOps.md
@@ -1,12 +1,15 @@
---
-layout: page
-title: DevOps guide
+ layout: default.md
+ title: "DevOps guide"
+ pageNav: 3
---
-* Table of Contents
-{:toc}
+# DevOps guide
---------------------------------------------------------------------------------------------------------------------
+
+
+
+
## Build automation
diff --git a/docs/DeveloperGuide (Pristine).md b/docs/DeveloperGuide (Pristine).md
new file mode 100644
index 00000000000..d66279f5d0a
--- /dev/null
+++ b/docs/DeveloperGuide (Pristine).md
@@ -0,0 +1,391 @@
+---
+ layout: default.md
+ title: "Developer Guide"
+ pageNav: 3
+---
+
+# AB-3 Developer Guide
+
+
+
+
+--------------------------------------------------------------------------------------------------------------------
+
+## **Acknowledgements**
+
+_{ list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well }_
+
+--------------------------------------------------------------------------------------------------------------------
+
+## **Setting up, getting started**
+
+Refer to the guide [_Setting up and getting started_](SettingUp.md).
+
+--------------------------------------------------------------------------------------------------------------------
+
+## **Design**
+
+### Architecture
+
+
+
+The ***Architecture Diagram*** given above explains the high-level design of the App.
+
+Given below is a quick overview of main components and how they interact with each other.
+
+**Main components of the architecture**
+
+**`Main`** (consisting of classes [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java)) is in charge of the app launch and shut down.
+* At app launch, it initializes the other components in the correct sequence, and connects them up with each other.
+* At shut down, it shuts down the other components and invokes cleanup methods where necessary.
+
+The bulk of the app's work is done by the following four components:
+
+* [**`UI`**](#ui-component): The UI of the App.
+* [**`Logic`**](#logic-component): The command executor.
+* [**`Model`**](#model-component): Holds the data of the App in memory.
+* [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk.
+
+[**`Commons`**](#common-classes) represents a collection of classes used by multiple other components.
+
+**How the architecture components interact with each other**
+
+The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`.
+
+
+
+Each of the four main components (also shown in the diagram above),
+
+* defines its *API* in an `interface` with the same name as the Component.
+* implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point.
+
+For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
+
+
+
+The sections below give more details of each component.
+
+### UI component
+
+The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java)
+
+
+
+The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI.
+
+The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml)
+
+The `UI` component,
+
+* executes user commands using the `Logic` component.
+* listens for changes to `Model` data so that the UI can be updated with the modified data.
+* keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands.
+* depends on some classes in the `Model` component, as it displays `Addressbook` object residing in the `Model`.
+
+### Logic component
+
+**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java)
+
+Here's a (partial) class diagram of the `Logic` component:
+
+
+
+The sequence diagram below illustrates the interactions within the `Logic` component, taking `execute("delete 1")` API call as an example.
+
+
+
+
+
+**Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+
+
+How the `Logic` component works:
+
+1. When `Logic` is called upon to execute a command, it is passed to an `AddressBookParser` object which in turn creates a parser that matches the command (e.g., `DeleteCommandParser`) and uses it to parse the command.
+1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `DeleteCommand`) which is executed by the `LogicManager`.
+1. The command can communicate with the `Model` when it is executed (e.g. to delete a addressbook).
+1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`.
+
+Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command:
+
+
+
+How the parsing works:
+* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object.
+* All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing.
+
+### Model component
+**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java)
+
+
+
+
+The `Model` component,
+
+* stores the address book data i.e., all `Addressbook` objects (which are contained in a `UniqueAddressbookList` object).
+* stores the currently 'selected' `Addressbook` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
+* stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects.
+* does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components)
+
+
+
+**Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Addressbook` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Addressbook` needing their own `Tag` objects.
+
+
+
+
+
+
+### Storage component
+
+**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java)
+
+
+
+The `Storage` component,
+* can save both address book data and user preference data in JSON format, and read them back into corresponding objects.
+* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed).
+* depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`)
+
+### Common classes
+
+Classes used by multiple components are in the `seedu.addressbook.commons` package.
+
+--------------------------------------------------------------------------------------------------------------------
+
+## **Implementation**
+
+This section describes some noteworthy details on how certain features are implemented.
+
+### \[Proposed\] Undo/redo feature
+
+#### Proposed Implementation
+
+The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations:
+
+* `VersionedAddressBook#commit()` — Saves the current address book state in its history.
+* `VersionedAddressBook#undo()` — Restores the previous address book state from its history.
+* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history.
+
+These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively.
+
+Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
+
+Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state.
+
+
+
+Step 2. The user executes `delete 5` command to delete the 5th addressbook in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state.
+
+
+
+Step 3. The user executes `add n/David …` to add a new addressbook. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`.
+
+
+
+
+
+**Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`.
+
+
+
+Step 4. The user now decides that adding the addressbook was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state.
+
+
+
+
+
+
+**Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather
+than attempting to perform the undo.
+
+
+
+The following sequence diagram shows how the undo operation works:
+
+
+
+
+
+**Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+
+
+
+The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state.
+
+
+
+**Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
+
+
+
+Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged.
+
+
+
+Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …` command. This is the behavior that most modern desktop applications follow.
+
+
+
+The following activity diagram summarizes what happens when a user executes a new command:
+
+
+
+#### Design considerations:
+
+**Aspect: How undo & redo executes:**
+
+* **Alternative 1 (current choice):** Saves the entire address book.
+ * Pros: Easy to implement.
+ * Cons: May have performance issues in terms of memory usage.
+
+* **Alternative 2:** Individual command knows how to undo/redo by
+ itself.
+ * Pros: Will use less memory (e.g. for `delete`, just save the addressbook being deleted).
+ * Cons: We must ensure that the implementation of each individual command are correct.
+
+_{more aspects and alternatives to be added}_
+
+### \[Proposed\] Data archiving
+
+_{Explain here how the data archiving feature will be implemented}_
+
+
+--------------------------------------------------------------------------------------------------------------------
+
+## **Documentation, logging, testing, configuration, dev-ops**
+
+* [Documentation guide](Documentation.md)
+* [Testing guide](Testing.md)
+* [Logging guide](Logging.md)
+* [Configuration guide](Configuration.md)
+* [DevOps guide](DevOps.md)
+
+--------------------------------------------------------------------------------------------------------------------
+
+## **Appendix: Requirements**
+
+### Product scope
+
+**Target user profile**:
+
+* has a need to manage a significant number of contacts
+* prefer desktop apps over other types
+* can type fast
+* prefers typing to mouse interactions
+* is reasonably comfortable using CLI apps
+
+**Value proposition**: manage contacts faster than a typical mouse/GUI driven app
+
+
+### User stories
+
+Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
+
+| Priority | As a … | I want to … | So that I can… |
+|----------|--------------------------------------------|------------------------------|------------------------------------------------------------------------|
+| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App |
+| `* * *` | user | add a new addressbook | |
+| `* * *` | user | delete a addressbook | remove entries that I no longer need |
+| `* * *` | user | find a addressbook by name | locate details of addressbooks without having to go through the entire list |
+| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident |
+| `*` | user with many addressbooks in the address book | sort addressbooks by name | locate a addressbook easily |
+
+*{More to be added}*
+
+### Use cases
+
+(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise)
+
+**Use case: Delete a addressbook**
+
+**MSS**
+
+1. User requests to list addressbooks
+2. AddressBook shows a list of addressbooks
+3. User requests to delete a specific addressbook in the list
+4. AddressBook deletes the addressbook
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. The list is empty.
+
+ Use case ends.
+
+* 3a. The given index is invalid.
+
+ * 3a1. AddressBook shows an error message.
+
+ Use case resumes at step 2.
+
+*{More to be added}*
+
+### Non-Functional Requirements
+
+1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed.
+2. Should be able to hold up to 1000 addressbooks without a noticeable sluggishness in performance for typical usage.
+3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
+
+*{More to be added}*
+
+### Glossary
+
+* **Mainstream OS**: Windows, Linux, Unix, OS-X
+* **Private contact detail**: A contact detail that is not meant to be shared with others
+
+--------------------------------------------------------------------------------------------------------------------
+
+## **Appendix: Instructions for manual testing**
+
+Given below are instructions to test the app manually.
+
+
+
+**Note:** These instructions only provide a starting point for testers to work on;
+testers are expected to do more *exploratory* testing.
+
+
+
+### Launch and shutdown
+
+1. Initial launch
+
+ 1. Download the jar file and copy into an empty folder
+
+ 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
+
+1. Saving window preferences
+
+ 1. Resize the window to an optimum size. Move the window to a different location. Close the window.
+
+ 1. Re-launch the app by double-clicking the jar file.
+ Expected: The most recent window size and location is retained.
+
+1. _{ more test cases … }_
+
+### Deleting a addressbook
+
+1. Deleting a addressbook while all addressbooks are being shown
+
+ 1. Prerequisites: List all addressbooks using the `list` command. Multiple addressbooks in the list.
+
+ 1. Test case: `delete 1`
+ Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.
+
+ 1. Test case: `delete 0`
+ Expected: No addressbook is deleted. Error details shown in the status message. Status bar remains the same.
+
+ 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
+ Expected: Similar to previous.
+
+1. _{ more test cases … }_
+
+### Saving data
+
+1. Dealing with missing/corrupted data files
+
+ 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_
+
+1. _{ more test cases … }_
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 8a861859bfd..4cb466e1a8c 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,15 +1,19 @@
---
-layout: page
-title: Developer Guide
+layout: default.md
+title: "Developer Guide"
+pageNav: 3
---
-* Table of Contents
-{:toc}
+
+# [Ba]king [Br]ead Developer Guide
+
+
+
--------------------------------------------------------------------------------------------------------------------
## **Acknowledgements**
-
-* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+This project is based on the [AddressBook-Level3](https://se-education.org/addressbook-level3/) project created by the
+[SE-EDU initiative](https://se-education.org/).
--------------------------------------------------------------------------------------------------------------------
@@ -18,17 +22,11 @@ title: Developer Guide
Refer to the guide [_Setting up and getting started_](SettingUp.md).
--------------------------------------------------------------------------------------------------------------------
-
## **Design**
-
-
-:bulb: **Tip:** The `.puml` files used to create diagrams in this document `docs/diagrams` folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
-
-
### Architecture
-
+
The ***Architecture Diagram*** given above explains the high-level design of the App.
@@ -36,7 +34,10 @@ Given below is a quick overview of main components and how they interact with ea
**Main components of the architecture**
-**`Main`** (consisting of classes [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java)) is in charge of the app launch and shut down.
+**`Main`** (consisting of classes
+[`Main`](https://github.com/AY2324S1-CS2103T-F10-3/tp/blob/master/src/main/java/seedu/address/Main.java) and
+[`MainApp`](https://github.com/AY2324S1-CS2103T-F10-3/tp/blob/master/src/main/java/seedu/address/MainApp.java)) is in
+charge of the app launch and shut down.
* At app launch, it initializes the other components in the correct sequence, and connects them up with each other.
* At shut down, it shuts down the other components and invokes cleanup methods where necessary.
@@ -53,7 +54,7 @@ The bulk of the app's work is done by the following four components:
The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`.
-
+
Each of the four main components (also shown in the diagram above),
@@ -62,182 +63,379 @@ Each of the four main components (also shown in the diagram above),
For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
-
+
The sections below give more details of each component.
### UI component
-The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java)
+**API:** [`Ui.java`](https://github.com/AY2324S1-CS2103T-F10-3/tp/blob/master/src/main/java/seedu/address/ui/Ui.java)
-![Structure of the UI Component](images/UiClassDiagram.png)
+
-The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI.
+The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `IngredientListPanel`,
+`RecipeListPanel` `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class
+which captures the commonalities between classes that represent parts of the visible GUI.
-The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml)
+The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that
+are in the `src/main/resources/view` folder. For example, the layout of the
+[`MainWindow`](https://github.com/AY2324S1-CS2103T-F10-3/tp/blob/master/src/main/java/seedu/address/ui/MainWindow.java)
+is specified in
+[`MainWindow.fxml`](https://github.com/AY2324S1-CS2103T-F10-3/tp/blob/master/src/main/resources/view/MainWindow.fxml)
The `UI` component,
* executes user commands using the `Logic` component.
* listens for changes to `Model` data so that the UI can be updated with the modified data.
* keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands.
-* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`.
+* depends on some classes in the `Model` component, as it displays `Inventory` object residing in the `Model`.
-### Logic component
+
-**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java)
+### Model Component
-Here's a (partial) class diagram of the `Logic` component:
+**API**: [`Model.java`](https://github.com/AY2324S1-CS2103T-F10-3/tp/blob/master/src/main/java/seedu/address/model/Model.java)
-
+
-The sequence diagram below illustrates the interactions within the `Logic` component, taking `execute("delete 1")` API call as an example.
+The `Model` stores `Ingredient` and `Recipe`, the main components of this product. It provides the API to interact with
+the available list of `Ingredient` and `Recipe`.
-![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png)
+The `Model` component,
-
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
-
+- stores a `UserPref` object that represents the user's preferences.
+- stores the `Inventory` data.
+- stores the `Recipe Book` data.
+- exposes an unmodifiable `ObservableList`, `ObservableList` that can be 'observed' by the UI.
+- does not depend on the other three components
-How the `Logic` component works:
+
-1. When `Logic` is called upon to execute a command, it is passed to an `AddressBookParser` object which in turn creates a parser that matches the command (e.g., `DeleteCommandParser`) and uses it to parse the command.
-1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `DeleteCommand`) which is executed by the `LogicManager`.
-1. The command can communicate with the `Model` when it is executed (e.g. to delete a person).
-1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`.
+### Storage Component
-Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command:
+**API**: [`Storage.java`](https://github.com/AY2324S1-CS2103T-F10-3/tp/blob/master/src/main/java/seedu/address/storage/Storage.java)
-
+
-How the parsing works:
-* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object.
-* All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing.
+The `Storage` manages the saving and reading of currently available `Ingredient` and `Recipe` to local storage.
+It allows users to use the product as-is throughout different sessions.
-### Model component
-**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java)
+The `Storage` component,
-
+- consists of an Inventory Storage and Recipe Book Storage sub-component.
+- saves the `UserPref` object in json format and read it back.
+- saves the `Inventory` object in json format and read it back.
+- saves the `RecipeBook` object in json format and read it back.
+
-The `Model` component,
+## Implementation
+This section describes some noteworthy details on how certain features are implemented.
-* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object).
-* stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
-* stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects.
-* does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components)
+### Search ingredient feature
+#### Implementation
+The search ingredient mechanism is implemented as a `Command`, extending from the `Command` abstract class.
-
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
+Given below is an example usage scenario and how the search ingredient mechanism behaves at each step. The applicacation
+is assumed to be initialised with at least one ingredient loaded in the `ModelManager`.
-
+Step 1. The user keys in `stock Flour` into the UI command box. `LogicManager` takes this string command and executes it.
-
+Step 2. `InventoryAppParser` is then called to parse the `stock Flour` command.
+Step 3. `StockCommandParser` is then called to handle the parsing. The `parse(String args)` function is called with
+the argument `"Flour"`.
-### Storage component
+Step 4. `NameContainsKeywordsPredicate` predicate object is created which returns true for any ingredients tested on
+the predicate with a name containing the phrase `"Flour"`. This step is case-insensitive.
-**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java)
+Step 5. The `StockCommand` then filters the inventory in `ModelManager` according to the predicate.
-
+Step 6. The `MainWindow` in the `ui` detects that there are some items in the filtered inventory, and proceeds to
+display ingredients satisfying the predicate
-The `Storage` component,
-* can save both address book data and user preference data in JSON format, and read them back into corresponding objects.
-* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed).
-* depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`)
+`StockCommand` calls `Model#updateFilteredIngredientList(Predicate predicate)`, filtering the
+ingredient list in `ModelManager` according to the predicate set.
-### Common classes
+#### Alternatives considered:
+An alternative implementation of the stock command would be to find the ingredient that matches the query perfectly,
+resulting in either 0 or 1 ingredients are filtering. Each ingredient should have a unique name, hence an ingredient with
+any name can either only be stored in the inventory or not, and therefore the search result will be more specific, and
+potentially more convenient to use.
-Classes used by multiple components are in the `seedu.addressbook.commons` package.
+However, we expect users to have multiple ingredients with a common word, as natural language tends to group items of a
+similar nature in the same group of phrases (eg. eggs and duck eggs). Hence, we have decided that searching for items
+whose name contains the phrase of the query is more suitable for home bakers, considering the number of repeated phrases
+and expressions commonly used in baking.
---------------------------------------------------------------------------------------------------------------------
+### Add recipe feature
+#### Implementation
+The add recipe mechanism is implemented as a `Command`, extending from the `Command` abstract class.
-## **Implementation**
+Given below is an example usage scenario and how the add recipe mechanism behaves at each st ep.
-This section describes some noteworthy details on how certain features are implemented.
+Step 1. The user keys in the following command.
+```
+addrecipe
+```
+
+Step 2. `RecipeAddInputHandler` is then used to control how the input is handled, preventing other commands from being used.
+
+Step 3. The user keys in the name of the recipe.
+```
+Cookie
+```
+
+Step 4. `RecipeAddInputHandler` passes the string to `RecipeAddCommandParser`, which parses it into a `Name` instance.
+
+Step 5. The user enters as many ingredients as they like one by one, in the following format:
+`flour 100g`
+
+Step 6. Each string is parsed by the `RecipeAddCommandParser`, and the parsed `Ingredient` instance is added to a list.
+If a parsing error occurs, no ingredient is added, but the recipe creation still continues.
+
+Step 7. The user enters `steps start` to cease the adding of ingredients and begin writing steps.
+
+Step 8. Each string is parsed by the `RecipeAddCommandParser`, and the parsed `RecipeStep` instance is added to a list.
+If a parsing error occurs, no step is added, but the recipe creation still continues.
+
+Step 9. A new `Recipe` instance is created using the `Name`, `List` and `List`.
+The `List` is sorted by step number.
+
+Step 10. A `RecipeAddCommand` instance is created by the `RecipeAddCommandParser`, which is passed to the `ModelManager` and executed.
+
+Step 9. The `RecipeAddCommand` adds the new `Recipe` instance to the recipe list in `ModelManager`.
+
+The following sequence diagram shows how the add recipe operation works:
+![img.png](img.png)
+
+#### Alternatives considered:
+
+An alternative implementation of the recipe add command would be to only specify the name, and add the ingredient and step lists
+later using recipe modifying commands. This would reduce the size of the command, leading to lower chances of user input error.
+
+However, modifying the recipe through commands may be more slow than typing everything at once, since more command words need to be
+used. Furthermore, when inputting a recipe, users are likely to copy and paste the ingredient list and steps from another source.
+As such, errors in input should be unlikely. It is also easy to see where an error may be in the input, since the format is
+very readable, with little tokens and command words.
+
+### View recipe feature
+#### Implementation
+The view recipe mechanism is implemented as a `Command`, extending from the `Command` abstract class.
+
+Given below is an example usage scenario and how the view recipe mechanism behaves at each step. The application is
+assumed to be initialised with at least one recipe loaded in the `ModelManager`.
+
+Step 1. The user keys in `view 1` into the UI command box. `LogicManager` takes this string command and executes it.
+
+Step 2. `InventoryAppParser` is then called to parse the `view 1` command.
+
+Step 3. By Polymorphism, `RecipeViewCommandParser` is called on to handle the parsing. The `parse(String args)` function
+is called with the argument of `"1"`
+
+Step 4. A `RecipeUuidMatchesPredicate` object is created which returns true for any recipe tested on the
+predicate with the same unique identifier (UUID) of `1`.
+
+Step 5. The `RecipeViewCommand` then filters the recipe list in `ModelManager` according to the predicate. The recipe
+list should only have at most 1 item after filtration.
+
+Step 6. If there is a recipe with the same UUID inputted by the user in the recipe book, the `MainWindow` in `ui` will
+detect that there is one item in the recipe list, and proceed to display the full recipe.
+
+`RecipeViewCommand` calls `Model#updateFilteredRecipeList(Predicate predicate)`, filtering the
+recipe list in `ModelManager` according to the predicate set.
+
+The following sequence diagram shows how the view recipe operation works:
+
+
+
+**Note**: If the argument is an invalid UUID (less than or equals to 0), a
+`ParseException` will be thrown and users will be informed that the UUID provided is invalid.
+
+**Note**: If the recipe with that UUID does not exist in the app, a `CommandException` will be thrown and users will be
+informed that there is no recipe with that UUID in the recipe book.
+
+**Note**: The lifeline for `RecipeViewCommandParser` should end at the destroy marker (X) but
+due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+
+#### Alternatives considered:
+An alternative implementation considered is to find the first recipe with UUID that matches instead of filtering through
+the whole recipe list. Each recipe has a unique id, and hence the first instance of a recipe with match UUID should be
+the only recipe with that UUID. This could lead to faster search times to view a specific recipe.
+
+However, we do not expect a user to have so many recipes that performance would become an issue. We do not expect
+users to be frequently using this command either, since baking something requires much time and effort. Filtering
+through the whole list also confers an advantage of being able to assert that there is at most one such recipe
+with that particular UUID.
+
+### List recipe feature
+#### Implementation
+The List feature is implemented as a type of `Command`. It extends the abstract class `Command`.
+
+Given below is an example usage scenario and how the list feature behaves at each step. The recipe storage is assumed to
+be initialised with at least 2 recipes within `ModelManager`.
+
+Step 1. The user launches the application. All recipes will be shown as the current recipeList has not been filtered.
+
+Step 2. The user executes `view 1`. The `view` command will then update the recipeList to only contain the filtered
+recipe.
-### \[Proposed\] Undo/redo feature
+Step 3. The user then executes `list`. The `list` command will be parsed using the `Inventory App Parser` within
+`LogicManager`.
-#### Proposed Implementation
+Step 4. This parsed command will be executed once again with `LogicManager`.
-The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations:
+Step 5. During execution, `ModelManager#updateFilteredRecipeList` will be called with the `PREDICATE_SHOW_ALL_RECIPES`
+to update the current recipeList with all the recipes.
-* `VersionedAddressBook#commit()` — Saves the current address book state in its history.
-* `VersionedAddressBook#undo()` — Restores the previous address book state from its history.
-* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history.
+Step 6. After execution, the returned `CommandResult` will then be returned back to the `MainWindow` to be displayed.
-These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively.
+The following sequence diagram shows how the list recipe feature works:
-Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
+
-Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state.
+#### Design Considerations:
+**Aspect : How view executes:**
+- Alternative 1 (Current Choice): Gets the list from the "RecipeBook"
+ - Pro: Easy to implement
+ - Con: Need to constantly store current state
+- Alternative 2: Read the list from the storage
+ - Pro: No need to store current state after command
+ - Con: Need to access storage
-![UndoRedoState0](images/UndoRedoState0.png)
-Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state.
+### Search recipe feature
+#### Implementation
+The search recipe mechanism is implemented as a `Command`, extending from the `Command` abstract class.
-![UndoRedoState1](images/UndoRedoState1.png)
+Given below is an example usage scenario and how the search feature behaves at each step. For this scenario, it is assumed
+that at least one recipe is loaded into `ModelManager`
-Step 3. The user executes `add n/David …` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`.
+Step 1. The user launches the application. All recipes will be shown as the current recipeList has not been filtered.
-![UndoRedoState2](images/UndoRedoState2.png)
+Step 2. The user executes `search flour`. The `search` command will be parsed using the `Inventory App Parser` within
+`LogicManager`.
-
:information_source: **Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`.
+Step 3. By Polymorphism, `SearchCommandParser` is called on to handle the parsing. The `parse(String args)` function
+is called with the argument of `"flour"`.
-
+Step 4. A `RecipeIngredientNameMatchesPredicate` will be with the argument `"flour"` as its parameter.
-Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state.
+Step 5. The `SearchCommand` then filters the recipe list in `ModelManager` according to the predicate. The recipe
+list will display all recipes that require `"flour"`.
-![UndoRedoState3](images/UndoRedoState3.png)
+Step 6. After execution, the returned `CommandResult` will then be returned back to the `MainWindow` to be displayed.
-
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather
-than attempting to perform the undo.
+`SearchCommand` calls `Model#updateFilteredRecipeList(Predicate predicate)`, filtering the
+recipe list in `ModelManager` according to the predicate set.
-
+The following sequence diagram shows how the search recipe feature works:
-The following sequence diagram shows how the undo operation works:
+
-![UndoSequenceDiagram](images/UndoSequenceDiagram.png)
+### Delete recipe feature
+#### Implementation
+The delete recipe mechanism is implemented as a `Command`, extending from the `Command` abstract class.
-
:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+Given below is an example usage scenario and how the delete recipe mechanism behaves at each step. The application is
+assumed to be initialised with at least one recipe loaded in the `ModelManager`.
-
+Step 1. The user keys in `delete 1` into the UI command box. `LogicManager` takes this string command and executes it.
-The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state.
+Step 2. `InventoryAppParser` is then called to parse the `delete 1` command.
-
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
+Step 3. By Polymorphism, `DeleteCommandParser` is called on to handle the parsing. The `parse(String args)` function is
+called with the argument of "1" and the "1" is parsed as an `Integer`. This integer is then wrapped in a `UniqueId` constructor.
-
+Step 4: A `DeleteCommand` object is then created with the `UniqueId` created passed in as a parameter.
-Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged.
+Step 5: This `DeleteCommand` object is then executed. During execution, the recipe that matches with the `UniqueId`
+passed into the `DeleteCommand` is retrieved from the recipe list through the `Model#getRecipe(UniqueId uuid)` and
+`Model#deleteRecipe(Recipe recipe)` will be called with this recipe, causing the recipe to be deleted from the recipe list.
-![UndoRedoState4](images/UndoRedoState4.png)
+Step 6. After execution, the returned `CommandResult` will then be returned back to the `MainWindow` to be displayed.
-Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …` command. This is the behavior that most modern desktop applications follow.
+**Note**: If the argument is an invalid UUID (less than or equals to 0), a
+`ParseException` will be thrown and users will be informed that the UUID provided is invalid.
-![UndoRedoState5](images/UndoRedoState5.png)
+**Note**: If the recipe with that UUID does not exist in the app, a `CommandException` will be thrown and users will be
+informed that there is no recipe with that UUID in the recipe book.
-The following activity diagram summarizes what happens when a user executes a new command:
+The following sequence diagram shows how the DeleteCommand works:
+
-
+**Note**: In the diagram, `uuid` is the `UniqueId` that was passed into the `DeleteCommand` object.
-#### Design considerations:
+**Note**: The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML,
+the lifeline reaches the end of the diagram.
-**Aspect: How undo & redo executes:**
-* **Alternative 1 (current choice):** Saves the entire address book.
- * Pros: Easy to implement.
- * Cons: May have performance issues in terms of memory usage.
+### Modify recipe feature
+#### Implementation
+The modify recipe mechanism is implemented as a `Command`, extending from the `Command` abstract class.
-* **Alternative 2:** Individual command knows how to undo/redo by
- itself.
- * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted).
- * Cons: We must ensure that the implementation of each individual command are correct.
+Given below is an example usage scenario and how the modify recipe mechanism behaves at each step. The application is
+assumed to be initialised with at least one recipe loaded in the `ModelManager`.
-_{more aspects and alternatives to be added}_
+Step 1. The user keys in `modify i/1 n/Milk q/100 u/g` into the UI command box. `LogicManager` takes this string command
+and executes it.
-### \[Proposed\] Data archiving
+Step 2. `InventoryAppParser` is then called to parse the `modify i/1 n/Milk q/100 u/g` command.
-_{Explain here how the data archiving feature will be implemented}_
+Step 3. By Polymorphism, `ModifyCommandParser` is called on to handle the parsing. The `parse(String args)` function is
+called with the argument of "i/1 n/Milk q/100 u/g" and this is then tokenized into the UUID of the recipe that will be modified,
+the modified ingredient's name, amount and unit. An `Ingredient` is then created with this name, amount and unit specified.
+Step 4: A `ModifyCommand` object is then created with two parameters passed in - the UUID specified
+earlier wrapped in a `UniqueId` constructor as well as the `Ingredient` created earlier.
+
+Step 5: This `ModifyCommand` object is then executed. During execution, the recipe that matches with the `UniqueId`
+passed into the `ModifyCommand` is retrieved from the recipe list through the `Model#getRecipe(UniqueId uuid)`. This is the old recipe
+before the ingredients are modified.
+
+Step 6: Following that during the execution, there are 2 possibilities.
+
+If the recipe already contains the ingredient that was specified,
+the `Recipe#modifyIngredients(String oldIngredient, Ingredient newIngredient)` will be called with the
+name of the ingredient that is to be modified as well as the `Ingredient` that was passed into `ModifyCommand` such that
+the amount and unit of the ingredient can be modified.
+
+If the recipe does not already contain the ingredient that was specified, the `Recipe#addIngredient(Ingredient ingredient)`
+will be called with the `Ingredient` that was passed into `ModifyCommand` such that the ingredient is added to the
+ingredient list of that recipe.
+
+In both cases, this results in the creation of a new recipe object with a modified ingredient list.
+
+Step 7: The `Model#deleteRecipe(Recipe recipe)` is called with the old recipe that had the ingredient list before
+it was modified, deleting the old recipe from the recipe list.
+
+Step 8: The `Model#addRecipe(Recipe recipe)` is called with the new recipe that has the modified ingredient list, adding the new
+recipe to the recipe list.
+
+Step 9: `ModifyCommand` then calls `Model#updateFilteredRecipeList(Predicate predicate)`, filtering the
+recipe list in `ModelManager` with a `RecipeUuidMatchesPredicate`, which ultimately shows the recipe that matches
+the `UniqueId` that was passed into `ModifyCommand`.
+
+Step 10. After execution, the returned `CommandResult` will then be returned back to the `MainWindow` to be displayed.
+
+**Note**: If an invalid UUID (less than or equals to 0) is inputted, a
+`ParseException` will be thrown and users will be informed that the UUID provided is invalid.
+
+**Note**: If the recipe with that UUID does not exist in the app, a `CommandException` will be thrown and users will be
+informed that there is no recipe with that UUID in the recipe book.
+
+The following sequence diagram shows how the modify recipe feature works:
+
+
+
+**Note**: Due to limited space in the sequence diagram, certain behaviour could not be shown.
+1. `recipeUuid` is the `UniqueId` of the recipe that was passed into `ModifyCommand`.
+2. `newRecipe` is the modified version of the recipe that contains the modified list of ingredients. This recipe was
+created with interaction with `Recipe` as mentioned in Step 6 above.
+3. `predicate` is the `RecipeUuidMatchesPredicate` with the `UniqueId` that was passed into `ModifyCommand` as its parameter.
+
+**Note**: The lifeline for `ModifyCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML,
+the lifeline reaches the end of the diagram.
--------------------------------------------------------------------------------------------------------------------
@@ -251,127 +449,408 @@ _{Explain here how the data archiving feature will be implemented}_
--------------------------------------------------------------------------------------------------------------------
+
+
## **Appendix: Requirements**
-### Product scope
+### Product Scope
+
+**Target User Profile**
+
+- is a home-baker
+- has a need to manage and use their stock of ingredients
+- has a need to manage a significant amount of recipes and view the ingredients needed
+- is reasonably comfortable using CLI apps
+- prefer desktop apps over other types
+- can type fast
+
+**Value Propositions**
+
+Home-bakers often struggle with managing their recipe book as well as checking if they have the ingredients needed for
+a particular recipe. This application is designed for home-bakers to easily check what ingredients they already have and
+search for the recipes that they want along with the necessary ingredients required, which makes baking a more convenient
+and easy process.
+
+**User Stories**
+
+Priorities: High (must have) - `***`, Medium (nice to have) - `**`, Low (unlikely to have) - `*`
+
+| Priority | As a... | I want to ... | So that I can ... |
+|:---------|:-------:|-------------------------------------------:|--------------------------------------------------|
+| `***` | baker | view my stock | know what and the quantity of ingredients I have |
+| `***` | baker | add ingredients to my stock | update the stock I have |
+| `***` | baker | reduce ingredients' quantities in my stock | update the stock I have after I used the items |
+| `***` | baker | clear my stock | have an empty stock |
+| `***` | baker | find recipes by name | find a specific recipe |
+| `***` | baker | view recipes | see the steps and ingredients involved |
+| `***` | baker | add recipes to the recipe book | add new recipes in my recipe book |
+| `***` | baker | delete recipes from the recipe book | delete recipes I no longer need |
+| `**` | baker | modify recipes' ingredients | make changes to the ingredients needed |
+| `***` | baker | view the ingredients needed for a recipe | know if I have the necessary ingredients |
+| `***` | baker | request for help | learn how to use the recipe book when I'm lost |
+
+### Use Cases
+(For all use cases below, the System is the RecipeBook and the Actor is the user, unless specified otherwise)
+
+#### Use case: Add an ingredient to the stock
+#### MSS
+1. User requests to add a specific ingredient to their stock
+2. [Ba]king [Br]ead adds that ingredient to the stock
+
+ Use case ends.
+
+#### Extensions
+- 1a. User does not specify the quantity and/or unit of that ingredient
+ - [Ba]king [Br]ead shows an error message
+- 1b. User inputs an invalid quantity (less than or equals to 0 or non-numerical)
+ - [Ba]king [Br]ead shows an error message
+- 1c. The specified unit is not recognised
+ - [Ba]king [Br]ead shows an error message
+- 1d. There is an error in the format of the user's input command.
+ - [Ba]king [Br]ead shows an error message
+- 2a. The ingredient is not in the stock
+ - [Ba]king [Br]ead will add the ingredient along with its quantity to the stock
+- 2b. The ingredient is already in the stock
+ - [Ba]king [Br]ead will add the quantity specified to the specified ingredient already in the stock
+
+#### Use case: Reduce items' quantities in the stock
+#### MSS
+1. User requests to use up specific quantities of an ingredient
+2. [Ba]king [Br]ead reduces the quantity of that ingredient in the stock
+
+ Use case ends.
+
+#### Extensions
+- 1a. The specified unit is not recognised
+ - [Ba]king [Br]ead shows an error message
+- 1b. User inputs an invalid quantity (less than or equals to 0 or non-numerical)
+ - [Ba]king [Br]ead shows an error message
+- 1c. User either only inputs the quantity or the unit
+ - [Ba]king [Br]ead shows an error message
+- 1d. User inputs a unit that cannot be converted to the unit specified for the specified ingredient in the stock
+ - [Ba]king [Br]ead shows an error message
+- 1e. There is an error in the format of the user's input command.
+ - [Ba]king [Br]ead shows an error message
+- 2a. User does not specify the quantity of that ingredient used
+ - [Ba]king [Br]ead depletes the entire quantity of that ingredient
+- 2b. The quantity the user requests to use is more than the current quantity in stock
+ - [Ba]king [Br]ead depletes the entire quantity of that ingredient
+
+
+#### Use case: View the stock of ingredients
+#### MSS
+1. User requests to view the stock of specific ingredient(s)
+2. [Ba]king [Br]ead shows the ingredient(s) and the quantity of the ingredient(s)
+
+ Use case ends.
+
+#### Extensions
+- 1a. The specified ingredient(s) are not in the stock
+ - [Ba]king [Br]ead shows an error message
+- 2a. User does not specify what ingredients they would like to view
+ - [Ba]king [Br]ead shows the entire stock of ingredients
+- 2b. User specified one ingredient they would like to view
+ - [Ba]king [Br]ead shows the stock of that ingredient
+- 2c. User specified more than one ingredient they would like to view
+ - [Ba]king [Br]ead shows the stock of all the ingredients specified
+
+#### Use case: Find a specific recipe
+#### MSS
+1. User requests to view a specific recipe
+2. [Ba]king [Br]ead shows the corresponding recipe
+
+ Use case ends.
+
+#### Extensions
+- 1a. There is an error in the format of the user's input command
+ - [Ba]king [Br]ead shows an error message
+- 1b. The specified recipe does not exist
+ - [Ba]king [Br]ead shows an error message
+- 1c. The specified recipe UUID inputted is less than 1
+ - [Ba]king [Br]ead shows an error message
+
+#### Use case: List all recipes
+#### MSS
+1. User requests to list all possible recipes
+2. [Ba]king [Br]ead lists out all possible recipes
+
+ Use case ends.
+
+#### Extensions
+- 2a. There is only one recipe
+ - The recipe will be displayed in full, inclusive of steps
+- 2b. There are multiple recipes
+ - The recipes will only have their name and required ingredients listed
+- 2c. There are currently no recipes stored
+ - No updates will be made to the screen
+
+#### Use case: Add a new recipe
+#### MSS
+1. User requests to start adding a recipe
+2. User enters the name of the recipe
+3. User enters the ingredients of the recipe
+4. User enters command to move to the steps portion
+5. User enters the steps of the recipe
+6. User enters command to finalise the add recipe command
+7. [Ba]king [Br]ead adds the recipe to the recipe list
+
+ Use case ends
+
+#### Extensions:
+- 2a. Name is not of alphanumeric format
+ - [Ba]king [Br]ead shows an error message
+- 3a. Ingredient inputted does not follow given format
+ - [Ba]king [Br]ead shows an error message
+- 5a. Step inputted does not contain an index at the start
+ - [Ba]king [Br]ead shows an error message
+
+#### Use case: Search for a recipe with a specific ingredient
+#### MSS
+1. User requests to search for recipes with a specific ingredient
+2. [Ba]king [Br]ead displays all recipes that uses that specific ingredient
+
+ Use case ends
+
+#### Extensions:
+- 1a. User does not input an ingredient
+ - [Ba]king [Br]ead will show an error message
+- 2a. No recipe contains that ingredient
+ - No recipes will be displayed
+- 2b. One recipe contains that ingredient
+ - That one recipe will be shown in full, inclusive of steps
+- 2c. Multiple recipes contain that ingredient
+ - All recipes will be displayed without the steps
+
+#### Use case: Delete a recipe from the recipe list
+#### MSS
+1. User requests to delete a specific recipe
+2. [Ba]king [Br]ead deletes the corresponding recipe
+
+ Use case ends.
+
+#### Extensions:
+- 1a. The specified recipe does not exist
+ - [Ba]king [Br]ead shows an error message
+- 1b. The specified recipe UUID inputted is less than 1
+ - [Ba]king [Br]ead shows an error message
+- 1c. There is an error in the format of the user's input command.
+ - [Ba]king [Br]ead shows an error message
+
+
+#### Use case: Modify the ingredients in the recipe
+1. User requests to modify an ingredient in a specific recipe
+2. [Ba]king [Br]ead modifies the corresponding recipe
+
+#### Extensions:
+- 1a. The specified recipe does not exist
+ - [Ba]king [Br]ead shows an error message
+- 1b. The specified recipe UUID inputted is less than 1
+ - [Ba]king [Br]ead shows an error message
+- 1c. User inputs an invalid quantity (less than or equals to 0 or non-numerical)
+ - [Ba]king [Br]ead shows an error message
+- 1d. The specified unit is not recognised
+ - [Ba]king [Br]ead shows an error message
+- 1e. There is an error in the format of the user's input command.
+ - [Ba]king [Br]ead shows an error message
+- 2a. The ingredient specified in the command is already in the recipe's ingredient list
+ - [Ba]king [Br]ead modifies the ingredient in the ingredient list of the recipe
+- 2b. The ingredient specified in the command is not in the recipe's ingredient list
+ - [Ba]king [Br]ead adds the ingredient to the ingredient list of the recipe
+
+
-**Target user profile**:
+### Non-Functional Requirements
-* has a need to manage a significant number of contacts
-* prefer desktop apps over other types
-* can type fast
-* prefers typing to mouse interactions
-* is reasonably comfortable using CLI apps
+1. The system should work on any *mainstream* OS as long as it has Java `11` installed.
+2. The system should be able to hold 500 ingredients and recipes in total without any noticeable sluggishness.
+3. The system should be usable by any novice who is not familiar with computer programs.
+4. The system should be understandable by any novice who is not familiar with baking.
+5. The system should log error messages for user debugging & bug reporting purposes.
+6. The system should be well-documented for future extension and maintainability.
+7. The system should be backwards-compatible with data from older versions.
-**Value proposition**: manage contacts faster than a typical mouse/GUI driven app
+### Glossary
+- **Mainstream OS:** Windows, Linux, OS-X
+- **Unique identifier (UUID):** An identifier that is unique to all other identifiers, in this project, each recipe's
+UUID is unique.
+--------------------------------------------------------------------------------------------------------------------
-### User stories
+## **Appendix: Instructions for manual testing**
-Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
+Given below are instructions to test the app manually.
-| Priority | As a … | I want to … | So that I can… |
-| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- |
-| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App |
-| `* * *` | user | add a new person | |
-| `* * *` | user | delete a person | remove entries that I no longer need |
-| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list |
-| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident |
-| `*` | user with many persons in the address book | sort persons by name | locate a person easily |
+
-*{More to be added}*
+**Note:** These instructions only provide a starting point for testers to work on;
+testers are expected to do more *exploratory* testing.
-### Use cases
+
-(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise)
+### Launch and shutdown
-**Use case: Delete a person**
+1. Initial launch
-**MSS**
+ 1. Download the jar file and copy into an empty folder
-1. User requests to list persons
-2. AddressBook shows a list of persons
-3. User requests to delete a specific person in the list
-4. AddressBook deletes the person
+ 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
- Use case ends.
+1. Saving window preferences
-**Extensions**
+ 1. Resize the window to an optimum size. Move the window to a different location. Close the window.
-* 2a. The list is empty.
+ 1. Re-launch the app by double-clicking the jar file.
+ Expected: The most recent window size and location is retained.
- Use case ends.
+### Adding an ingredient
-* 3a. The given index is invalid.
+1. Adding an ingredient with quantity and unit
+ 1. Test case: `add n/Potato Starch q/100 u/g`
+ Expected: Adds Potato Starch to the ingredients or increases the amount of Potato Starch by 100g if it already exists.
+
+ 1. Other incorrect add commands to try : `add`, `add n/Flour`
+ Expected: Error details shown in the command result message.
- * 3a1. AddressBook shows an error message.
+### Using an ingredient
- Use case resumes at step 2.
+1. Using an ingredient with quantity and unit or neither
-*{More to be added}*
+ 1. Prerequisites: Ingredients to be used are currently in the ingredient list
-### Non-Functional Requirements
+ 1. Test case: `use n/Flour q/100 u/g`
+ Expected: Uses 100g of Potato Starch or all of it if there is less than 100g of it left.
-1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed.
-2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.
-3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
+ 1. Test case: `use n/Flour`
+ Expected: Uses the entire stock of Potato Starch.
-*{More to be added}*
+ 1. Other incorrect add commands to try : `use`, `use n/Flour q/100`
+ Expected: Error details shown in the command result message.
-### Glossary
+### Viewing of Ingredients
-* **Mainstream OS**: Windows, Linux, Unix, OS-X
-* **Private contact detail**: A contact detail that is not meant to be shared with others
+1. Views all or specific ingredients
---------------------------------------------------------------------------------------------------------------------
+ 1. Test case: `stock`
+ Expected: Displays all ingredients.
-## **Appendix: Instructions for manual testing**
+ 1. Test case: `stock butter`
+ Expected: Displays all ingredients with butter in their name.
-Given below are instructions to test the app manually.
+### Viewing all recipes
-
:information_source: **Note:** These instructions only provide a starting point for testers to work on;
-testers are expected to do more *exploratory* testing.
+1. Displays all recipes
-
+ 1. Test case: `list`
+ Expected: Displays all recipes.
-### Launch and shutdown
+### Viewing a specific recipe
-1. Initial launch
+1. Views a specific recipe based on the UUID
- 1. Download the jar file and copy into an empty folder
+ 1. Test case: `view 1`
+ Expected: Displays the recipe with UUID 1.
- 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
+ 1. Other incorrect delete commands to try: `view`, `view x`(where x is larger than the list size)
+ Expected: Error details shown in the command result message.
-1. Saving window preferences
+### Adding a recipe
- 1. Resize the window to an optimum size. Move the window to a different location. Close the window.
+1. Adds a recipe with the name, ingredients and steps
- 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained.
+ 1. Test case: `addrecipe`, `Bread`, `Flour 100g`, `Milk 50g`, `steps start`, `1. Mix Flour and Milk`, `2. Bake at 300C for 30min`, `complete recipe` (**Note**: Each block represents one line or one input)
+ Expected: Adds the recipe Bread with the ingredients and steps
-1. _{ more test cases … }_
+### Modifying a recipe
-### Deleting a person
+1. Modifies the ingredients of a recipe
-1. Deleting a person while all persons are being shown
+ 1. Prerequisite: To be modified recipe exists
+
+ 1. Test case: `modify i/1 n/Flour q/100 u/g`
+ Expected: Changes the quantity of flour to 100 if the ingredient exists. Adds the ingredient if it does not.
- 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list.
+### Searching for a recipe with a specific ingredient
- 1. Test case: `delete 1`
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.
+1. Searches for all recipes that contains the ingredient
- 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
+ 1. Test case: `search flour`
+ Expected: Displays all recipes that uses the ingredient flour
- 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous.
+### Deleting a recipe
-1. _{ more test cases … }_
+1. Deleting a recipe while all recipes are being shown
+
+ 1. Prerequisites: List all recipes using the `list` command. Multiple recipes in the list.
+
+ 1. Test case: `delete 1`
+ Expected: First recipe is deleted from the list. UUID of the deleted recipe shown in the command result message.
+
+ 1. Test case: `delete 0`
+ Expected: No recipe is deleted. Error details shown in the command result message.
+
+ 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
+ Expected: Similar to previous.
### Saving data
1. Dealing with missing/corrupted data files
- 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_
+ 1. Head over to the folder where your jar file is located and then delete both inventory.json and recipeBook.json within the data folder.
+ 2. Restart the application and new sample data should be used instead.
+
+--------------------------------------------------------------------------------------------------------------------
+
+## **Appendix: Planned Enhancements**
+#### 1. Recycling of UUIDs after deleting recipes
+ Currently, the UUIDs for the recipes are unique across deleted and non deleted recipes. As such, when deleting a recipe, the corresponding UUID will no longer
+ be available. For example, if there are recipes with the UUIDs 1, 2 and 3 and recipe with UUID 2 gets deleted, then the
+ next recipe added will have a UUID of 4 instead of 2 as 2 is no longer in use. We plan to make it such that when a recipe
+ gets deleted, the next recipe added will take on this UUID to ensure that the UUID does not end up getting too large.
+
+#### 2. Ensuring at least one ingredient in a recipe
+ When inputting the ingredients during the addrecipe commands, users are able to entirely skip this portion by just typing
+ steps start. This however is unrealistic as no recipe would require no ingredients to make. We plan to add a sanity check
+ to ensure that at least one ingredient is inserted into the recipe.
+
+#### 3. Ensuring at least one step in a recipe
+ As a continuation from enhancement 2, addrecipe is also able to execute successfully without inputting any steps. We plan to make it such that
+ users have to input at least one step.
+
+#### 4. Checking only one ingredient inserted
+ When adding ingredients to a recipe during the addrecipe command, there is no check to ensure that only one ingredient is
+ being inputted at any point in time. As such, inputs such as `flour 100g milk 100g` would be parsed as `name: flour 100g milk`,
+ `quantity: 100` `unit: g`. We plan to add a check that would ensure only one ingredient can be inputted at one time.
+ So `flour 100g` would work but `flour 100g milk 100g` would return an error message.
+
+#### 5. Allowing for use of default unit
+ When using ingredients, either both unit and quantity must be inputted or neither must be present. However, it would be
+ more efficient if we could just use input the quantity and use based on the unit of the ingredient. We plan to add a way
+ to input only the name and quantity without the units to be more intuitive. For example: `use n/flour q/100` will not
+ show an error but rather, consume 100 of whatever unit that flour is currently stored in.
+
+#### 6. Checking for recipe step
+ When adding recipe steps during the addrecipe command, the numbering of the steps is entirely dependent on the user and users
+ can input in the wrong order such as `1 4 5`. We plan to add a check where users need not type in the step number and the application will
+ automatically generate the step index as per the order of steps inputted.
+
+#### 7. Modifying the steps of a recipe
+ Currently, the modify function only allows users to modify the ingredients in the recipe. However, it may be useful for users
+ to be able to modify the steps of the recipe as well, especially if they want to add on to the ingredients in the list to
+ modify the recipe.
+
+
+--------------------------------------------------------------------------------------------------------------------
+
+## **Appendix: Effort**
+- Significant effort went into refactoring the entire code base to change the AddressBook entities into ingredients and recipes.
+- The recipe model and ingredient model were not trivial to design.
+ - Much consideration was given to the design of the system representing the different units of an ingredient and the conversion between them.
+ - The recipe model and how to build a recipe is also complex, since a recipe composes of many steps and ingredients.
+ - Compared to AB3, our application manages more entities and has entities of greater complexity.
+- The above also meant the add recipe and modify recipe feature were not easy to implement, and were very bug prone which took
+effort to fix.
+- The UI design took a significant effort, a mockup design was first done on figma to test the color combinations and layouts.
+ - Figuring out how to change the view from a recipe list to a full recipe view was not simple, how javafx updates its
+UI components is not explained or easily understood through the UI code.
+ - The UI is also made to be size responsive, the layout will retain its integrity even if the app is viewed in full-screen.
-1. _{ more test cases … }_
diff --git a/docs/Documentation.md b/docs/Documentation.md
index 3e68ea364e7..082e652d947 100644
--- a/docs/Documentation.md
+++ b/docs/Documentation.md
@@ -1,29 +1,21 @@
---
-layout: page
-title: Documentation guide
+ layout: default.md
+ title: "Documentation guide"
+ pageNav: 3
---
-**Setting up and maintaining the project website:**
-
-* We use [**Jekyll**](https://jekyllrb.com/) to manage documentation.
-* The `docs/` folder is used for documentation.
-* To learn how set it up and maintain the project website, follow the guide [_[se-edu/guides] **Using Jekyll for project documentation**_](https://se-education.org/guides/tutorials/jekyll.html).
-* Note these points when adapting the documentation to a different project/product:
- * The 'Site-wide settings' section of the page linked above has information on how to update site-wide elements such as the top navigation bar.
- * :bulb: In addition to updating content files, you might have to update the config files `docs\_config.yml` and `docs\_sass\minima\_base.scss` (which contains a reference to `AB-3` that comes into play when converting documentation pages to PDF format).
-* If you are using Intellij for editing documentation files, you can consider enabling 'soft wrapping' for `*.md` files, as explained in [_[se-edu/guides] **Intellij IDEA: Useful settings**_](https://se-education.org/guides/tutorials/intellijUsefulSettings.html#enabling-soft-wrapping)
+# Documentation Guide
+* We use [**MarkBind**](https://markbind.org/) to manage documentation.
+* The `docs/` folder contains the source files for the documentation website.
+* To learn how set it up and maintain the project website, follow the guide [[se-edu/guides] Working with Forked MarkBind sites](https://se-education.org/guides/tutorials/markbind-forked-sites.html) for project documentation.
**Style guidance:**
* Follow the [**_Google developer documentation style guide_**](https://developers.google.com/style).
+* Also relevant is the [_se-edu/guides **Markdown coding standard**_](https://se-education.org/guides/conventions/markdown.html).
-* Also relevant is the [_[se-edu/guides] **Markdown coding standard**_](https://se-education.org/guides/conventions/markdown.html)
-
-**Diagrams:**
-
-* See the [_[se-edu/guides] **Using PlantUML**_](https://se-education.org/guides/tutorials/plantUml.html)
-**Converting a document to the PDF format:**
+**Converting to PDF**
-* See the guide [_[se-edu/guides] **Saving web documents as PDF files**_](https://se-education.org/guides/tutorials/savingPdf.html)
+* See the guide [_se-edu/guides **Saving web documents as PDF files**_](https://se-education.org/guides/tutorials/savingPdf.html).
diff --git a/docs/Gemfile b/docs/Gemfile
deleted file mode 100644
index c8385d85874..00000000000
--- a/docs/Gemfile
+++ /dev/null
@@ -1,10 +0,0 @@
-# frozen_string_literal: true
-
-source "https://rubygems.org"
-
-git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
-
-gem 'jekyll'
-gem 'github-pages', group: :jekyll_plugins
-gem 'wdm', '~> 0.1.0' if Gem.win_platform?
-gem 'webrick'
diff --git a/docs/Logging.md b/docs/Logging.md
index 5e4fb9bc217..589644ad5c6 100644
--- a/docs/Logging.md
+++ b/docs/Logging.md
@@ -1,8 +1,10 @@
---
-layout: page
-title: Logging guide
+ layout: default.md
+ title: "Logging guide"
---
+# Logging guide
+
* We are using `java.util.logging` package for logging.
* The `LogsCenter` class is used to manage the logging levels and logging destinations.
* The `Logger` for a class can be obtained using `LogsCenter.getLogger(Class)` which will log messages according to the specified logging level.
diff --git a/docs/SettingUp.md b/docs/SettingUp.md
index 275445bd551..7b53ff6ddfd 100644
--- a/docs/SettingUp.md
+++ b/docs/SettingUp.md
@@ -1,27 +1,32 @@
---
-layout: page
-title: Setting up and getting started
+ layout: default.md
+ title: "Setting up and getting started"
+ pageNav: 3
---
-* Table of Contents
-{:toc}
+# Setting up and getting started
+
+
--------------------------------------------------------------------------------------------------------------------
## Setting up the project in your computer
-
:exclamation: **Caution:**
+
+**Caution:**
Follow the steps in the following guide precisely. Things will not work out if you deviate in some steps.
-
+
First, **fork** this repo, and **clone** the fork into your computer.
If you plan to use Intellij IDEA (highly recommended):
1. **Configure the JDK**: Follow the guide [_[se-edu/guides] IDEA: Configuring the JDK_](https://se-education.org/guides/tutorials/intellijJdk.html) to to ensure Intellij is configured to use **JDK 11**.
-1. **Import the project as a Gradle project**: Follow the guide [_[se-edu/guides] IDEA: Importing a Gradle project_](https://se-education.org/guides/tutorials/intellijImportGradleProject.html) to import the project into IDEA.
- :exclamation: Note: Importing a Gradle project is slightly different from importing a normal Java project.
+1. **Import the project as a Gradle project**: Follow the guide [_[se-edu/guides] IDEA: Importing a Gradle project_](https://se-education.org/guides/tutorials/intellijImportGradleProject.html) to import the project into IDEA.
+
+ Note: Importing a Gradle project is slightly different from importing a normal Java project.
+
1. **Verify the setup**:
1. Run the `seedu.address.Main` and try a few commands.
1. [Run the tests](Testing.md) to ensure they all pass.
@@ -34,10 +39,11 @@ If you plan to use Intellij IDEA (highly recommended):
If using IDEA, follow the guide [_[se-edu/guides] IDEA: Configuring the code style_](https://se-education.org/guides/tutorials/intellijCodeStyle.html) to set up IDEA's coding style to match ours.
-
:bulb: **Tip:**
+
+ **Tip:**
Optionally, you can follow the guide [_[se-edu/guides] Using Checkstyle_](https://se-education.org/guides/tutorials/checkstyle.html) to find how to use the CheckStyle within IDEA e.g., to report problems _as_ you write code.
-
+
1. **Set up CI**
@@ -45,7 +51,7 @@ If you plan to use Intellij IDEA (highly recommended):
1. **Learn the design**
- When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [AddressBook’s architecture](DeveloperGuide.md#architecture).
+ When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [[Ba]king [Br]ead’s architecture](DeveloperGuide.md#architecture).
1. **Do the tutorials**
These tutorials will help you get acquainted with the codebase.
diff --git a/docs/Testing.md b/docs/Testing.md
index 8a99e82438a..78ddc57e670 100644
--- a/docs/Testing.md
+++ b/docs/Testing.md
@@ -1,12 +1,15 @@
---
-layout: page
-title: Testing guide
+ layout: default.md
+ title: "Testing guide"
+ pageNav: 3
---
-* Table of Contents
-{:toc}
+# Testing guide
---------------------------------------------------------------------------------------------------------------------
+
+
+
+
## Running tests
@@ -19,8 +22,10 @@ There are two ways to run tests.
* **Method 2: Using Gradle**
* Open a console and run the command `gradlew clean test` (Mac/Linux: `./gradlew clean test`)
-
:link: **Link**: Read [this Gradle Tutorial from the se-edu/guides](https://se-education.org/guides/tutorials/gradle.html) to learn more about using Gradle.
-
+
+
+**Link**: Read [this Gradle Tutorial from the se-edu/guides](https://se-education.org/guides/tutorials/gradle.html) to learn more about using Gradle.
+
--------------------------------------------------------------------------------------------------------------------
diff --git a/docs/UserGuide (Pristine).md b/docs/UserGuide (Pristine).md
new file mode 100644
index 00000000000..9b30020fd80
--- /dev/null
+++ b/docs/UserGuide (Pristine).md
@@ -0,0 +1,203 @@
+---
+ layout: default.md
+ title: "User Guide"
+ pageNav: 3
+---
+
+# AB-3 User Guide
+
+AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps.
+
+
+
+
+--------------------------------------------------------------------------------------------------------------------
+
+## Quick start
+
+1. Ensure you have Java `11` or above installed in your Computer.
+
+1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases).
+
+1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook.
+
+1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.jar` command to run the application.
+ A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
+ ![Ui](images/Ui-annotated.png)
+
+1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
+ Some example commands you can try:
+
+ * `list` : Lists all contacts.
+
+ * `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book.
+
+ * `delete 3` : Deletes the 3rd contact shown in the current list.
+
+ * `clear` : Deletes all contacts.
+
+ * `exit` : Exits the app.
+
+1. Refer to the [Features](#features) below for details of each command.
+
+--------------------------------------------------------------------------------------------------------------------
+
+## Features
+
+
+
+**Notes about the command format:**
+
+* Words in `UPPER_CASE` are the parameters to be supplied by the user.
+ e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`.
+
+* Items in square brackets are optional.
+ e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`.
+
+* Items with `…` after them can be used multiple times including zero times.
+ e.g. `[t/TAG]…` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc.
+
+* Parameters can be in any order.
+ e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable.
+
+* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
+ e.g. if the command specifies `help 123`, it will be interpreted as `help`.
+
+* If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application.
+
+
+### Viewing help : `help`
+
+Shows a message explaning how to access the help page.
+
+![help message](images/helpMessage.png)
+
+Format: `help`
+
+
+### Adding a person: `add`
+
+Adds a person to the address book.
+
+Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…`
+
+
+
+**Tip:** A person can have any number of tags (including 0)
+
+
+Examples:
+* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01`
+* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal`
+
+### Listing all persons : `list`
+
+Shows a list of all persons in the address book.
+
+Format: `list`
+
+### Editing a person : `edit`
+
+Edits an existing person in the address book.
+
+Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
+
+* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …
+* At least one of the optional fields must be provided.
+* Existing values will be updated to the input values.
+* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative.
+* You can remove all the person’s tags by typing `t/` without
+ specifying any tags after it.
+
+Examples:
+* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively.
+* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags.
+
+### Locating persons by name: `find`
+
+Finds persons whose names contain any of the given keywords.
+
+Format: `find KEYWORD [MORE_KEYWORDS]`
+
+* The search is case-insensitive. e.g `hans` will match `Hans`
+* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
+* Only the name is searched.
+* Only full words will be matched e.g. `Han` will not match `Hans`
+* Persons matching at least one keyword will be returned (i.e. `OR` search).
+ e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
+
+Examples:
+* `find John` returns `john` and `John Doe`
+* `find alex david` returns `Alex Yeoh`, `David Li`
+ ![result for 'find alex david'](images/findAlexDavidResult.png)
+
+### Deleting a person : `delete`
+
+Deletes the specified person from the address book.
+
+Format: `delete INDEX`
+
+* Deletes the person at the specified `INDEX`.
+* The index refers to the index number shown in the displayed person list.
+* The index **must be a positive integer** 1, 2, 3, …
+
+Examples:
+* `list` followed by `delete 2` deletes the 2nd person in the address book.
+* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command.
+
+### Clearing all entries : `clear`
+
+Clears all entries from the address book.
+
+Format: `clear`
+
+### Exiting the program : `exit`
+
+Exits the program.
+
+Format: `exit`
+
+### Saving the data
+
+AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
+
+### Editing the data file
+
+AddressBook data are saved automatically as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file.
+
+
+
+**Caution:**
+If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
+
+
+### Archiving data files `[coming in v2.0]`
+
+_Details coming soon ..._
+
+--------------------------------------------------------------------------------------------------------------------
+
+## FAQ
+
+**Q**: How do I transfer my data to another Computer?
+**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder.
+
+--------------------------------------------------------------------------------------------------------------------
+
+## Known issues
+
+1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again.
+
+--------------------------------------------------------------------------------------------------------------------
+
+## Command summary
+
+Action | Format, Examples
+-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…` e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague`
+**Clear** | `clear`
+**Delete** | `delete INDEX` e.g., `delete 3`
+**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…` e.g.,`edit 2 n/James Lee e/jameslee@example.com`
+**Find** | `find KEYWORD [MORE_KEYWORDS]` e.g., `find James Jake`
+**List** | `list`
+**Help** | `help`
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index 57437026c7b..e7f2e128132 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,151 +1,484 @@
---
-layout: page
-title: User Guide
+layout: default.md
+title: "User Guide"
+pageNav: 3
---
-AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps.
+# [Ba]king [Br]ead User Guide
+Welcome **new bakers**!
+If you're feeling overwhelmed by the chaos of baking, fret not because [Ba]king [Br]ead is here to save the day!
+We're here to help you manage the overwhelming number of ingredients in your kitchen and the endless recipes you have stored,
+making your baking experience a lot smoother!
-* Table of Contents
-{:toc}
+[Ba]king [Br]ead will help you to:
+1. Keep track of all your baking ingredients in your kitchen
+2. Collate all your recipes digitally for easy viewing
+3. Achieve maximum efficiency
+
+Our application's command-line interface, coupled with your fast fingers, makes baking a whole lot more seamless and relaxing!
+
+Long gone are the days when you have to rummage through your cupboards to see what you have. Now with [Ba]king [Br]ead,
+you will never forget what ingredient you need to stock up on during your next trip to the supermarket. And now, you will never
+have to flip through endless heavy physical recipe books just to find a recipe you like.
+
+So, what are you waiting for? Master [Ba]king [Br]ead and fast-track your baking journey now!
+
+## Using this Guide
+This user guide contains all the information you will need in order to use [Ba]king [Br]ead. For new users, this document will also guide you in using the application,
+starting with the more essential features.
+
+**For new users**:
+- Start using [Ba]king [Br]ead by heading over to the [Quick Start](#quick-start) to learn how to set up the app and use it.
+
+**For experienced users:**
+- Utilise the [Table of Contents](#table-of-contents) to refresh your memory on the various features
+
+Listed below are the definitions of the terminology used throughout the User Guide
+
+| Term | Definition |
+|:-------------------------------------|:----------------------------------------------------------------------------------------------------------------|
+| GUI (Graphical User Interface) | The visual interface of [Ba]king [Br]ead |
+| Command | A set of words that execute a certain operation |
+| Parameter | Key information that a user specifies in a command |
+| UUID (Universally Unique IDentifier) | A unique number that is used to identify the recipes |
+| Case-insensitive | Parameters are not treated differently if they are in capitals or lower-case text. Eg. MILK is the same as milk |
+
+Do also take note of the following highlighted information panels as we will be using this throughout the User Guide.
+
+
+
+**Note:**
+Used to highlight essential information
+
+
+
+
+**Tip:**
+Used to highlight helpful tips
+
+
+
+
+**Caution:**
+Used to highlight errors to look out for
+
+
+
--------------------------------------------------------------------------------------------------------------------
+## Table of Contents
+* [Getting Started](#quick-start)
+* [Navigating the GUI](#navigating-the-gui)
+* [Features](#features)
+ * [Help](#viewing-help-help)
+ * [Add ingredient](#adding-an-ingredient-add)
+ * [Use ingredient](#using-up-ingredients-use)
+ * [Find quantity of ingredient](#finding-the-quantity-of-an-ingredient-by-name-stock)
+ * [Clear ingredient list](#clearing-all-ingredients-from-ingredient-stock-clear)
+ * [View list of all recipes](#listing-all-recipes-list)
+ * [View a recipe](#viewing-specific-recipes-view)
+ * [Add a recipe](#adding-recipes-addrecipe)
+ * [Modify a recipe](#modifying-recipes-modify)
+ * [Search for recipes that have an ingredient](#searching-recipes-given-an-ingredient-search)
+ * [Exit the program](#exiting-the-program-exit)
+ * [Save the data](#saving-the-data)
+ * [Edit the data file](#editing-the-data-file)
+* [Common Errors](#common-possible-errors)
+* [FAQ](#faq)
+* [Command Summary](#command-summary)
+
+
## Quick start
+New to [Ba]king [Br]ead? Not to worry, simply follow the instructions below to get started!
+### Setting up [Ba]king [Br]ead
1. Ensure you have Java `11` or above installed in your Computer.
-1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases).
+2. Download the latest `bakingbread.jar` from [here](https://github.com/AY2324S1-CS2103T-F10-3/tp/releases).
+
+3. Copy the file to the folder you want to use as the _home folder_ for [Ba]king [Br]ead.
+
+4. Double-click on the jar and a GUI similar to the one below should appear in a few seconds. If this does not work for you, check the [FAQ](#faq)
+
+5. Some sample data has been preloaded into the application
+
+![Ui](images/Ui.png)
+
+
+
+### Navigating the GUI
+The GUI may be confusing if you are a first time user of our application. Here is the breakdown of the GUI for [Ba]king [Br]ead.
+![Ui](images/Ui-annotated.png)
+**Input box:** This is where you will be inputting your commands.
+**Command result box:** Where the application will return a message regarding the outcome of command inputted.
+**Ingredients list:** Contain a list view of all your ingredients with their specified quantity and unit.
+**Recipe list:** Contains a list view of all your recipes.
-1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook.
+
-1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.jar` command to run the application.
- A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.png)
+### Tutorials
+#### Adding your first ingredient
+Time to add your first ingredient! Take any ingredient from your kitchen, weigh the quantity of that ingredient and
+let's get started!
+1. Suppose that you want to record 100 grams of flour. Type in the command
+into the application's input box as shown below.
+
+2. Press enter to execute the command.
+3. You should see the ingredient added into your ingredient list as seen below. Hooray! You have added your first ingredient!
+
-1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
- Some example commands you can try:
+#### Adding your first recipe
+Recipe adding is a little more complicated! But don't worry, you will get the hang of it in no time! Refer to the
+infographic below for step-by-step instructions on adding your first recipe.
- * `list` : Lists all contacts.
+![Addrecipe Instructions](images/ug-images/addrecipe_instructions1.png)
+![Addrecipe Instructions](images/ug-images/addrecipe_instructions2.png)
- * `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book.
+And there you have it, your first recipe added to [Ba]king [Br]ead!
- * `delete 3` : Deletes the 3rd contact shown in the current list.
+
- * `clear` : Deletes all contacts.
+If you are stuck at any point, typing **`help`** and pressing Enter will open the help window to guide you along.
+
- * `exit` : Exits the app.
+
-1. Refer to the [Features](#features) below for details of each command.
+Refer to the [Features](#features) below for details of the other commands available!
+
--------------------------------------------------------------------------------------------------------------------
+
## Features
-
+
-**:information_source: Notes about the command format:**
+**Notes about the command format:**
* Words in `UPPER_CASE` are the parameters to be supplied by the user.
- e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`.
-
-* Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`.
+ e.g. in `add n/NAME`, `NAME` is a parameter which can be specified, like so: `add n/Milk`.
* Items with `…` after them can be used multiple times including zero times.
- e.g. `[t/TAG]…` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc.
+ e.g. `[NAME]…` can be used as ` ` (i.e. 0 times), `milk`, `milk flour` etc.
* Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable.
+ e.g. if the command specifies `n/NAME q/QUANTITY`, `q/QUANTITY n/NAME` is also acceptable.
+
+* Parameters are case-insensitive.
+ e.g. A parameter specified as `n/Milk` is functionally identical to `n/MILK` or `n/milk`.
-* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
+* Extraneous parameters for commands that do not require parameters (such as `help`, `exit` , `list` and `clear`) will be ignored.
e.g. if the command specifies `help 123`, it will be interpreted as `help`.
+
-* If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application.
-
+#### List of parameter types:
+
+| Parameter | Description | Constraints | Valid examples | Invalid examples |
+|:----------|:-----------------------------------:|--------------------------------------------------|-----------------------|------------------|
+| `n/` | Name of the ingredient | Alphanumeric characters (a to z, A to Z, 0 to 9) | Flour, Chocolate Chip | Chocolate-Chip |
+| `q/` | Quantity of the ingredient | Must be numeric and more than 0 | 1000, 1, 200 | -100, hundred, 0 |
+| `u/` | Unit used to measure the ingredient | Must be one of the supported units | GRAM, KILOGRAM, PIECE | ML, L, TSP |
+| `i/` | UUID of recipe | UUID must be at least 1 | 1, 2, 3 | 0, -1, 0.1 |
-### Viewing help : `help`
-Shows a message explaning how to access the help page.
+
-![help message](images/helpMessage.png)
+#### Supported Units:
+
+| Unit | Alias |
+|----------|---------------------------------------|
+| GRAM | g, gram, GRAM |
+| KILOGRAM | kg, kilogram, KILOGRAM |
+| PIECE | pc, pcs, piece, pieces, PIECE, PIECES |
+
+
+### Viewing help : `help`
+
+If you are not sure how to use [Ba]king [Br]ead, this command will show a pop-up with a link to access the help page.
Format: `help`
+
+
+
+
+### Adding an ingredient: `add`
+
+Adds an ingredient to the stock.
+
+Format: `add n/NAME q/QUANTITY u/UNIT`
+
+* If there is some of the specified ingredient already in the stock, using this command will combine the newly added quantity with the existing quantity.
+
+Restrictions:
+* Unit specified must be supported.
+* Quantity must be a positive number.
+* When adding an ingredient that is already in the stock, the unit conversion must be possible. For example, if the unit
+ of the ingredient which is already in the stock is in GRAM/KILOGRAM, you cannot add the same ingredient in terms of PIECE.
+ For example, if the ingredient in the stock is 100 grams of Flour, `add n/flour q/50 u/PIECE`
+ would throw an error.
+
+Examples:
+* `add n/Flour q/1 u/kg` adds 1kg of Flour to the stock.
+* `add n/Milk q/600 u/g` adds 600g of Milk to the stock.
+
+
+
+Example Used: `add n/Flour q/1 u/kg` **Note**: Sample started with 3kg of Flour in the stock.
+
+
+
+
-### Adding a person: `add`
+Example Used: `add n/Milk q/600 u/g` **Note**: Sample did not have any Milk in the stock.
-Adds a person to the address book.
+
-Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…`
+### Using up ingredients : `use`
-
:bulb: **Tip:**
-A person can have any number of tags (including 0)
-
+Depletes a specified quantity of an ingredient from the stock.
+
+Format: `use n/NAME [q/QUANTITY] [u/UNIT]`
+
+* If no quantity and unit is specified, the entire stock of the specified ingredient will be depleted.
+* If the quantity depleted exceeds the current quantity in stock, the entire stock will be depleted but will not go into the negative.
+* Note that the specified name **must** be exact. As such, `use n/butters` will not work if the ingredient list contains `butter`.
+
+Restrictions:
+* Unit specified must be supported.
+* The quantity provided must be more than 0.
+* When specifying the quantity, the unit conversion must be possible. If the unit
+ of the ingredient that is in the stock is in GRAM/KILOGRAM, the use command cannot specify PIECE for using that
+ specific ingredient, and vice versa. For example, if the ingredient in the stock is 100g of Flour, `use n/flour q/50 u/PIECE`
+ would throw an error.
+* When using up an ingredient, an error will be shown if the user inputs only the quantity or only the unit. Either both the unit and quantity must be specified, or neither.
+
+Examples:
+* `use n/Milk q/600 u/g` Depletes the current quantity of milk by 600g.
+* `use n/Egg` Fully depletes the current quantity of egg.
+
+Example Used: `use n/Milk q/600 u/g` **Note**: Sample started with 1kg of Milk in the stock.
+
+
+
+
+
+If you try to use an ingredient which is not in the stock, the error message "Ingredient does not exist in Inventory" will appear.
+Only ingredients already in the stock can be specified when using this command.
+
+
+### Finding the quantity of an ingredient by name: `stock`
+
+Filters the list of ingredients to only the specified ingredient(s).
+
+Format: `stock [NAME]…`
+
+* Multiple ingredients can be specified e.g. `stock Flour Butter` will list the quantities of both ingredients.
+* If no ingredients are specified, the quantity of all ingredients will be listed.
+* Only full words will be matched e.g. `Flou` will not match `Flour`.
+* For ingredients with names comprising multiple words, any ingredient that contains the specified word will also be listed.
+ e.g. `stock butter` will display both `butter` and `butter stick`.
+* When no names are specified, all ingredients will be displayed.
Examples:
-* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01`
-* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal`
+* `stock Butter` will show the `Butter` ingredient in the list.
+* `stock Butter flour` will show both the `Butter` and `Flour` ingredient in the list.
+* `stock` lists all ingredients.
+
+
+
+Example used: `stock butter`
-### Listing all persons : `list`
+
-Shows a list of all persons in the address book.
+
+
+### Clearing all ingredients from ingredient stock : `clear`
+
+Clears all ingredients from the ingredient stock.
+
+Format: `clear`
+
+Example used: `clear`
+
+
+
+
+
+### Listing all Recipes : `list`
+
+Lists all recipes that are currently stored in [Ba]king [Br]ead.
Format: `list`
-### Editing a person : `edit`
+Example Used: `list`
-Edits an existing person in the address book.
+
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
+### Viewing Specific Recipes : `view`
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …
-* At least one of the optional fields must be provided.
-* Existing values will be updated to the input values.
-* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative.
-* You can remove all the person’s tags by typing `t/` without
- specifying any tags after it.
+Views a specific recipe in [Ba]king [Br]ead.
+
+Format: `view UUID`
+* `UUID` must be an integer greater than or equal to 1.
+* To toggle back to listing all recipes, use the `list` command.
Examples:
-* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively.
-* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags.
+* `view 1` views the recipe with `UUID` of 1.
+* `view 21` views the recipe with `UUID` of 21.
+
+Example Used: `view 1`
+
+
+
+
+
+
+**Note:**
+Only recipes that are currently in the recipe list can be viewed.
+
+
+
+### Adding Recipes : `addrecipe`
+
+Adds a new recipe to the recipe book. Each line in this command should be entered one by one.
+
+Format:
+```
+addrecipe
+NAME
+(INGREDIENT_NAME) (INGREDIENT_QUANTITY)(INGREDIENT_UNIT)
+⋮
+steps start
+1. STEP 1
+2. STEP 2
+⋮
+complete recipe
+```
+
+* Name of Recipe needs to be of Alphanumeric format.
+* Ingredients specified **must** be of the format `(NAME) (QUANTITY)(UNIT)`, such as `flour 100g`.
+* The quantity of the ingredient must be positive and the unit must be supported.
+* For the recipe steps, the format to follow is `(STEP_NUMBER). (STEP)` such as `1. Mix Water and Flour`.
+* For the recipe steps, you should input the appropriate step number at each step, or the order of the steps may be incorrect in the completed recipe.
+
+
+
+**Note**: There is no error message when inputting negative quantities for ingredients. As such, it is up to the user to ensure
+ that a positive quantity is specified.
+
+
+Example:
+```
+addrecipe
+Bread
+Water 100g
+Flour 1kg
+steps start
+1. Mix Water and Flour
+2. Bake at 180C
+complete recipe
+```
+
+
+When encountering the error message `Failed to add ingredient`, ensure that the ingredient specified follows the following format **exactly** : `(NAME) (QUANTITY)(UNIT)` Example: `Milk 100g`
+
+
+
+When encountering the error message `Failed to add step`, ensure that the index(A whole number) has been specified been in before keying in the step itself
+
+
+
+**Caution:** As this function relies heavily on the user's input, please do check that your input is of the correct format.
+
+
+### Modifying Recipes : `modify`
+
+Modifies the ingredients in a recipe.
-### Locating persons by name: `find`
+Format: `modify i/UUID n/NAME q/QUANTITY u/UNIT`
+* `UUID` must be an integer greater than or equal to 1.
+* The quantity provided must be more than 0.
+* The unit used must be supported.
-Finds persons whose names contain any of the given keywords.
+Example:
+* Assuming flour is used in the recipe, `modify i/1 n/Flour q/100 u/g` modifies the `Flour` ingredient in the recipe to 100 grams of flour.
+* Assuming flour is not used in the recipe, `modify i/1 n/Flour q/100 u/g` adds the 100 grams of the `Flour` ingredient to the recipe.
-Format: `find KEYWORD [MORE_KEYWORDS]`
+
-* The search is case-insensitive. e.g `hans` will match `Hans`
-* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
-* Only the name is searched.
-* Only full words will be matched e.g. `Han` will not match `Hans`
-* Persons matching at least one keyword will be returned (i.e. `OR` search).
- e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
+Example Used: `modify i/1 n/Flour q/100 u/g`
+
+**Note**: Recipe being modified has 200g of flour as part of the ingredient list
+
+
+
+
+
+Example Used: `modify i/1 n/Cream q/100 u/g`
+**Note**: Recipe being modified does not have cream in its ingredient list
+
+
+
+
+
+**Note:**
+Only recipes that are currently in the recipe list can be modified.
+
+
+
+
+**Note**: After a recipe has been modified, it will be pushed to the bottom of the recipe list.
+
+
+
+### Deleting Recipes : `delete`
+
+Deletes a specific recipe from [Ba]king [Br]ead when you longer need it.
+
+Format: `delete UUID`
+
+* `UUID` must be an integer greater than or equal to 1.
+* `UUID` should match one of the UUIDs of the existing recipes.
Examples:
-* `find John` returns `john` and `John Doe`
-* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png)
+* `delete 1` deletes the recipe with `UUID` of 1.
+* `delete 21` deletes the recipe with `UUID` of 21.
-### Deleting a person : `delete`
+
-Deletes the specified person from the address book.
+**Note:**
+Only recipes that are currently in the recipe list can be deleted.
+
-Format: `delete INDEX`
+### Searching Recipes given an ingredient: `search`
-* Deletes the person at the specified `INDEX`.
-* The index refers to the index number shown in the displayed person list.
-* The index **must be a positive integer** 1, 2, 3, …
+Searches for recipes that include a specific ingredient.
+
+Format: `search NAME`
+
+* `NAME` cannot be empty.
+* `NAME` is case-insensitive.
+* If none of the recipes contain that ingredient, an empty recipe list will be displayed instead.
+* For ingredients with multiple words, `NAME` must be an **exact** match. As such, `search chocolate` will not display recipes
+ that contain `chocolate chips`.
Examples:
-* `list` followed by `delete 2` deletes the 2nd person in the address book.
-* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command.
+* `search flour` searches for all recipes that use `flour`
+* `search butter` searches for all recipes that use `butter`
-### Clearing all entries : `clear`
+
-Clears all entries from the address book.
+Example used: `search flour` **Note**: Multiple recipes use the ingredient flour
-Format: `clear`
+
+
+
+
+Example used: `search blueberry` **Note**: Only one recipe uses the ingredient blueberry
+
+
### Exiting the program : `exit`
@@ -155,43 +488,64 @@ Format: `exit`
### Saving the data
-AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
+Inventory and Recipe data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
-### Editing the data file
+
-AddressBook data are saved automatically as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file.
+**Note**: When the application loads for the first time and no command is run, no inventory or recipe data will be saved.
+
-
:exclamation: **Caution:**
-If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
-
+### Editing the data file
+
+Inventory data are saved automatically as a JSON file `[JAR file location]/data/inventory.json`. Advanced users are welcome to update data directly by editing that data file.
-### Archiving data files `[coming in v2.0]`
+
-_Details coming soon ..._
+**Caution:**
+If your changes to the data file makes its format invalid, all data will be discarded and you will start with an empty data file the next run. Hence, it is recommended to take a backup of the file before editing it.
+
--------------------------------------------------------------------------------------------------------------------
-## FAQ
+### Common Possible Errors
-**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder.
+| Error Message | What to do |
+|-----------------------------------|----------------------------------------------------------------------------------------|
+| Unknown command | Ensure that the command specified is one of the commands mentioned within this document |
+| Invalid Command Format | Ensure that all parameters have specified and that none are left empty |
+| Names should only contain alphanumeric characters and spaces, and it should not be blank | Ensure that only letters and numbers are used for names |
+| Quantity has to be positive | Ensure that the quantity value specified is **greater** than 0 |
+| There is no recipe with the recipe UUID provided in the recipe book | Check the UUID of recipe attempting to use and ensure that it is correct |
+| The recipe UUID provided is invalid | Ensure that the UUID you have entered is valid (ie. it is a positive integer) |
--------------------------------------------------------------------------------------------------------------------
-## Known issues
+## FAQ
+
+**Q**: How do I transfer my data to another Computer?
+**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous [Ba]king [Br]ead home folder.
-1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again.
+**Q**: The application does not run when I double-click it!
+**A**: First, check if you have Java `11` installed in your computer. If you do and still have issues, try this: Open a command terminal, `cd` into the
+folder you put the jar file in, and use the `java -jar bakingbread.jar` command to run the application.
--------------------------------------------------------------------------------------------------------------------
## Command summary
-Action | Format, Examples
---------|------------------
-**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…` e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague`
-**Clear** | `clear`
-**Delete** | `delete INDEX` e.g., `delete 3`
-**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…` e.g.,`edit 2 n/James Lee e/jameslee@example.com`
-**Find** | `find KEYWORD [MORE_KEYWORDS]` e.g., `find James Jake`
-**List** | `list`
-**Help** | `help`
+**Note**: For `AddRecipe`, each command is to be run line by line.
+
+| Action | Format, Examples |
+|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **Add** | `add n/NAME q/QUANTITY u/UNIT` e.g., `add n/milk q/600 u/g` |
+| **Clear** | `clear` |
+| **Use** | `use n/NAME [q/QUANTITY] [u/UNIT]` e.g., `use n/milk q/200 u/g` |
+| **Stock** | `stock [NAME]…` e.g., `stock milk egg` |
+| **List** | `list` |
+| **View** | `view UUID` e.g., `view 1` |
+| **AddRecipe** | `addrecipe` `NAME` `(INGREDIENT_NAME) (INGREDIENT_QUANTITY)(INGREDIENT_UNIT)` `steps start` `1. STEP 1` `complete recipe` e.g., `addrecipe` `Bread` `Water 100g` `Flour 1kg` `steps start` `1. Mix Water with Flour` `complete recipe` |
+| **Delete** | `delete UUID` e.g., `delete 1` |
+| **Search** | `search NAME` e.g., `search flour` |
+| **Modify** | `modify i/UUID n/NAME q/QUANTITY u/UNIT` e.g., `modify i/1 n/Flour q/100 u/g` |
+| **Help** | `help` |
+| **Exit** | `exit` |
diff --git a/docs/_config.yml b/docs/_config.yml
deleted file mode 100644
index 6bd245d8f4e..00000000000
--- a/docs/_config.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-title: "AB-3"
-theme: minima
-
-header_pages:
- - UserGuide.md
- - DeveloperGuide.md
- - AboutUs.md
-
-markdown: kramdown
-
-repository: "se-edu/addressbook-level3"
-github_icon: "images/github-icon.png"
-
-plugins:
- - jemoji
diff --git a/docs/_data/projects.yml b/docs/_data/projects.yml
deleted file mode 100644
index 8f3e50cb601..00000000000
--- a/docs/_data/projects.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-- name: "AB-1"
- url: https://se-edu.github.io/addressbook-level1
-
-- name: "AB-2"
- url: https://se-edu.github.io/addressbook-level2
-
-- name: "AB-3"
- url: https://se-edu.github.io/addressbook-level3
-
-- name: "AB-4"
- url: https://se-edu.github.io/addressbook-level4
-
-- name: "Duke"
- url: https://se-edu.github.io/duke
-
-- name: "Collate"
- url: https://se-edu.github.io/collate
-
-- name: "Book"
- url: https://se-edu.github.io/se-book
-
-- name: "Resources"
- url: https://se-edu.github.io/resources
diff --git a/docs/_includes/custom-head.html b/docs/_includes/custom-head.html
deleted file mode 100644
index 8559a67ffad..00000000000
--- a/docs/_includes/custom-head.html
+++ /dev/null
@@ -1,6 +0,0 @@
-{% comment %}
- Placeholder to allow defining custom head, in principle, you can add anything here, e.g. favicons:
-
- 1. Head over to https://realfavicongenerator.net/ to add your own favicons.
- 2. Customize default _includes/custom-head.html in your source directory and insert the given code snippet.
-{% endcomment %}
diff --git a/docs/_includes/head.html b/docs/_includes/head.html
deleted file mode 100644
index 83ac5326933..00000000000
--- a/docs/_includes/head.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
- {%- include custom-head.html -%}
-
- {{page.title}}
-
-
diff --git a/docs/_includes/header.html b/docs/_includes/header.html
deleted file mode 100644
index 33badcd4f99..00000000000
--- a/docs/_includes/header.html
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
+
-:information_source: Don’t forget to update `AddressBookParser` to use our new `RemarkCommandParser`!
+Don’t forget to update `AddressBookParser` to use our new `RemarkCommandParser`!
-
+
If you are stuck, check out the sample
-[here](https://github.com/se-edu/addressbook-level3/commit/dc6d5139d08f6403da0ec624ea32bd79a2ae0cbf#diff-8bf239e8e9529369b577701303ddd96af93178b4ed6735f91c2d8488b20c6b4a).
+[here](https://github.com/se-edu/inventory-level3/commit/dc6d5139d08f6403da0ec624ea32bd79a2ae0cbf#diff-8bf239e8e9529369b577701303ddd96af93178b4ed6735f91c2d8488b20c6b4a).
## Add `Remark` to the model
-Now that we have all the information that we need, let’s lay the groundwork for propagating the remarks added into the in-memory storage of person data. We achieve that by working with the `Person` model. Each field in a Person is implemented as a separate class (e.g. a `Name` object represents the person’s name). That means we should add a `Remark` class so that we can use a `Remark` object to represent a remark given to a person.
+Now that we have all the information that we need, let’s lay the groundwork for propagating the remarks added into the in-memory storage of ingredient data. We achieve that by working with the `Ingredient` model. Each field in a Ingredient is implemented as a separate class (e.g. a `Name` object represents the ingredient’s name). That means we should add a `Remark` class so that we can use a `Remark` object to represent a remark given to a ingredient.
### Add a new `Remark` class
-Create a new `Remark` in `seedu.address.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code.
+Create a new `Remark` in `seedu.address.model.ingredient`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code.
-A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-41bb13c581e280c686198251ad6cc337cd5e27032772f06ed9bf7f1440995ece). Note how `Remark` has no constrains and thus does not require input
+A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/inventory-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-41bb13c581e280c686198251ad6cc337cd5e27032772f06ed9bf7f1440995ece). Note how `Remark` has no constrains and thus does not require input
validation.
### Make use of `Remark`
@@ -238,13 +241,13 @@ Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark`
## Add a placeholder element for remark to the UI
-Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person.
+Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each ingredient.
-Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688).
+Simply add the following to [`seedu.address.ui.IngredientCard`](https://github.com/se-edu/inventory-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688).
-**`PersonCard.java`:**
+**`IngredientCard.java`:**
-``` java
+```java
@FXML
private Label remark;
```
@@ -252,9 +255,9 @@ private Label remark;
`@FXML` is an annotation that marks a private or protected field and makes it accessible to FXML. It might sound like Greek to you right now, don’t worry — we will get back to it later.
-Then insert the following into [`main/resources/view/PersonListCard.fxml`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-d44c4f51c24f6253c277a2bb9bc440b8064d9c15ad7cb7ceda280bca032efce9).
+Then insert the following into [`main/resources/view/IngredientListCard.fxml`](https://github.com/se-edu/inventory-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-d44c4f51c24f6253c277a2bb9bc440b8064d9c15ad7cb7ceda280bca032efce9).
-**`PersonListCard.fxml`:**
+**`IngredientListCard.fxml`:**
``` xml
@@ -264,54 +267,54 @@ That’s it! Fire up the application again and you should see something like thi
![$remark shows up in each entry](../images/add-remark/$Remark.png)
-## Modify `Person` to support a `Remark` field
+## Modify `Ingredient` to support a `Remark` field
-Since `PersonCard` displays data from a `Person`, we need to update `Person` to get our `Remark` displayed!
+Since `IngredientCard` displays data from a `Ingredient`, we need to update `Ingredient` to get our `Remark` displayed!
-### Modify `Person`
+### Modify `Ingredient`
-We change the constructor of `Person` to take a `Remark`. We will also need to define new fields and accessors accordingly to store our new addition.
+We change the constructor of `Ingredient` to take a `Remark`. We will also need to define new fields and accessors accordingly to store our new addition.
-### Update other usages of `Person`
+### Update other usages of `Ingredient`
-Unfortunately, a change to `Person` will cause other commands to break, you will have to modify these commands to use the updated `Person`!
+Unfortunately, a change to `Ingredient` will cause other commands to break, you will have to modify these commands to use the updated `Ingredient`!
-
+
-:bulb: Use the `Find Usages` feature in IntelliJ IDEA on the `Person` class to find these commands.
+Use the `Find Usages` feature in IntelliJ IDEA on the `Ingredient` class to find these commands.
-
+
-Refer to [this commit](https://github.com/se-edu/addressbook-level3/commit/ce998c37e65b92d35c91d28c7822cd139c2c0a5c) and check that you have got everything in order!
+Refer to [this commit](https://github.com/se-edu/inventory-level3/commit/ce998c37e65b92d35c91d28c7822cd139c2c0a5c) and check that you have got everything in order!
## Updating Storage
-AddressBook stores data by serializing `JsonAdaptedPerson` into `json` with the help of an external library — Jackson. Let’s update `JsonAdaptedPerson` to work with our new `Person`!
+AddressBook stores data by serializing `JsonAdaptedIngredient` into `json` with the help of an external library — Jackson. Let’s update `JsonAdaptedIngredient` to work with our new `Ingredient`!
While the changes to code may be minimal, the test data will have to be updated as well.
-
+
-:exclamation: You must delete AddressBook’s storage file located at `/data/addressbook.json` before running it! Not doing so will cause AddressBook to default to an empty address book!
+You must delete AddressBook’s storage file located at `/data/inventory.json` before running it! Not doing so will cause AddressBook to default to an empty address book!
-
+
-Check out [this commit](https://github.com/se-edu/addressbook-level3/commit/556cbd0e03ff224d7a68afba171ad2eb0ce56bbf)
+Check out [this commit](https://github.com/se-edu/inventory-level3/commit/556cbd0e03ff224d7a68afba171ad2eb0ce56bbf)
to see what the changes entail.
## Finalizing the UI
-Now that we have finalized the `Person` class and its dependencies, we can now bind the `Remark` field to the UI.
+Now that we have finalized the `Ingredient` class and its dependencies, we can now bind the `Remark` field to the UI.
-Just add [this one line of code!](https://github.com/se-edu/addressbook-level3/commit/5b98fee11b6b3f5749b6b943c4f3bd3aa049b692)
+Just add [this one line of code!](https://github.com/se-edu/inventory-level3/commit/5b98fee11b6b3f5749b6b943c4f3bd3aa049b692)
-**`PersonCard.java`:**
+**`IngredientCard.java`:**
-``` java
-public PersonCard(Person person, int displayedIndex) {
+```java
+public IngredientCard(Ingredient ingredient, int displayedIndex) {
//...
- remark.setText(person.getRemark().value);
+ remark.setText(ingredient.getRemark().value);
}
```
@@ -323,43 +326,43 @@ After the previous step, we notice a peculiar regression — we went from di
### Update `RemarkCommand` and `RemarkCommandParser`
-In this last step, we modify `RemarkCommand#execute()` to change the `Remark` of a `Person`. Since all fields in a `Person` are immutable, we create a new instance of a `Person` with the values that we want and
-save it with `Model#setPerson()`.
+In this last step, we modify `RemarkCommand#execute()` to change the `Remark` of a `Ingredient`. Since all fields in a `Ingredient` are immutable, we create a new instance of a `Ingredient` with the values that we want and
+save it with `Model#setIngredient()`.
**`RemarkCommand.java`:**
-``` java
+```java
//...
- public static final String MESSAGE_ADD_REMARK_SUCCESS = "Added remark to Person: %1$s";
- public static final String MESSAGE_DELETE_REMARK_SUCCESS = "Removed remark from Person: %1$s";
+ public static final String MESSAGE_ADD_REMARK_SUCCESS = "Added remark to Ingredient: %1$s";
+ public static final String MESSAGE_DELETE_REMARK_SUCCESS = "Removed remark from Ingredient: %1$s";
//...
@Override
public CommandResult execute(Model model) throws CommandException {
- List lastShownList = model.getFilteredPersonList();
+ List lastShownList = model.getFilteredIngredientList();
if (index.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ throw new CommandException(Messages.MESSAGE_INVALID_INGREDIENT_DISPLAYED_INDEX);
}
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = new Person(
- personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(),
- personToEdit.getAddress(), remark, personToEdit.getTags());
+ Ingredient ingredientToEdit = lastShownList.get(index.getZeroBased());
+ Ingredient editedIngredient = new Ingredient(
+ ingredientToEdit.getName(), ingredientToEdit.getPhone(), ingredientToEdit.getEmail(),
+ ingredientToEdit.getAddress(), remark, ingredientToEdit.getTags());
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ model.setIngredient(ingredientToEdit, editedIngredient);
+ model.updateFilteredIngredientList(PREDICATE_SHOW_ALL_INGREDIENTS);
- return new CommandResult(generateSuccessMessage(editedPerson));
+ return new CommandResult(generateSuccessMessage(editedIngredient));
}
/**
* Generates a command execution success message based on whether
* the remark is added to or removed from
- * {@code personToEdit}.
+ * {@code ingredientToEdit}.
*/
- private String generateSuccessMessage(Person personToEdit) {
+ private String generateSuccessMessage(Ingredient ingredientToEdit) {
String message = !remark.value.isEmpty() ? MESSAGE_ADD_REMARK_SUCCESS : MESSAGE_DELETE_REMARK_SUCCESS;
- return String.format(message, personToEdit);
+ return String.format(message, ingredientToEdit);
}
```
@@ -371,7 +374,7 @@ Tests are crucial to ensuring that bugs don’t slip into the codebase unnoticed
Let’s verify the correctness of our code by writing some tests!
-Of course you can simply add the test cases manually, like you've been doing all along this tutorial. The result would be like the test cases in [here](https://github.com/se-edu/addressbook-level3/commit/fac8f3fd855d55831ca0cc73313b5943d49d4d6e#diff-ff58f7c10338b34f76645df49b71ecb2bafaf7611b20e7ff59ebc98475538a01). Alternatively, you can get the help of IntelliJ to generate the skeletons of the test cases, as explained in the next section.
+Of course you can simply add the test cases manually, like you've been doing all along this tutorial. The result would be like the test cases in [here](https://github.com/se-edu/inventory-level3/commit/fac8f3fd855d55831ca0cc73313b5943d49d4d6e#diff-ff58f7c10338b34f76645df49b71ecb2bafaf7611b20e7ff59ebc98475538a01). Alternatively, you can get the help of IntelliJ to generate the skeletons of the test cases, as explained in the next section.
### Automatically generating tests
@@ -392,7 +395,7 @@ Following convention, let’s change the name of the generated method to `execut
Let’s use the utility functions provided in `CommandTestUtil`. The functions ensure that commands produce the expected `CommandResult` and output the correct message. In this case, `CommandTestUtil#assertCommandSuccess` is the best fit as we are testing that a `RemarkCommand` will successfully add a `Remark`.
-You should end up with a test that looks something like [this](https://github.com/se-edu/addressbook-level3/commit/fac8f3fd855d55831ca0cc73313b5943d49d4d6e#diff-ff58f7c10338b34f76645df49b71ecb2bafaf7611b20e7ff59ebc98475538a01R36-R49).
+You should end up with a test that looks something like [this](https://github.com/se-edu/inventory-level3/commit/fac8f3fd855d55831ca0cc73313b5943d49d4d6e#diff-ff58f7c10338b34f76645df49b71ecb2bafaf7611b20e7ff59ebc98475538a01R36-R49).
## Conclusion
diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md
index f29169bc924..e5b77acd12a 100644
--- a/docs/tutorials/RemovingFields.md
+++ b/docs/tutorials/RemovingFields.md
@@ -1,26 +1,29 @@
---
-layout: page
-title: "Tutorial: Removing Fields"
+ layout: default.md
+ title: "Tutorial: Removing Fields"
+ pageNav: 3
---
+# Tutorial: Removing Fields
+
> Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
>
> — Antoine de Saint-Exupery
When working on an existing code base, you will most likely find that some features that are no longer necessary.
-This tutorial aims to give you some practice on such a code 'removal' activity by removing the `address` field from `Person` class.
+This tutorial aims to give you some practice on such a code 'removal' activity by removing the `address` field from `Ingredient` class.
-
+
**If you have done the [Add `remark` command tutorial](AddRemark.html) already**, you should know where the code had to be updated to add the field `remark`. From that experience, you can deduce where the code needs to be changed to _remove_ that field too. The removing of the `address` field can be done similarly.
However, if you have no such prior knowledge, removing a field can take a quite a bit of detective work. This tutorial takes you through that process. **At least have a read even if you don't actually do the steps yourself.**
-
+
-* Table of Contents
-{:toc}
+
+
## Safely deleting `Address`
@@ -28,7 +31,7 @@ IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a re
### Assisted refactoring
-The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu.
+The `address` field in `Ingredient` is actually an instance of the `seedu.address.model.ingredient.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu.
* :bulb: To make things simpler, you can unselect the options `Search in comments and strings` and `Search for text occurrences`
![Usages detected](../images/remove/UnsafeDelete.png)
@@ -37,11 +40,11 @@ Choose to `View Usages` and you should be presented with a list of `Safe Delete
![List of conflicts](../images/remove/SafeDeleteConflicts.png)
-Remove usages of `Address` by performing `Safe Delete`s on each entry i.e., double-click on the entry (which takes you to the code in concern, right-click on that entity, and choose `Refactor` -> `Safe delete` as before). You will need to exercise discretion when removing usages of `Address`. Functions like `ParserUtil#parseAddress()` can be safely removed but its usages must be removed as well. Other usages like in `EditPersonDescriptor` may require more careful inspection.
+Remove usages of `Address` by performing `Safe Delete`s on each entry i.e., double-click on the entry (which takes you to the code in concern, right-click on that entity, and choose `Refactor` -> `Safe delete` as before). You will need to exercise discretion when removing usages of `Address`. Functions like `ParserUtil#parseAddress()` can be safely removed but its usages must be removed as well. Other usages like in `EditIngredientDescriptor` may require more careful inspection.
-Let’s try removing references to `Address` in `EditPersonDescriptor`.
+Let’s try removing references to `Address` in `EditIngredientDescriptor`.
-1. Safe delete the field `address` in `EditPersonDescriptor`.
+1. Safe delete the field `address` in `EditIngredientDescriptor`.
1. Select `Yes` when prompted to remove getters and setters.
@@ -50,10 +53,10 @@ Let’s try removing references to `Address` in `EditPersonDescriptor`.
1. Remove the usages of `address` and select `Do refactor` when you are done.
-
+
- :bulb: **Tip:** Removing usages may result in errors. Exercise discretion and fix them. For example, removing the `address` field from the `Person` class will require you to modify its constructor.
-
+ **Tip:** Removing usages may result in errors. Exercise discretion and fix them. For example, removing the `address` field from the `Ingredient` class will require you to modify its constructor.
+
1. Repeat the steps for the remaining usages of `Address`
@@ -63,22 +66,22 @@ After you are done, verify that the application still works by compiling and run
Unfortunately, there are usages of `Address` that IntelliJ IDEA cannot identify. You can find them by searching for instances of the word `address` in your code (`Edit` \> `Find` \> `Find in path`).
-Places of interest to look out for would be resources used by the application. `main/resources` contains images and `fxml` files used by the application and `test/resources` contains test data. For example, there is a `$address` in each `PersonCard` that has not been removed nor identified.
+Places of interest to look out for would be resources used by the application. `main/resources` contains images and `fxml` files used by the application and `test/resources` contains test data. For example, there is a `$address` in each `IngredientCard` that has not been removed nor identified.
![$address](../images/remove/$address.png)
-A quick look at the `PersonCard` class and its `fxml` file quickly reveals why it slipped past the automated refactoring.
+A quick look at the `IngredientCard` class and its `fxml` file quickly reveals why it slipped past the automated refactoring.
-**`PersonCard.java`**
+**`IngredientCard.java`**
-``` java
+```java
...
@FXML
private Label address;
...
```
-**`PersonCard.fxml`**
+**`IngredientCard.fxml`**
``` xml
...
@@ -96,12 +99,12 @@ At this point, your application is working as intended and all your tests are pa
In `src/test/data/`, data meant for testing purposes are stored. While keeping the `address` field in the json files does not cause the tests to fail, it is not good practice to let cruft from old features accumulate.
-**`invalidPersonAddressBook.json`:**
+**`invalidIngredientAddressBook.json`:**
```json
{
- "persons": [ {
- "name": "Person with invalid name field: Ha!ns Mu@ster",
+ "ingredients": [ {
+ "name": "Ingredient with invalid name field: Ha!ns Mu@ster",
"phone": "9482424",
"email": "hans@example.com",
"address": "4th street"
diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md
index 4fb62a83ef6..dcd642b3da4 100644
--- a/docs/tutorials/TracingCode.md
+++ b/docs/tutorials/TracingCode.md
@@ -1,31 +1,35 @@
---
-layout: page
-title: "Tutorial: Tracing code"
+ layout: default.md
+ title: "Tutorial: Tracing code"
+ pageNav: 3
---
+# Tutorial: Tracing code
+
+
> Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. …\[Therefore,\] making it easy to read makes it easier to write.
>
> — Robert C. Martin Clean Code: A Handbook of Agile Software Craftsmanship
When trying to understand an unfamiliar code base, one common strategy used is to trace some representative execution path through the code base. One easy way to trace an execution path is to use a debugger to step through the code. In this tutorial, you will be using the IntelliJ IDEA’s debugger to trace the execution path of a specific user command.
-* Table of Contents
-{:toc}
+
+
## Before we start
Before we jump into the code, it is useful to get an idea of the overall structure and the high-level behavior of the application. This is provided in the 'Architecture' section of the developer guide. In particular, the architecture diagram (reproduced below), tells us that the App consists of several components.
-![ArchitectureDiagram](../images/ArchitectureDiagram.png)
+
It also has a sequence diagram (reproduced below) that tells us how a command propagates through the App.
-
+
Note how the diagram shows only the execution flows _between_ the main components. That is, it does not show details of the execution path *inside* each component. By hiding those details, the diagram aims to inform the reader about the overall execution path of a command without overwhelming the reader with too much details. In this tutorial, you aim to find those omitted details so that you get a more in-depth understanding of how the code works.
Before we proceed, ensure that you have done the following:
-1. Read the [*Architecture* section of the DG](../DeveloperGuide.md#architecture)
+1. Read the [*Architecture* section of the DG](../DeveloperGuide (Pristine).md#architecture)
1. Set up the project in Intellij IDEA
1. Learn basic debugging features of Intellij IDEA
* If you are using a different IDE, we'll leave it to you to figure out the equivalent feature to use in your IDE.
@@ -37,16 +41,16 @@ As you know, the first step of debugging is to put in a breakpoint where you wan
In our case, we would want to begin the tracing at the very point where the App start processing user input (i.e., somewhere in the UI component), and then trace through how the execution proceeds through the UI component. However, the execution path through a GUI is often somewhat obscure due to various *event-driven mechanisms* used by GUI frameworks, which happens to be the case here too. Therefore, let us put the breakpoint where the `UI` transfers control to the `Logic` component.
-
+
According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `seedu.address.logic.Logic`.
-
+
-:bulb: **Intellij Tip:** The ['**Search Everywhere**' feature](https://www.jetbrains.com/help/idea/searching-everywhere.html) can be used here. In particular, the '**Find Symbol**' ('Symbol' here refers to methods, variables, classes etc.) variant of that feature is quite useful here as we are looking for a _method_ named `execute`, not simply the text `execute`.
-
+**Intellij Tip:** The ['**Search Everywhere**' feature](https://www.jetbrains.com/help/idea/searching-everywhere.html) can be used here. In particular, the '**Find Symbol**' ('Symbol' here refers to methods, variables, classes etc.) variant of that feature is quite useful here as we are looking for a _method_ named `execute`, not simply the text `execute`.
+
A quick look at the `seedu.address.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for.
@@ -67,14 +71,14 @@ public interface Logic {
But apparently, this is an interface, not a concrete implementation.
That should be fine because the [Architecture section of the Developer Guide](../DeveloperGuide.html#architecture) tells us that components interact through interfaces. Here's the relevant diagram:
-
+
Next, let's find out which statement(s) in the `UI` code is calling this method, thus transferring control from the `UI` to the `Logic`.
-
+
-:bulb: **Intellij Tip:** The ['**Find Usages**' feature](https://www.jetbrains.com/help/idea/find-highlight-usages.html#find-usages) can find from which parts of the code a class/method/variable is being used.
-
+**Intellij Tip:** The ['**Find Usages**' feature](https://www.jetbrains.com/help/idea/find-highlight-usages.html#find-usages) can find from which parts of the code a class/method/variable is being used.
+
![`Find Usages` tool window. `Edit` \> `Find` \> `Find Usages`.](../images/tracing/FindUsages.png)
@@ -87,10 +91,10 @@ Now let’s set the breakpoint. First, double-click the item to reach the corres
Recall from the User Guide that the `edit` command has the format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…` For this tutorial we will be issuing the command `edit 1 n/Alice Yeoh`.
-
+
-:bulb: **Tip:** Over the course of the debugging session, you will encounter every major component in the application. Try to keep track of what happens inside the component and where the execution transfers to another component.
-
+**Tip:** Over the course of the debugging session, you will encounter every major component in the application. Try to keep track of what happens inside the component and where the execution transfers to another component.
+
1. To start the debugging session, simply `Run` \> `Debug Main`
@@ -110,7 +114,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
**LogicManager\#execute().**
- ``` java
+ ```java
@Override
public CommandResult execute(String commandText)
throws CommandException, ParseException {
@@ -142,7 +146,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
![StepOver](../images/tracing/StepOver.png)
1. _Step into_ the line where user input in parsed from a String to a Command, which should bring you to the `AddressBookParser#parseCommand()` method (partial code given below):
- ``` java
+ ```java
public Command parseCommand(String userInput) throws ParseException {
...
final String commandWord = matcher.group("commandWord");
@@ -157,7 +161,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
1. Stepping through the `switch` block, we end up at a call to `EditCommandParser().parse()` as expected (because the command we typed is an edit command).
- ``` java
+ ```java
...
case EditCommand.COMMAND_WORD:
return new EditCommandParser().parse(arguments);
@@ -166,64 +170,69 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
1. Let’s see what `EditCommandParser#parse()` does by stepping into it. You might have to click the 'step into' button multiple times here because there are two method calls in that statement: `EditCommandParser()` and `parse()`.
-
:bulb: **Intellij Tip:** Sometimes, you might end up stepping into functions that are not of interest. Simply use the `step out` button to get out of them!
-
+
+
+ **Intellij Tip:** Sometimes, you might end up stepping into functions that are not of interest. Simply use the `step out` button to get out of them!
+
1. Stepping through the method shows that it calls `ArgumentTokenizer#tokenize()` and `ParserUtil#parseIndex()` to obtain the arguments and index required.
-1. The rest of the method seems to exhaustively check for the existence of each possible parameter of the `edit` command and store any possible changes in an `EditPersonDescriptor`. Recall that we can verify the contents of `editPersonDesciptor` through the 'Variables' window.
+1. The rest of the method seems to exhaustively check for the existence of each possible parameter of the `edit` command and store any possible changes in an `EditIngredientDescriptor`. Recall that we can verify the contents of `editIngredientDesciptor` through the 'Variables' window.
![EditCommand](../images/tracing/EditCommand.png)
1. As you just traced through some code involved in parsing a command, you can take a look at this class diagram to see where the various parsing-related classes you encountered fit into the design of the `Logic` component.
-
+
1. Let’s continue stepping through until we return to `LogicManager#execute()`.
The sequence diagram below shows the details of the execution path through the Logic component. Does the execution path you traced in the code so far match the diagram?
- ![Tracing an `edit` command through the Logic component](../images/tracing/LogicSequenceDiagram.png)
+
1. Now, step over until you read the statement that calls the `execute()` method of the `EditCommand` object received, and step into that `execute()` method (partial code given below):
**`EditCommand#execute()`:**
- ``` java
+ ```java
@Override
public CommandResult execute(Model model) throws CommandException {
...
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
- if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
+ Ingredient ingredientToEdit = lastShownList.get(index.getZeroBased());
+ Ingredient editedIngredient = createEditedIngredient(ingredientToEdit, editIngredientDescriptor);
+ if (!ingredientToEdit.isSameIngredient(editedIngredient) && model.hasIngredient(editedIngredient)) {
+ throw new CommandException(MESSAGE_DUPLICATE_INGREDIENT);
}
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson));
+ model.setIngredient(ingredientToEdit, editedIngredient);
+ model.updateFilteredIngredientList(PREDICATE_SHOW_ALL_INGREDIENTS);
+ return new CommandResult(String.format(MESSAGE_EDIT_INGREDIENT_SUCCESS, editedIngredient));
}
```
1. As suspected, `command#execute()` does indeed make changes to the `model` object. Specifically,
- * it uses the `setPerson()` method (defined in the interface `Model` and implemented in `ModelManager` as per the usual pattern) to update the person data.
- * it uses the `updateFilteredPersonList` method to ask the `Model` to populate the 'filtered list' with _all_ persons.
- FYI, The 'filtered list' is the list of persons resulting from the most recent operation that will be shown to the user immediately after. For the `edit` command, we populate it with all the persons so that the user can see the edited person along with all other persons. If this was a `find` command, we would be setting that list to contain the search results instead.
- To provide some context, given below is the class diagram of the `Model` component. See if you can figure out where the 'filtered list' of persons is being tracked.
-
+ * it uses the `setIngredient()` method (defined in the interface `Model` and implemented in `ModelManager` as per the usual pattern) to update the ingredient data.
+ * it uses the `updateFilteredIngredientList` method to ask the `Model` to populate the 'filtered list' with _all_ ingredients.
+ FYI, The 'filtered list' is the list of ingredients resulting from the most recent operation that will be shown to the user immediately after. For the `edit` command, we populate it with all the ingredients so that the user can see the edited ingredient along with all other ingredients. If this was a `find` command, we would be setting that list to contain the search results instead.
+ To provide some context, given below is the class diagram of the `Model` component. See if you can figure out where the 'filtered list' of ingredients is being tracked.
+
* :bulb: This may be a good time to read through the [`Model` component section of the DG](../DeveloperGuide.html#model-component)
1. As you step through the rest of the statements in the `EditCommand#execute()` method, you'll see that it creates a `CommandResult` object (containing information about the result of the execution) and returns it.
Advancing the debugger by one more step should take you back to the middle of the `LogicManager#execute()` method.
1. Given that you have already seen quite a few classes in the `Logic` component in action, see if you can identify in this partial class diagram some of the classes you've encountered so far, and see how they fit into the class structure of the `Logic` component:
-
+
+
* :bulb: This may be a good time to read through the [`Logic` component section of the DG](../DeveloperGuide.html#logic-component)
1. Similar to before, you can step over/into statements in the `LogicManager#execute()` method to examine how the control is transferred to the `Storage` component and what happens inside that component.
-
:bulb: **Intellij Tip:** When trying to step into a statement such as `storage.saveAddressBook(model.getAddressBook())` which contains multiple method calls, Intellij will let you choose (by clicking) which one you want to step into.
-
+
+
+ **Intellij Tip:** When trying to step into a statement such as `storage.saveAddressBook(model.getAddressBook())` which contains multiple method calls, Intellij will let you choose (by clicking) which one you want to step into.
+
-1. As you step through the code inside the `Storage` component, you will eventually arrive at the `JsonAddressBook#saveAddressBook()` method which calls the `JsonSerializableAddressBook` constructor, to create an object that can be _serialized_ (i.e., stored in storage medium) in JSON format. That constructor is given below (with added line breaks for easier readability):
+1. As you step through the code inside the `Storage` component, you will eventually arrive at the `JsonAddressBook#saveAddressBook()` method which calls the `JsonSerializableAddressBook` constructor, to create an object that can be _serialized_ (i.e., stored in storage medium) in JSON format. That constructor is given below (with added line breaks for easier readability):
**`JsonSerializableAddressBook` constructor:**
- ``` java
+ ```java
/**
* Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use.
*
@@ -231,19 +240,20 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
* {@code JsonSerializableAddressBook}.
*/
public JsonSerializableAddressBook(ReadOnlyAddressBook source) {
- persons.addAll(
- source.getPersonList()
+ ingredients.addAll(
+ source.getIngredientList()
.stream()
- .map(JsonAdaptedPerson::new)
+ .map(JsonAdaptedIngredient::new)
.collect(Collectors.toList()));
}
```
-1. It appears that a `JsonAdaptedPerson` is created for each `Person` and then added to the `JsonSerializableAddressBook`.
+1. It appears that a `JsonAdaptedIngredient` is created for each `Ingredient` and then added to the `JsonSerializableAddressBook`.
This is because regular Java objects need to go through an _adaptation_ for them to be suitable to be saved in JSON format.
1. While you are stepping through the classes in the `Storage` component, here is the component's class diagram to help you understand how those classes fit into the structure of the component.
-
+
+
* :bulb: This may be a good time to read through the [`Storage` component section of the DG](../DeveloperGuide.html#storage-component)
1. We can continue to step through until you reach the end of the `LogicManager#execute()` method and return to the `MainWindow#executeCommand()` method (the place where we put the original breakpoint).
@@ -251,7 +261,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
1. Stepping into `resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser());`, we end up in:
**`ResultDisplay#setFeedbackToUser()`**
- ``` java
+ ```java
public void setFeedbackToUser(String feedbackToUser) {
requireNonNull(feedbackToUser);
resultDisplay.setText(feedbackToUser);
@@ -296,6 +306,6 @@ Here are some quick questions you can try to answer based on your execution path
4. Add a new command
- 5. Add a new field to `Person`
+ 5. Add a new field to `Ingredient`
6. Add a new entity to the address book
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java
index 3d6bd06d5af..46778fb815a 100644
--- a/src/main/java/seedu/address/MainApp.java
+++ b/src/main/java/seedu/address/MainApp.java
@@ -15,16 +15,20 @@
import seedu.address.commons.util.StringUtil;
import seedu.address.logic.Logic;
import seedu.address.logic.LogicManager;
-import seedu.address.model.AddressBook;
+import seedu.address.model.Inventory;
import seedu.address.model.Model;
import seedu.address.model.ModelManager;
-import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyInventory;
+import seedu.address.model.ReadOnlyRecipeBook;
import seedu.address.model.ReadOnlyUserPrefs;
+import seedu.address.model.RecipeBook;
import seedu.address.model.UserPrefs;
import seedu.address.model.util.SampleDataUtil;
-import seedu.address.storage.AddressBookStorage;
-import seedu.address.storage.JsonAddressBookStorage;
+import seedu.address.storage.InventoryStorage;
+import seedu.address.storage.JsonInventoryStorage;
+import seedu.address.storage.JsonRecipeBookStorage;
import seedu.address.storage.JsonUserPrefsStorage;
+import seedu.address.storage.RecipeBookStorage;
import seedu.address.storage.Storage;
import seedu.address.storage.StorageManager;
import seedu.address.storage.UserPrefsStorage;
@@ -36,7 +40,7 @@
*/
public class MainApp extends Application {
- public static final Version VERSION = new Version(0, 2, 2, true);
+ public static final Version VERSION = new Version(1, 3, 0, true);
private static final Logger logger = LogsCenter.getLogger(MainApp.class);
@@ -48,7 +52,7 @@ public class MainApp extends Application {
@Override
public void init() throws Exception {
- logger.info("=============================[ Initializing AddressBook ]===========================");
+ logger.info("=============================[ Initializing Inventory App ]===========================");
super.init();
AppParameters appParameters = AppParameters.parse(getParameters());
@@ -57,8 +61,9 @@ public void init() throws Exception {
UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath());
UserPrefs userPrefs = initPrefs(userPrefsStorage);
- AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath());
- storage = new StorageManager(addressBookStorage, userPrefsStorage);
+ InventoryStorage inventoryStorage = new JsonInventoryStorage(userPrefs.getInventoryFilePath());
+ RecipeBookStorage recipeBookStorage = new JsonRecipeBookStorage(userPrefs.getRecipeBookFilePath());
+ storage = new StorageManager(inventoryStorage, userPrefsStorage, recipeBookStorage);
model = initModelManager(storage, userPrefs);
@@ -68,29 +73,44 @@ public void init() throws Exception {
}
/**
- * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book is not found,
- * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book.
+ * Returns a {@code ModelManager} with the data from {@code storage}'s inventory and {@code userPrefs}.
+ * The data from the sample inventory will be used instead if {@code storage}'s inventory is not found,
+ * or an empty inventory will be used instead if errors occur when reading {@code storage}'s inventory.
*/
private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) {
- logger.info("Using data file : " + storage.getAddressBookFilePath());
+ logger.info("Using data file : " + storage.getInventoryFilePath());
- Optional addressBookOptional;
- ReadOnlyAddressBook initialData;
+ Optional inventoryOptional;
+ ReadOnlyInventory initialData;
+ Optional recipeBookOptional;
+ ReadOnlyRecipeBook initialRecipeData;
try {
- addressBookOptional = storage.readAddressBook();
- if (!addressBookOptional.isPresent()) {
- logger.info("Creating a new data file " + storage.getAddressBookFilePath()
- + " populated with a sample AddressBook.");
+ inventoryOptional = storage.readInventory();
+ if (!inventoryOptional.isPresent()) {
+ logger.info("Creating a new data file " + storage.getInventoryFilePath()
+ + " populated with a sample Inventory.");
}
- initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook);
+ initialData = inventoryOptional.orElseGet(SampleDataUtil::getSampleInventory);
+
+ } catch (DataLoadingException e) {
+ logger.warning("Data file at " + storage.getInventoryFilePath() + " could not be loaded."
+ + " Will be starting with an empty Inventory.");
+ initialData = new Inventory();
+ }
+
+ try {
+ recipeBookOptional = storage.readRecipeBook();
+ if (!recipeBookOptional.isPresent()) {
+ logger.info("Creating an empty recipe data file " + storage.getRecipeBookFilePath());
+ }
+ initialRecipeData = recipeBookOptional.orElseGet(SampleDataUtil::getSampleRecipeBook);
} catch (DataLoadingException e) {
- logger.warning("Data file at " + storage.getAddressBookFilePath() + " could not be loaded."
- + " Will be starting with an empty AddressBook.");
- initialData = new AddressBook();
+ logger.warning("Recipe data file at " + storage.getRecipeBookFilePath() + " could not be loaded."
+ + " Will be starting with an empty Recipe Book.");
+ initialRecipeData = new RecipeBook();
}
- return new ModelManager(initialData, userPrefs);
+ return new ModelManager(initialData, userPrefs, initialRecipeData);
}
private void initLogging(Config config) {
@@ -170,13 +190,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
@Override
public void start(Stage primaryStage) {
- logger.info("Starting AddressBook " + MainApp.VERSION);
+ logger.info("Starting Inventory App " + MainApp.VERSION);
ui.start(primaryStage);
}
@Override
public void stop() {
- logger.info("============================ [ Stopping Address Book ] =============================");
+ logger.info("============================ [ Stopping Inventory App ] =============================");
try {
storage.saveUserPrefs(model.getUserPrefs());
} catch (IOException e) {
diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/address/commons/core/GuiSettings.java
index a97a86ee8d7..3ec0c58f9ba 100644
--- a/src/main/java/seedu/address/commons/core/GuiSettings.java
+++ b/src/main/java/seedu/address/commons/core/GuiSettings.java
@@ -13,7 +13,7 @@
public class GuiSettings implements Serializable {
private static final double DEFAULT_HEIGHT = 600;
- private static final double DEFAULT_WIDTH = 740;
+ private static final double DEFAULT_WIDTH = 720;
private final double windowWidth;
private final double windowHeight;
diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/address/commons/core/LogsCenter.java
index 8cf8e15a0f0..dc848bccb5e 100644
--- a/src/main/java/seedu/address/commons/core/LogsCenter.java
+++ b/src/main/java/seedu/address/commons/core/LogsCenter.java
@@ -20,7 +20,7 @@
public class LogsCenter {
private static final int MAX_FILE_COUNT = 5;
private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB
- private static final String LOG_FILE = "addressbook.log";
+ private static final String LOG_FILE = "recipebook.log";
private static final Logger logger; // logger for this class
private static Logger baseLogger; // to be used as the parent of all other loggers created by this class.
private static Level currentLogLevel = Level.INFO;
@@ -75,11 +75,11 @@ private static void removeHandlers(Logger logger) {
}
/**
- * Creates a logger named 'ab3', containing a {@code ConsoleHandler} and a {@code FileHandler}.
+ * Creates a logger named 'recipebook', containing a {@code ConsoleHandler} and a {@code FileHandler}.
* Sets it as the {@code baseLogger}, to be used as the parent logger of all other loggers.
*/
private static void setBaseLogger() {
- baseLogger = Logger.getLogger("ab3");
+ baseLogger = Logger.getLogger("recipebook");
baseLogger.setUseParentHandlers(false);
removeHandlers(baseLogger);
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..0f0eb38d0a3 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -7,8 +7,9 @@
import seedu.address.logic.commands.CommandResult;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
+import seedu.address.model.ReadOnlyInventory;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.recipe.Recipe;
/**
* API of the Logic component
@@ -24,19 +25,19 @@ public interface Logic {
CommandResult execute(String commandText) throws CommandException, ParseException;
/**
- * Returns the AddressBook.
+ * Returns the Inventory.
*
- * @see seedu.address.model.Model#getAddressBook()
+ * @see seedu.address.model.Model#getInventory()
*/
- ReadOnlyAddressBook getAddressBook();
+ ReadOnlyInventory getInventory();
- /** Returns an unmodifiable view of the filtered list of persons */
- ObservableList getFilteredPersonList();
+ /** Returns an unmodifiable view of the filtered list of ingredients */
+ ObservableList getFilteredIngredientList();
/**
- * Returns the user prefs' address book file path.
+ * Returns the user prefs' inventory file path.
*/
- Path getAddressBookFilePath();
+ Path getInventoryFilePath();
/**
* Returns the user prefs' GUI settings.
@@ -47,4 +48,5 @@ public interface Logic {
* Set the user prefs' GUI settings.
*/
void setGuiSettings(GuiSettings guiSettings);
+ ObservableList getRecipeList();
}
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java
index 5aa3b91c7d0..b2c1cb9eedc 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -11,11 +11,12 @@
import seedu.address.logic.commands.Command;
import seedu.address.logic.commands.CommandResult;
import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.logic.parser.AddressBookParser;
+import seedu.address.logic.parser.InventoryAppParser;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.Model;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
+import seedu.address.model.ReadOnlyInventory;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.recipe.Recipe;
import seedu.address.storage.Storage;
/**
@@ -31,7 +32,9 @@ public class LogicManager implements Logic {
private final Model model;
private final Storage storage;
- private final AddressBookParser addressBookParser;
+ private final InventoryAppParser inventoryAppParser;
+
+ private final RecipeAddInputHandler recipeAddInputHandler;
/**
* Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}.
@@ -39,19 +42,31 @@ public class LogicManager implements Logic {
public LogicManager(Model model, Storage storage) {
this.model = model;
this.storage = storage;
- addressBookParser = new AddressBookParser();
+ inventoryAppParser = new InventoryAppParser();
+ recipeAddInputHandler = new RecipeAddInputHandler();
}
@Override
public CommandResult execute(String commandText) throws CommandException, ParseException {
logger.info("----------------[USER COMMAND][" + commandText + "]");
+ Command command;
CommandResult commandResult;
- Command command = addressBookParser.parseCommand(commandText);
- commandResult = command.execute(model);
+ if (!recipeAddInputHandler.check(commandText)) {
+ System.out.println(commandText);
+ command = inventoryAppParser.parseCommand(commandText);
+ commandResult = command.execute(model);
+ } else {
+ commandResult = recipeAddInputHandler.handle(commandText);
+ if (recipeAddInputHandler.isComplete()) {
+ command = recipeAddInputHandler.getCommand();
+ commandResult = command.execute(model);
+ }
+ }
try {
- storage.saveAddressBook(model.getAddressBook());
+ storage.saveInventory(model.getInventory());
+ storage.saveRecipeBook(model.getRecipeBook());
} catch (AccessDeniedException e) {
throw new CommandException(String.format(FILE_OPS_PERMISSION_ERROR_FORMAT, e.getMessage()), e);
} catch (IOException ioe) {
@@ -62,18 +77,18 @@ public CommandResult execute(String commandText) throws CommandException, ParseE
}
@Override
- public ReadOnlyAddressBook getAddressBook() {
- return model.getAddressBook();
+ public ReadOnlyInventory getInventory() {
+ return model.getInventory();
}
@Override
- public ObservableList getFilteredPersonList() {
- return model.getFilteredPersonList();
+ public ObservableList getFilteredIngredientList() {
+ return model.getFilteredIngredientList();
}
@Override
- public Path getAddressBookFilePath() {
- return model.getAddressBookFilePath();
+ public Path getInventoryFilePath() {
+ return model.getInventoryFilePath();
}
@Override
@@ -85,4 +100,9 @@ public GuiSettings getGuiSettings() {
public void setGuiSettings(GuiSettings guiSettings) {
model.setGuiSettings(guiSettings);
}
+
+ @Override
+ public ObservableList getRecipeList() {
+ return model.getFilteredRecipeList();
+ }
}
diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java
index ecd32c31b53..13bd4a894bb 100644
--- a/src/main/java/seedu/address/logic/Messages.java
+++ b/src/main/java/seedu/address/logic/Messages.java
@@ -5,7 +5,8 @@
import java.util.stream.Stream;
import seedu.address.logic.parser.Prefix;
-import seedu.address.model.person.Person;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.recipe.Recipe;
/**
* Container for user visible messages.
@@ -14,10 +15,18 @@ public class Messages {
public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
- public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid";
- public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
+ public static final String MESSAGE_INVALID_RECIPE_DISPLAYED_INDEX = "The recipe UUID provided is invalid";
+ public static final String MESSAGE_INGREDIENTS_LISTED_OVERVIEW = "%1$d ingredients listed!";
public static final String MESSAGE_DUPLICATE_FIELDS =
"Multiple values specified for the following single-valued field(s): ";
+ public static final String MESSAGE_RECIPE_LISTED = "Recipe of uuid %1$s opened";
+
+ public static final String MESSAGE_RECIPE_LISTED_OVERVIEW = "%1$d recipes listed!";
+ public static final String MESSAGE_RECIPE_DOES_NOT_EXIST = "There is no recipe with the recipe UUID provided in "
+ + "the recipe book ";
+ public static final String MESSAGE_MODIFY_RECIPE_SUCCESS = "Recipe of uuid %1$s modified";
+ public static final String MESSAGE_NO_SUCH_INGREDIENT = "There is no such ingredient in this recipe. Please input"
+ + "an ingredient that is in the recipe";
/**
* Returns an error message indicating the duplicate prefixes.
@@ -32,20 +41,24 @@ public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePref
}
/**
- * Formats the {@code person} for display to the user.
+ * Formats the {@code ingredient} for display to the user.
*/
- public static String format(Person person) {
+ public static String format(Ingredient ingredient) {
final StringBuilder builder = new StringBuilder();
- builder.append(person.getName())
- .append("; Phone: ")
- .append(person.getPhone())
- .append("; Email: ")
- .append(person.getEmail())
- .append("; Address: ")
- .append(person.getAddress())
- .append("; Tags: ");
- person.getTags().forEach(builder::append);
+ builder.append(ingredient.getName())
+ .append("; \nQuantity Left: ")
+ .append(ingredient.getQuantity().toString());
+ if (ingredient.getQuantity().getValue() == 0) {
+ builder.append("\nIngredient Removed");
+ }
return builder.toString();
}
+ /**
+ * Formats the {@code recipe} for display to the user.
+ */
+ public static String format(Recipe recipe) {
+ return recipe.getName().fullName;
+ }
+
}
diff --git a/src/main/java/seedu/address/logic/RecipeAddInputHandler.java b/src/main/java/seedu/address/logic/RecipeAddInputHandler.java
new file mode 100644
index 00000000000..e1a6b53fc98
--- /dev/null
+++ b/src/main/java/seedu/address/logic/RecipeAddInputHandler.java
@@ -0,0 +1,102 @@
+package seedu.address.logic;
+
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.parser.RecipeAddCommandParser;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.Name;
+
+/**
+ * Handles input in the recipe adding sequence.
+ */
+public class RecipeAddInputHandler {
+ private static final String MESSAGE_PROMPT_NAME = "Input the name of the recipe.";
+ private static final String MESSAGE_PROMPT_INGREDIENT = "Input an ingredient and it's quantity. Eg. Milk 100g"
+ + "\n" + "When you're done, type \"steps start\"";
+
+ private static final String MESSAGE_PROMPT_STEP = "Input the next step in the recipe. Eg. 1. Preheat the oven."
+ + "\n" + "When you're done, type \"complete recipe\"";
+
+ private final RecipeAddCommandParser recipeAddCommandParser;
+
+ private RecipeAddInputStage stage;
+
+ /**
+ * Initialise the parser and stage
+ */
+ public RecipeAddInputHandler() {
+ recipeAddCommandParser = new RecipeAddCommandParser();
+ stage = RecipeAddInputStage.COMPLETE;
+ }
+
+ /**
+ * Checks if the command is part of the adding recipe command sequence.
+ * @param commandText The command to check
+ */
+ public boolean check(String commandText) {
+ commandText = commandText.trim();
+ return (stage != RecipeAddInputStage.COMPLETE || commandText.equals("addrecipe"));
+ }
+
+ /**
+ * Handles a command in the adding recipe sequence.
+ * @param commandText The next command in the sequence
+ * @return A command result
+ */
+ public CommandResult handle(String commandText) {
+ commandText = commandText.trim();
+ String parseOutput = "";
+ if (commandText.equals("addrecipe")) {
+ recipeAddCommandParser.reset();
+ stage = RecipeAddInputStage.NAME;
+ } else if (stage == RecipeAddInputStage.NAME) {
+ try {
+ parseOutput = recipeAddCommandParser.addName(commandText);
+ stage = RecipeAddInputStage.INGREDIENT;
+ } catch (ParseException pe) {
+ return new CommandResult(parseOutput + "\n"
+ + Name.MESSAGE_CONSTRAINTS + "\n" + MESSAGE_PROMPT_NAME);
+ }
+ } else if (commandText.equals("steps start") || commandText.equals("step start")) {
+ stage = RecipeAddInputStage.STEPS;
+ } else if (commandText.equals("complete recipe")) {
+ stage = RecipeAddInputStage.COMPLETE;
+ } else {
+ switch (stage) {
+ case INGREDIENT:
+ parseOutput = recipeAddCommandParser.addIngredient(commandText);
+ break;
+ case STEPS:
+ parseOutput = recipeAddCommandParser.addStep(commandText);
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch(stage) {
+ case NAME:
+ return new CommandResult(parseOutput + "\n" + MESSAGE_PROMPT_NAME);
+ case INGREDIENT:
+ return new CommandResult(parseOutput + "\n" + MESSAGE_PROMPT_INGREDIENT);
+ case STEPS:
+ return new CommandResult(parseOutput + "\n" + MESSAGE_PROMPT_STEP);
+ default:
+ return new CommandResult(MESSAGE_PROMPT_NAME);
+ }
+ }
+
+ /**
+ * Checks if the adding recipe command sequence is complete
+ */
+ public boolean isComplete() {
+ return stage == RecipeAddInputStage.COMPLETE;
+ }
+
+ /**
+ * Returns the completed RecipeAddCommand instance
+ */
+ public Command getCommand() {
+ return recipeAddCommandParser.generateCommand();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/RecipeAddInputStage.java b/src/main/java/seedu/address/logic/RecipeAddInputStage.java
new file mode 100644
index 00000000000..5589a85a04e
--- /dev/null
+++ b/src/main/java/seedu/address/logic/RecipeAddInputStage.java
@@ -0,0 +1,11 @@
+package seedu.address.logic;
+
+/**
+ * Represents the stage of the add recipe command sequence.
+ */
+public enum RecipeAddInputStage {
+ NAME,
+ INGREDIENT,
+ STEPS,
+ COMPLETE
+}
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java
index 5d7185a9680..578e9901572 100644
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ b/src/main/java/seedu/address/logic/commands/AddCommand.java
@@ -1,63 +1,57 @@
package seedu.address.logic.commands;
import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_UNIT;
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
-import seedu.address.model.person.Person;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.ingredient.exceptions.UnitConversionException;
/**
- * Adds a person to the address book.
+ * Adds an ingredient to the inventory.
*/
public class AddCommand extends Command {
public static final String COMMAND_WORD = "add";
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an ingredient to the recipe book. "
+ "Parameters: "
+ PREFIX_NAME + "NAME "
- + PREFIX_PHONE + "PHONE "
- + PREFIX_EMAIL + "EMAIL "
- + PREFIX_ADDRESS + "ADDRESS "
- + "[" + PREFIX_TAG + "TAG]...\n"
+ + PREFIX_QUANTITY + "QUANTITY "
+ + PREFIX_UNIT + "UNIT "
+ "Example: " + COMMAND_WORD + " "
- + PREFIX_NAME + "John Doe "
- + PREFIX_PHONE + "98765432 "
- + PREFIX_EMAIL + "johnd@example.com "
- + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 "
- + PREFIX_TAG + "friends "
- + PREFIX_TAG + "owesMoney";
+ + PREFIX_NAME + "flour "
+ + PREFIX_QUANTITY + "600 "
+ + PREFIX_UNIT + "g ";
- public static final String MESSAGE_SUCCESS = "New person added: %1$s";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book";
+ public static final String MESSAGE_SUCCESS = "New ingredient added: %1$s";
+ public static final String MESSAGE_DUPLICATE_INGREDIENT = "This ingredient already exists in the inventory";
- private final Person toAdd;
+ final Ingredient toAdd;
/**
- * Creates an AddCommand to add the specified {@code Person}
+ * Creates an AddCommand to add the specified {@code Ingredient}
*/
- public AddCommand(Person person) {
- requireNonNull(person);
- toAdd = person;
+ public AddCommand(Ingredient ingredient) {
+ requireNonNull(ingredient);
+ toAdd = ingredient;
}
@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
-
- if (model.hasPerson(toAdd)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
+ try {
+ model.addIngredient(toAdd);
+ } catch (UnitConversionException uce) {
+ throw new CommandException(uce.getMessage());
}
-
- model.addPerson(toAdd);
- return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd)));
+ Ingredient editedIngredient = new Ingredient(toAdd.getName(), model.getQuantityOf(toAdd.getName()));
+ return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(editedIngredient)));
}
@Override
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java
index 9c86b1fa6e4..0edab272950 100644
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java
@@ -2,22 +2,22 @@
import static java.util.Objects.requireNonNull;
-import seedu.address.model.AddressBook;
+import seedu.address.model.Inventory;
import seedu.address.model.Model;
/**
- * Clears the address book.
+ * Clears the inventory.
*/
public class ClearCommand extends Command {
public static final String COMMAND_WORD = "clear";
- public static final String MESSAGE_SUCCESS = "Address book has been cleared!";
+ public static final String MESSAGE_SUCCESS = "Ingredient List has been cleared!";
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
- model.setAddressBook(new AddressBook());
+ model.setInventory(new Inventory());
return new CommandResult(MESSAGE_SUCCESS);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
index 1135ac19b74..1f503a5b5bc 100644
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
@@ -2,47 +2,48 @@
import static java.util.Objects.requireNonNull;
-import java.util.List;
-
-import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
-import seedu.address.model.person.Person;
+import seedu.address.model.recipe.Recipe;
+import seedu.address.model.recipe.UniqueId;
+
/**
- * Deletes a person identified using it's displayed index from the address book.
+ * Deletes a recipe identified using it's displayed index from the recipe book.
*/
public class DeleteCommand extends Command {
public static final String COMMAND_WORD = "delete";
public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Deletes the person identified by the index number used in the displayed person list.\n"
- + "Parameters: INDEX (must be a positive integer)\n"
+ + ": Deletes the recipe identified by the Unique Id (uuid) in the displayed recipe list.\n"
+ + "Parameters: UUID (must be a positive integer greater than 1)\n"
+ "Example: " + COMMAND_WORD + " 1";
- public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
+ public static final String MESSAGE_DELETE_RECIPE_SUCCESS = "Deleted Recipe: %1$s";
- private final Index targetIndex;
+ private final UniqueId uuid;
- public DeleteCommand(Index targetIndex) {
- this.targetIndex = targetIndex;
+ public DeleteCommand(UniqueId uuid) {
+ this.uuid = uuid;
}
@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
- if (targetIndex.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ assert uuid.getId() > 0;
+
+ if (!model.hasRecipe(this.uuid)) {
+ throw new CommandException(Messages.MESSAGE_RECIPE_DOES_NOT_EXIST);
}
- Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
- model.deletePerson(personToDelete);
- return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete)));
+ Recipe deletedRecipe = model.getRecipe(this.uuid);
+ model.deleteRecipe(deletedRecipe);
+
+ return new CommandResult(String.format(MESSAGE_DELETE_RECIPE_SUCCESS, this.uuid));
}
@Override
@@ -57,13 +58,13 @@ public boolean equals(Object other) {
}
DeleteCommand otherDeleteCommand = (DeleteCommand) other;
- return targetIndex.equals(otherDeleteCommand.targetIndex);
+ return (this.uuid.equals(otherDeleteCommand.uuid));
}
@Override
public String toString() {
return new ToStringBuilder(this)
- .add("targetIndex", targetIndex)
+ .add("uuid", uuid)
.toString();
}
}
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
index 4b581c7331e..1af86e7366f 100644
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ b/src/main/java/seedu/address/logic/commands/EditCommand.java
@@ -1,242 +1,228 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.commons.util.CollectionUtil;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
-
-/**
- * Edits the details of an existing person in the address book.
- */
-public class EditCommand extends Command {
-
- public static final String COMMAND_WORD = "edit";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified "
- + "by the index number used in the displayed person list. "
- + "Existing values will be overwritten by the input values.\n"
- + "Parameters: INDEX (must be a positive integer) "
- + "[" + PREFIX_NAME + "NAME] "
- + "[" + PREFIX_PHONE + "PHONE] "
- + "[" + PREFIX_EMAIL + "EMAIL] "
- + "[" + PREFIX_ADDRESS + "ADDRESS] "
- + "[" + PREFIX_TAG + "TAG]...\n"
- + "Example: " + COMMAND_WORD + " 1 "
- + PREFIX_PHONE + "91234567 "
- + PREFIX_EMAIL + "johndoe@example.com";
-
- public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s";
- public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book.";
-
- private final Index index;
- private final EditPersonDescriptor editPersonDescriptor;
-
- /**
- * @param index of the person in the filtered person list to edit
- * @param editPersonDescriptor details to edit the person with
- */
- public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) {
- requireNonNull(index);
- requireNonNull(editPersonDescriptor);
-
- this.index = index;
- this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor);
- }
-
- @Override
- public CommandResult execute(Model model) throws CommandException {
- requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
-
- if (index.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
-
- if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
- }
-
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)));
- }
-
- /**
- * Creates and returns a {@code Person} with the details of {@code personToEdit}
- * edited with {@code editPersonDescriptor}.
- */
- private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) {
- assert personToEdit != null;
-
- Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName());
- Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone());
- Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail());
- Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress());
- Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags());
-
- return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags);
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof EditCommand)) {
- return false;
- }
-
- EditCommand otherEditCommand = (EditCommand) other;
- return index.equals(otherEditCommand.index)
- && editPersonDescriptor.equals(otherEditCommand.editPersonDescriptor);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("index", index)
- .add("editPersonDescriptor", editPersonDescriptor)
- .toString();
- }
-
- /**
- * Stores the details to edit the person with. Each non-empty field value will replace the
- * corresponding field value of the person.
- */
- public static class EditPersonDescriptor {
- private Name name;
- private Phone phone;
- private Email email;
- private Address address;
- private Set tags;
-
- public EditPersonDescriptor() {}
-
- /**
- * Copy constructor.
- * A defensive copy of {@code tags} is used internally.
- */
- public EditPersonDescriptor(EditPersonDescriptor toCopy) {
- setName(toCopy.name);
- setPhone(toCopy.phone);
- setEmail(toCopy.email);
- setAddress(toCopy.address);
- setTags(toCopy.tags);
- }
-
- /**
- * Returns true if at least one field is edited.
- */
- public boolean isAnyFieldEdited() {
- return CollectionUtil.isAnyNonNull(name, phone, email, address, tags);
- }
-
- public void setName(Name name) {
- this.name = name;
- }
-
- public Optional getName() {
- return Optional.ofNullable(name);
- }
-
- public void setPhone(Phone phone) {
- this.phone = phone;
- }
-
- public Optional getPhone() {
- return Optional.ofNullable(phone);
- }
-
- public void setEmail(Email email) {
- this.email = email;
- }
-
- public Optional getEmail() {
- return Optional.ofNullable(email);
- }
-
- public void setAddress(Address address) {
- this.address = address;
- }
-
- public Optional getAddress() {
- return Optional.ofNullable(address);
- }
-
- /**
- * Sets {@code tags} to this object's {@code tags}.
- * A defensive copy of {@code tags} is used internally.
- */
- public void setTags(Set tags) {
- this.tags = (tags != null) ? new HashSet<>(tags) : null;
- }
-
- /**
- * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
- * Returns {@code Optional#empty()} if {@code tags} is null.
- */
- public Optional> getTags() {
- return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof EditPersonDescriptor)) {
- return false;
- }
-
- EditPersonDescriptor otherEditPersonDescriptor = (EditPersonDescriptor) other;
- return Objects.equals(name, otherEditPersonDescriptor.name)
- && Objects.equals(phone, otherEditPersonDescriptor.phone)
- && Objects.equals(email, otherEditPersonDescriptor.email)
- && Objects.equals(address, otherEditPersonDescriptor.address)
- && Objects.equals(tags, otherEditPersonDescriptor.tags);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("name", name)
- .add("phone", phone)
- .add("email", email)
- .add("address", address)
- .add("tags", tags)
- .toString();
- }
- }
-}
+//package seedu.address.logic.commands;
+//
+//import static java.util.Objects.requireNonNull;
+//import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+//import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+//import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+//import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+//import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+//import static seedu.address.model.Model.PREDICATE_SHOW_ALL_INGREDIENTS;
+//
+//import java.util.Collections;
+//import java.util.HashSet;
+//import java.util.List;
+//import java.util.Objects;
+//import java.util.Optional;
+//import java.util.Set;
+//
+//import seedu.address.commons.core.index.Index;
+//import seedu.address.commons.util.CollectionUtil;
+//import seedu.address.commons.util.ToStringBuilder;
+//import seedu.address.logic.Messages;
+//import seedu.address.logic.commands.exceptions.CommandException;
+//import seedu.address.model.Model;
+//import seedu.address.model.Name;
+//import seedu.address.model.ingredient.Ingredient;
+//import seedu.address.model.tag.Tag;
+//
+///**
+// * Edits the details of an existing ingredient in the inventory.
+// */
+//public class EditCommand extends Command {
+//
+// public static final String COMMAND_WORD = "edit";
+//
+// public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the ingredient identified "
+// + "by the index number used in the displayed ingredient list. "
+// + "Existing values will be overwritten by the input values.\n"
+// + "Parameters: INDEX (must be a positive integer) "
+// + "[" + PREFIX_NAME + "NAME] "
+// + "[" + PREFIX_PHONE + "PHONE] "
+// + "[" + PREFIX_EMAIL + "EMAIL] "
+// + "[" + PREFIX_ADDRESS + "ADDRESS] "
+// + "[" + PREFIX_TAG + "TAG]...\n"
+// + "Example: " + COMMAND_WORD + " 1 "
+// + PREFIX_PHONE + "91234567 "
+// + PREFIX_EMAIL + "johndoe@example.com";
+//
+// public static final String MESSAGE_EDIT_INGREDIENT_SUCCESS = "Edited Ingredient: %1$s";
+// public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
+// public static final String MESSAGE_DUPLICATE_INGREDIENT = "This ingredient already exists in the inventory.";
+//
+// private final Index index;
+// private final EditIngredientDescriptor editIngredientDescriptor;
+//
+// /**
+// * @param index of the ingredient in the filtered ingredient list to edit
+// * @param editIngredientDescriptor details to edit the ingredient with
+// */
+// public EditCommand(Index index, EditIngredientDescriptor editIngredientDescriptor) {
+// requireNonNull(index);
+// requireNonNull(editIngredientDescriptor);
+//
+// this.index = index;
+// this.editIngredientDescriptor = new EditIngredientDescriptor(editIngredientDescriptor);
+// }
+//
+// @Override
+// public CommandResult execute(Model model) throws CommandException {
+// requireNonNull(model);
+// List lastShownList = model.getFilteredIngredientList();
+//
+// if (index.getZeroBased() >= lastShownList.size()) {
+// throw new CommandException(Messages.MESSAGE_INVALID_INGREDIENT_DISPLAYED_INDEX);
+// }
+//
+// Ingredient ingredientToEdit = lastShownList.get(index.getZeroBased());
+// Ingredient editedIngredient = createEditedIngredient(ingredientToEdit, editIngredientDescriptor);
+//
+// if (!ingredientToEdit.isSameIngredient(editedIngredient) && model.hasIngredient(editedIngredient)) {
+// throw new CommandException(MESSAGE_DUPLICATE_INGREDIENT);
+// }
+//
+// model.useIngredient(ingredientToEdit, null);
+// model.updateFilteredIngredientList(PREDICATE_SHOW_ALL_INGREDIENTS);
+// return new CommandResult(String.format(MESSAGE_EDIT_INGREDIENT_SUCCESS, Messages.format(editedIngredient)));
+// }
+//
+// /**
+// * Creates and returns a {@code Ingredient} with the details of {@code ingredientToEdit}
+// * edited with {@code editIngredientDescriptor}.
+// */
+// private static Ingredient createEditedIngredient(Ingredient ingredientToEdit,
+// EditIngredientDescriptor editIngredientDescriptor) {
+// assert ingredientToEdit != null;
+//
+// Name updatedName = editIngredientDescriptor.getName().orElse(ingredientToEdit.getName());
+// //Phone updatedPhone = editIngredientDescriptor.getPhone().orElse(ingredientToEdit.getPhone());
+// //Email updatedEmail = editIngredientDescriptor.getEmail().orElse(ingredientToEdit.getEmail());
+// //Address updatedAddress = editIngredientDescriptor.getAddress().orElse(ingredientToEdit.getAddress());
+// //Set updatedTags = editIngredientDescriptor.getTags().orElse(ingredientToEdit.getTags());
+//
+// return new Ingredient(updatedName, null);
+// }
+//
+// @Override
+// public boolean equals(Object other) {
+// if (other == this) {
+// return true;
+// }
+//
+// // instanceof handles nulls
+// if (!(other instanceof EditCommand)) {
+// return false;
+// }
+//
+// EditCommand otherEditCommand = (EditCommand) other;
+// return index.equals(otherEditCommand.index)
+// && editIngredientDescriptor.equals(otherEditCommand.editIngredientDescriptor);
+// }
+//
+// @Override
+// public String toString() {
+// return new ToStringBuilder(this)
+// .add("index", index)
+// .add("editIngredientDescriptor", editIngredientDescriptor)
+// .toString();
+// }
+//
+// /**
+// * Stores the details to edit the ingredient with. Each non-empty field value will replace the
+// * corresponding field value of the ingredient.
+// */
+// public static class EditIngredientDescriptor {
+// private Name name;
+// private Set tags;
+//
+// public EditIngredientDescriptor() {}
+//
+// /**
+// * Copy constructor.
+// * A defensive copy of {@code tags} is used internally.
+// */
+// public EditIngredientDescriptor(EditIngredientDescriptor toCopy) {
+// setName(toCopy.name);
+// setTags(toCopy.tags);
+// }
+//
+// /**
+// * Returns true if at least one field is edited.
+// */
+//// public boolean isAnyFieldEdited() {
+//// return CollectionUtil.isAnyNonNull(name, phone, email, address, tags);
+//// }
+//
+// public void setName(Name name) {
+// this.name = name;
+// }
+//
+// public Optional getName() {
+// return Optional.ofNullable(name);
+// }
+//
+//// public void setPhone(Phone phone) {
+//// this.phone = phone;
+//// }
+////
+//// public Optional getPhone() {
+//// return Optional.ofNullable(phone);
+//// }
+////
+//// public void setEmail(Email email) {
+//// this.email = email;
+//// }
+////
+//// public Optional getEmail() {
+//// return Optional.ofNullable(email);
+//// }
+////
+//// public void setAddress(Address address) {
+//// this.address = address;
+//// }
+////
+//// public Optional getAddress() {
+//// return Optional.ofNullable(address);
+//// }
+//
+// /**
+// * Sets {@code tags} to this object's {@code tags}.
+// * A defensive copy of {@code tags} is used internally.
+// */
+// public void setTags(Set tags) {
+// this.tags = (tags != null) ? new HashSet<>(tags) : null;
+// }
+//
+// /**
+// * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException}
+// * if modification is attempted.
+// * Returns {@code Optional#empty()} if {@code tags} is null.
+// */
+// public Optional> getTags() {
+// return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
+// }
+//
+// @Override
+// public boolean equals(Object other) {
+// if (other == this) {
+// return true;
+// }
+//
+// // instanceof handles nulls
+// if (!(other instanceof EditIngredientDescriptor)) {
+// return false;
+// }
+//
+// EditIngredientDescriptor otherEditIngredientDescriptor = (EditIngredientDescriptor) other;
+// return Objects.equals(name, otherEditIngredientDescriptor.name)
+// && Objects.equals(tags, otherEditIngredientDescriptor.tags);
+// }
+//
+// @Override
+// public String toString() {
+// return new ToStringBuilder(this)
+// .add("name", name)
+// .add("tags", tags)
+// .toString();
+// }
+// }
+//}
diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java
index 3dd85a8ba90..38eb1bca00f 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java
@@ -9,7 +9,7 @@ public class ExitCommand extends Command {
public static final String COMMAND_WORD = "exit";
- public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ...";
+ public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Recipe Book as requested ...";
@Override
public CommandResult execute(Model model) {
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
index 72b9eddd3a7..c363a4dab03 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/seedu/address/logic/commands/FindCommand.java
@@ -1,58 +1,59 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.model.Model;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-
-/**
- * Finds and lists all persons in address book whose name contains any of the argument keywords.
- * Keyword matching is case insensitive.
- */
-public class FindCommand extends Command {
-
- public static final String COMMAND_WORD = "find";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of "
- + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n"
- + "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
- + "Example: " + COMMAND_WORD + " alice bob charlie";
-
- private final NameContainsKeywordsPredicate predicate;
-
- public FindCommand(NameContainsKeywordsPredicate predicate) {
- this.predicate = predicate;
- }
-
- @Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.updateFilteredPersonList(predicate);
- return new CommandResult(
- String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof FindCommand)) {
- return false;
- }
-
- FindCommand otherFindCommand = (FindCommand) other;
- return predicate.equals(otherFindCommand.predicate);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("predicate", predicate)
- .toString();
- }
-}
+//package seedu.address.logic.commands;
+//
+//import static java.util.Objects.requireNonNull;
+//
+//import seedu.address.commons.util.ToStringBuilder;
+//import seedu.address.logic.Messages;
+//import seedu.address.model.Model;
+//import seedu.address.model.ingredient.NameContainsKeywordsPredicate;
+//
+///**
+// * Finds and lists all ingredients in inventory whose name contains any of the argument keywords.
+// * Keyword matching is case insensitive.
+// */
+//public class FindCommand extends Command {
+//
+// public static final String COMMAND_WORD = "find";
+//
+// public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all ingredients whose names contain any of "
+// + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n"
+// + "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
+// + "Example: " + COMMAND_WORD + " alice bob charlie";
+//
+// private final NameContainsKeywordsPredicate predicate;
+//
+// public FindCommand(NameContainsKeywordsPredicate predicate) {
+// this.predicate = predicate;
+// }
+//
+// @Override
+// public CommandResult execute(Model model) {
+// requireNonNull(model);
+// model.updateFilteredIngredientList(predicate);
+// return new CommandResult(
+// String.format(Messages.MESSAGE_INGREDIENTS_LISTED_OVERVIEW, model.getFilteredIngredientList()
+// .size()));
+// }
+//
+// @Override
+// public boolean equals(Object other) {
+// if (other == this) {
+// return true;
+// }
+//
+// // instanceof handles nulls
+// if (!(other instanceof FindCommand)) {
+// return false;
+// }
+//
+// FindCommand otherFindCommand = (FindCommand) other;
+// return predicate.equals(otherFindCommand.predicate);
+// }
+//
+// @Override
+// public String toString() {
+// return new ToStringBuilder(this)
+// .add("predicate", predicate)
+// .toString();
+// }
+//}
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java
index 84be6ad2596..06e3c0c7cdd 100644
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ListCommand.java
@@ -1,24 +1,22 @@
package seedu.address.logic.commands;
import static java.util.Objects.requireNonNull;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_RECIPES;
import seedu.address.model.Model;
/**
- * Lists all persons in the address book to the user.
+ * Lists all ingredients in the inventory to the user.
*/
public class ListCommand extends Command {
public static final String COMMAND_WORD = "list";
- public static final String MESSAGE_SUCCESS = "Listed all persons";
-
-
+ public static final String MESSAGE_SUCCESS = "Listed all recipes";
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ model.updateFilteredRecipeList(PREDICATE_SHOW_ALL_RECIPES);
return new CommandResult(MESSAGE_SUCCESS);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/ModifyCommand.java b/src/main/java/seedu/address/logic/commands/ModifyCommand.java
new file mode 100644
index 00000000000..4efa7ded70d
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ModifyCommand.java
@@ -0,0 +1,100 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_MODIFY_RECIPE_SUCCESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_UNIT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_UUID;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.recipe.Recipe;
+import seedu.address.model.recipe.RecipeUuidMatchesPredicate;
+import seedu.address.model.recipe.UniqueId;
+
+/**
+ * Modifies an ingredient in a recipe.
+ */
+public class ModifyCommand extends Command {
+ public static final String COMMAND_WORD = "modify";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Modifies the ingredients used in the "
+ + "recipe "
+ + "by the uuid used in the displayed recipe list. "
+ + "Existing values will be overwritten by the input values.\n"
+ + "Parameters: "
+ + PREFIX_UUID + "UUID "
+ + PREFIX_NAME + "NAME "
+ + PREFIX_QUANTITY + "QUANTITY "
+ + PREFIX_UNIT + "UNIT "
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_UUID + "1 "
+ + PREFIX_NAME + "milk "
+ + PREFIX_QUANTITY + "600 "
+ + PREFIX_UNIT + "g ";
+ private UniqueId recipeUuid;
+ private Ingredient editedIngredient;
+ /**
+ * Creates a ModifyCommand to modify the specified {@code Ingredient}
+ */
+ public ModifyCommand(UniqueId uuid, Ingredient newIngredient) {
+ requireNonNull(newIngredient);
+ requireNonNull(uuid);
+ editedIngredient = newIngredient;
+ recipeUuid = uuid;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ assert recipeUuid.getId() > 0;
+
+ if (!model.hasRecipe(recipeUuid)) {
+ throw new CommandException(Messages.MESSAGE_RECIPE_DOES_NOT_EXIST);
+ }
+
+ Recipe newRecipe;
+ Recipe oldRecipe = model.getRecipe(recipeUuid);
+
+ if (!oldRecipe.containsIngredient(editedIngredient.getName())) {
+ newRecipe = oldRecipe.addIngredient(editedIngredient);
+ } else {
+ newRecipe = oldRecipe.modifyIngredients(editedIngredient.getName().fullName, editedIngredient);
+ }
+
+ model.deleteRecipe(oldRecipe);
+ model.addRecipe(newRecipe);
+ model.updateFilteredRecipeList(new RecipeUuidMatchesPredicate(recipeUuid));
+ return new CommandResult(String.format(MESSAGE_MODIFY_RECIPE_SUCCESS,
+ recipeUuid));
+
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof ModifyCommand)) {
+ return false;
+ }
+
+ ModifyCommand otherModifyCommand = (ModifyCommand) other;
+ return editedIngredient.equals(otherModifyCommand.editedIngredient)
+ && this.recipeUuid.equals(otherModifyCommand.recipeUuid);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("uuid", recipeUuid)
+ .add("toModify", editedIngredient)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/RecipeAddCommand.java b/src/main/java/seedu/address/logic/commands/RecipeAddCommand.java
new file mode 100644
index 00000000000..3faa2d73e35
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/RecipeAddCommand.java
@@ -0,0 +1,62 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.recipe.Recipe;
+
+/**
+ * Views a specific recipe in the recipe list.
+ */
+public class RecipeAddCommand extends Command {
+ public static final String COMMAND_WORD = "addrecipe";
+
+ public static final String MESSAGE_INGREDIENT_USAGE = "Ingredient format: NAME NUMBER-UNIT (e.g. flour 100g)";
+
+ public static final String MESSAGE_SUCCESS = "New recipe added: %1$s";
+
+ public static final String MESSAGE_STEP_USAGE = "Step format: NUMBER. STEP (e.g. 1. Preheat Oven)";
+
+ private final Recipe toAdd;
+
+ /**
+ * Creates an AddCommand to add the specified {@code Ingredient}
+ */
+ public RecipeAddCommand(Recipe recipe) {
+ requireNonNull(recipe);
+ toAdd = recipe;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.addRecipe(toAdd);
+
+ return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd)));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof RecipeAddCommand)) {
+ return false;
+ }
+
+ RecipeAddCommand otherAddCommand = (RecipeAddCommand) other;
+ return toAdd.equals(otherAddCommand.toAdd);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/RecipeViewCommand.java b/src/main/java/seedu/address/logic/commands/RecipeViewCommand.java
new file mode 100644
index 00000000000..1f1e622cef3
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/RecipeViewCommand.java
@@ -0,0 +1,72 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import javafx.collections.ObservableList;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.recipe.Recipe;
+import seedu.address.model.recipe.RecipeUuidMatchesPredicate;
+import seedu.address.model.recipe.UniqueId;
+
+/**
+ * Views a specific recipe in the recipe list.
+ */
+public class RecipeViewCommand extends Command {
+ public static final String COMMAND_WORD = "view";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Views a specific recipe in the recipe book. "
+ + "Parameters: "
+ + "uuid (greater or equal to 1)"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ private final RecipeUuidMatchesPredicate predicate;
+ private final UniqueId uuid;
+
+ /**
+ * Creates an RecipeViewCommand to view the specified {@code Recipe}
+ */
+ public RecipeViewCommand(RecipeUuidMatchesPredicate predicate) {
+ requireNonNull(predicate);
+ this.predicate = predicate;
+ this.uuid = predicate.getUuid();
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ if (!model.hasRecipe(this.uuid)) {
+ throw new CommandException(Messages.MESSAGE_RECIPE_DOES_NOT_EXIST);
+ }
+
+ model.updateFilteredRecipeList(predicate);
+ ObservableList recipeLst = model.getFilteredRecipeList();
+ assert recipeLst.size() == 1;
+ return new CommandResult(String.format(Messages.MESSAGE_RECIPE_LISTED, uuid));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof RecipeViewCommand)) {
+ return false;
+ }
+
+ RecipeViewCommand otherStockCommand = (RecipeViewCommand) other;
+ return predicate.equals(otherStockCommand.predicate);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/SearchCommand.java b/src/main/java/seedu/address/logic/commands/SearchCommand.java
new file mode 100644
index 00000000000..b4cacaf7778
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/SearchCommand.java
@@ -0,0 +1,64 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.recipe.Recipe;
+
+/**
+ * Displays all recipes that uses the ingredient within the recipe book
+ */
+public class SearchCommand extends Command {
+ public static final String COMMAND_WORD = "search";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Displays all recipes that uses the ingredient within the recipe book "
+ + "Parameters: "
+ + "ingredient name "
+ + "Example: " + COMMAND_WORD + " flour";
+
+ private final Predicate predicate;
+
+ /**
+ * Creates an RecipeViewCommand to view the specified {@code Recipe}
+ */
+ public SearchCommand(Predicate predicate) {
+ requireNonNull(predicate);
+ this.predicate = predicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredRecipeList(predicate);
+ return new CommandResult(
+ String.format(Messages.MESSAGE_RECIPE_LISTED_OVERVIEW, model.getFilteredRecipeList().size()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof SearchCommand)) {
+ return false;
+ }
+
+ SearchCommand otherSearchCommand = (SearchCommand) other;
+ return predicate.equals(otherSearchCommand.predicate);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/StockCommand.java b/src/main/java/seedu/address/logic/commands/StockCommand.java
new file mode 100644
index 00000000000..fc7916785ed
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/StockCommand.java
@@ -0,0 +1,66 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.ingredient.Ingredient;
+
+// TODO Add JavaDocs
+/**
+ * Stub JavaDocs
+ */
+public class StockCommand extends Command {
+
+ public static final String COMMAND_WORD = "stock";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all specified ingredients and their respective"
+ + "quantities. "
+ + "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
+ + "Example: " + COMMAND_WORD + " flour milk";
+
+ private final Predicate predicate;
+
+ /**
+ * Constructor with for stock command with an ingredient predicate
+ * @param predicate
+ */
+ public StockCommand(Predicate predicate) {
+ //todo does this need a require non null?
+ this.predicate = predicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredIngredientList(predicate);
+ return new CommandResult(
+ String.format(Messages.MESSAGE_INGREDIENTS_LISTED_OVERVIEW, model.getFilteredIngredientList().size()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof StockCommand)) {
+ return false;
+ }
+
+ StockCommand otherStockCommand = (StockCommand) other;
+ return predicate.equals(otherStockCommand.predicate);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/commands/UseCommand.java b/src/main/java/seedu/address/logic/commands/UseCommand.java
new file mode 100644
index 00000000000..8e798ca45a8
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/UseCommand.java
@@ -0,0 +1,89 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_UNIT;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.Name;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.ingredient.Quantity;
+import seedu.address.model.ingredient.exceptions.UnitConversionException;
+
+/**
+ * Use an ingredient from the inventory.
+ */
+public class UseCommand extends Command {
+
+ public static final String COMMAND_WORD = "use";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Depletes a specified amount of an ingredient from stock. "
+ + "Parameters: "
+ + PREFIX_NAME + "NAME "
+ + "[" + PREFIX_QUANTITY + "QUANTITY] "
+ + "[" + PREFIX_UNIT + "UNIT] "
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "milk "
+ + PREFIX_QUANTITY + "600 "
+ + PREFIX_UNIT + "g ";
+
+ public static final String MESSAGE_SUCCESS = "Ingredient used: %1$s";
+
+ private final Name toUse;
+ private final Quantity quantityUsed;
+ /**
+ * Creates a UseCommand to use the specified {@code Ingredient}
+ */
+ public UseCommand(Name ingredient, Quantity quantity) {
+ requireNonNull(ingredient);
+ toUse = ingredient;
+ quantityUsed = quantity;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ if (quantityUsed == null) {
+ model.useIngredient(toUse, model.getQuantityOf(toUse));
+ return new CommandResult(String.format(MESSAGE_SUCCESS,
+ Messages.format(new Ingredient(toUse, model.getQuantityOf(toUse)))));
+ }
+ try {
+ model.useIngredient(toUse, quantityUsed);
+ } catch (UnitConversionException uce) {
+ throw new CommandException(uce.getMessage());
+ }
+
+ return new CommandResult(String.format(MESSAGE_SUCCESS,
+ Messages.format(new Ingredient(toUse, model.getQuantityOf(toUse)))));
+
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UseCommand)) {
+ return false;
+ }
+
+ UseCommand otherUseCommand = (UseCommand) other;
+ return toUse.equals(otherUseCommand.toUse);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toUse", toUse)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
index 4ff1a97ed77..c7e6509a771 100644
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
@@ -1,29 +1,25 @@
package seedu.address.logic.parser;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_UNIT;
-import java.util.Set;
import java.util.stream.Stream;
import seedu.address.logic.commands.AddCommand;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.Name;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.ingredient.Quantity;
+import seedu.address.model.ingredient.Unit;
+import seedu.address.model.ingredient.exceptions.UnitFormatException;
+
/**
* Parses input arguments and creates a new AddCommand object
*/
public class AddCommandParser implements Parser {
-
/**
* Parses the given {@code String} of arguments in the context of the AddCommand
* and returns an AddCommand object for execution.
@@ -31,23 +27,26 @@ public class AddCommandParser implements Parser {
*/
public AddCommand parse(String args) throws ParseException {
ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_UNIT);
- if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_UNIT)
|| !argMultimap.getPreamble().isEmpty()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
}
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_QUANTITY, PREFIX_UNIT);
Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
- Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get());
- Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get());
- Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get());
- Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
- Person person = new Person(name, phone, email, address, tagList);
+ Unit unit;
+ try {
+ unit = ParserUtil.parseUnitOfIngredient(argMultimap.getValue(PREFIX_UNIT).get());
+ } catch (UnitFormatException e) {
+ throw new ParseException("This is not a valid unit!");
+ }
+ Quantity quantity = ParserUtil.parseQuantity(argMultimap.getValue(PREFIX_QUANTITY).get(), unit);
+ Ingredient ingredient = new Ingredient(name, quantity);
- return new AddCommand(person);
+ return new AddCommand(ingredient);
}
/**
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index 75b1a9bf119..acf4e1dc918 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -7,9 +7,8 @@ public class CliSyntax {
/* Prefix definitions */
public static final Prefix PREFIX_NAME = new Prefix("n/");
- public static final Prefix PREFIX_PHONE = new Prefix("p/");
- public static final Prefix PREFIX_EMAIL = new Prefix("e/");
- public static final Prefix PREFIX_ADDRESS = new Prefix("a/");
- public static final Prefix PREFIX_TAG = new Prefix("t/");
+ public static final Prefix PREFIX_QUANTITY = new Prefix("q/");
+ public static final Prefix PREFIX_UNIT = new Prefix("u/");
+ public static final Prefix PREFIX_UUID = new Prefix("i/");
}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
index 3527fe76a3e..02a4165b236 100644
--- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
@@ -1,10 +1,12 @@
package seedu.address.logic.parser;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_RECIPE_DISPLAYED_INDEX;
-import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.DeleteCommand;
import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.recipe.UniqueId;
+
/**
* Parses input arguments and creates a new DeleteCommand object
@@ -17,13 +19,19 @@ public class DeleteCommandParser implements Parser {
* @throws ParseException if the user input does not conform the expected format
*/
public DeleteCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ int id;
try {
- Index index = ParserUtil.parseIndex(args);
- return new DeleteCommand(index);
- } catch (ParseException pe) {
+ id = Integer.parseInt(trimmedArgs);
+ } catch (NumberFormatException pe) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe);
}
- }
+ if (id <= 0) {
+ throw new ParseException(MESSAGE_INVALID_RECIPE_DISPLAYED_INDEX);
+ }
+
+ return new DeleteCommand(new UniqueId(id));
+ }
}
diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
index 46b3309a78b..c031eb2944a 100644
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
@@ -1,85 +1,86 @@
-package seedu.address.logic.parser;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Optional;
-import java.util.Set;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.logic.commands.EditCommand;
-import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.tag.Tag;
-
-/**
- * Parses input arguments and creates a new EditCommand object
- */
-public class EditCommandParser implements Parser {
-
- /**
- * Parses the given {@code String} of arguments in the context of the EditCommand
- * and returns an EditCommand object for execution.
- * @throws ParseException if the user input does not conform the expected format
- */
- public EditCommand parse(String args) throws ParseException {
- requireNonNull(args);
- ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
-
- Index index;
-
- try {
- index = ParserUtil.parseIndex(argMultimap.getPreamble());
- } catch (ParseException pe) {
- throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe);
- }
-
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
-
- EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
-
- if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
- editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
- }
- if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
- editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
- }
- if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
- editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
- }
- if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
- editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
- }
- parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags);
-
- if (!editPersonDescriptor.isAnyFieldEdited()) {
- throw new ParseException(EditCommand.MESSAGE_NOT_EDITED);
- }
-
- return new EditCommand(index, editPersonDescriptor);
- }
-
- /**
- * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty.
- * If {@code tags} contain only one element which is an empty string, it will be parsed into a
- * {@code Set} containing zero tags.
- */
- private Optional> parseTagsForEdit(Collection tags) throws ParseException {
- assert tags != null;
-
- if (tags.isEmpty()) {
- return Optional.empty();
- }
- Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags;
- return Optional.of(ParserUtil.parseTags(tagSet));
- }
-
-}
+//package seedu.address.logic.parser;
+//
+//import static java.util.Objects.requireNonNull;
+//import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+//import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+//import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+//import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+//import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+//import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+//
+//import java.util.Collection;
+//import java.util.Collections;
+//import java.util.Optional;
+//import java.util.Set;
+//
+//import seedu.address.commons.core.index.Index;
+//import seedu.address.logic.commands.EditCommand;
+//import seedu.address.logic.commands.EditCommand.EditIngredientDescriptor;
+//import seedu.address.logic.parser.exceptions.ParseException;
+//import seedu.address.model.tag.Tag;
+//
+///**
+// * Parses input arguments and creates a new EditCommand object
+// */
+//public class EditCommandParser implements Parser {
+//
+// /**
+// * Parses the given {@code String} of arguments in the context of the EditCommand
+// * and returns an EditCommand object for execution.
+// * @throws ParseException if the user input does not conform the expected format
+// */
+// public EditCommand parse(String args) throws ParseException {
+// requireNonNull(args);
+// ArgumentMultimap argMultimap =
+// ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+//
+// Index index;
+//
+// try {
+// index = ParserUtil.parseIndex(argMultimap.getPreamble());
+// } catch (ParseException pe) {
+// throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe);
+// }
+//
+// argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
+//
+// EditIngredientDescriptor editIngredientDescriptor = new EditIngredientDescriptor();
+//
+// if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
+// editIngredientDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
+// }
+//// if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
+//// editIngredientDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
+//// }
+//// if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
+//// editIngredientDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
+//// }
+//// if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
+//// editIngredientDescriptor
+// .setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
+//// }
+// parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editIngredientDescriptor::setTags);
+//
+//// if (!editIngredientDescriptor.isAnyFieldEdited()) {
+//// throw new ParseException(EditCommand.MESSAGE_NOT_EDITED);
+//// }
+//
+// return new EditCommand(index, editIngredientDescriptor);
+// }
+//
+// /**
+// * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty.
+// * If {@code tags} contain only one element which is an empty string, it will be parsed into a
+// * {@code Set} containing zero tags.
+// */
+// private Optional> parseTagsForEdit(Collection tags) throws ParseException {
+// assert tags != null;
+//
+// if (tags.isEmpty()) {
+// return Optional.empty();
+// }
+// Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags;
+// return Optional.of(ParserUtil.parseTags(tagSet));
+// }
+//
+//}
diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
index 2867bde857b..611b8a81af2 100644
--- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
@@ -1,33 +1,33 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-
-import java.util.Arrays;
-
-import seedu.address.logic.commands.FindCommand;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-
-/**
- * Parses input arguments and creates a new FindCommand object
- */
-public class FindCommandParser implements Parser {
-
- /**
- * Parses the given {@code String} of arguments in the context of the FindCommand
- * and returns a FindCommand object for execution.
- * @throws ParseException if the user input does not conform the expected format
- */
- public FindCommand parse(String args) throws ParseException {
- String trimmedArgs = args.trim();
- if (trimmedArgs.isEmpty()) {
- throw new ParseException(
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
- }
-
- String[] nameKeywords = trimmedArgs.split("\\s+");
-
- return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
- }
-
-}
+//package seedu.address.logic.parser;
+//
+//import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+//
+//import java.util.Arrays;
+//
+//import seedu.address.logic.commands.FindCommand;
+//import seedu.address.logic.parser.exceptions.ParseException;
+//import seedu.address.model.ingredient.NameContainsKeywordsPredicate;
+//
+///**
+// * Parses input arguments and creates a new FindCommand object
+// */
+//public class FindCommandParser implements Parser {
+//
+// /**
+// * Parses the given {@code String} of arguments in the context of the FindCommand
+// * and returns a FindCommand object for execution.
+// * @throws ParseException if the user input does not conform the expected format
+// */
+// public FindCommand parse(String args) throws ParseException {
+// String trimmedArgs = args.trim();
+// if (trimmedArgs.isEmpty()) {
+// throw new ParseException(
+// String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
+// }
+//
+// String[] nameKeywords = trimmedArgs.split("\\s+");
+//
+// return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
+// }
+//
+//}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/InventoryAppParser.java
similarity index 75%
rename from src/main/java/seedu/address/logic/parser/AddressBookParser.java
rename to src/main/java/seedu/address/logic/parser/InventoryAppParser.java
index 3149ee07e0b..5bdf538ea16 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/InventoryAppParser.java
@@ -12,23 +12,26 @@
import seedu.address.logic.commands.ClearCommand;
import seedu.address.logic.commands.Command;
import seedu.address.logic.commands.DeleteCommand;
-import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.ExitCommand;
-import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.ModifyCommand;
+import seedu.address.logic.commands.RecipeViewCommand;
+import seedu.address.logic.commands.SearchCommand;
+import seedu.address.logic.commands.StockCommand;
+import seedu.address.logic.commands.UseCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
* Parses user input.
*/
-public class AddressBookParser {
+public class InventoryAppParser {
/**
* Used for initial separation of command word and args.
*/
private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)");
- private static final Logger logger = LogsCenter.getLogger(AddressBookParser.class);
+ private static final Logger logger = LogsCenter.getLogger(InventoryAppParser.class);
/**
* Parses user input into command for execution.
@@ -52,31 +55,39 @@ public Command parseCommand(String userInput) throws ParseException {
logger.fine("Command word: " + commandWord + "; Arguments: " + arguments);
switch (commandWord) {
-
case AddCommand.COMMAND_WORD:
return new AddCommandParser().parse(arguments);
- case EditCommand.COMMAND_WORD:
- return new EditCommandParser().parse(arguments);
-
- case DeleteCommand.COMMAND_WORD:
- return new DeleteCommandParser().parse(arguments);
-
case ClearCommand.COMMAND_WORD:
return new ClearCommand();
- case FindCommand.COMMAND_WORD:
- return new FindCommandParser().parse(arguments);
-
case ListCommand.COMMAND_WORD:
return new ListCommand();
case ExitCommand.COMMAND_WORD:
return new ExitCommand();
+ case UseCommand.COMMAND_WORD:
+ return new UseCommandParser().parse(arguments);
+
case HelpCommand.COMMAND_WORD:
return new HelpCommand();
+ case StockCommand.COMMAND_WORD:
+ return new StockCommandParser().parse(arguments);
+
+ case RecipeViewCommand.COMMAND_WORD:
+ return new RecipeViewCommandParser().parse(arguments);
+
+ case DeleteCommand.COMMAND_WORD:
+ return new DeleteCommandParser().parse(arguments);
+
+ case ModifyCommand.COMMAND_WORD:
+ return new ModifyCommandParser().parse(arguments);
+
+ case SearchCommand.COMMAND_WORD:
+ return new SearchCommandParser().parse(arguments);
+
default:
logger.finer("This user input caused a ParseException: " + userInput);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
diff --git a/src/main/java/seedu/address/logic/parser/ModifyCommandParser.java b/src/main/java/seedu/address/logic/parser/ModifyCommandParser.java
new file mode 100644
index 00000000000..8a76450fba2
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ModifyCommandParser.java
@@ -0,0 +1,75 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_RECIPE_DISPLAYED_INDEX;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_UNIT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_UUID;
+
+import java.util.stream.Stream;
+
+import seedu.address.logic.commands.ModifyCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.Name;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.ingredient.Quantity;
+import seedu.address.model.ingredient.Unit;
+import seedu.address.model.ingredient.exceptions.UnitFormatException;
+import seedu.address.model.recipe.UniqueId;
+
+/**
+ * Parses input arguments and creates a new ModifyCommand object
+ */
+public class ModifyCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the ModifyCommand
+ * and returns a ModifyCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public ModifyCommand parse(String args) throws ParseException {
+ int id;
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_UUID, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_UNIT);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_UUID, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_UNIT)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ModifyCommand.MESSAGE_USAGE));
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_UUID, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_UNIT);
+
+ try {
+ id = Integer.parseInt(argMultimap.getValue(PREFIX_UUID).get());
+ } catch (NumberFormatException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ModifyCommand.MESSAGE_USAGE), pe);
+ }
+
+ if (id <= 0) {
+ throw new ParseException(MESSAGE_INVALID_RECIPE_DISPLAYED_INDEX);
+ }
+
+ Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
+
+ Unit unit;
+ try {
+ unit = ParserUtil.parseUnitOfIngredient(argMultimap.getValue(PREFIX_UNIT).get());
+ } catch (UnitFormatException e) {
+ throw new ParseException("This is not a valid unit!");
+ }
+
+ Quantity quantity = ParserUtil.parseQuantity(argMultimap.getValue(PREFIX_QUANTITY).get(), unit);
+ Ingredient newIngredient = new Ingredient(name, quantity);
+
+ return new ModifyCommand(new UniqueId(id), newIngredient);
+
+ }
+ /**
+ * Returns true if none of the prefixes contains empty {@code Optional} values in the given
+ * {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
index b117acb9c55..5289eeda6a9 100644
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java
@@ -1,19 +1,18 @@
package seedu.address.logic.parser;
import static java.util.Objects.requireNonNull;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.StringUtil;
+import seedu.address.logic.commands.RecipeAddCommand;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.Name;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.ingredient.Quantity;
+import seedu.address.model.ingredient.Unit;
+import seedu.address.model.recipe.RecipeStep;
+import seedu.address.model.recipe.exceptions.RecipeStepFormatException;
/**
* Contains utility methods used for parsing strings in the various *Parser classes.
@@ -50,75 +49,110 @@ public static Name parseName(String name) throws ParseException {
return new Name(trimmedName);
}
+ // Todo Add JavaDocs
/**
- * Parses a {@code String phone} into a {@code Phone}.
- * Leading and trailing whitespaces will be trimmed.
- *
- * @throws ParseException if the given {@code phone} is invalid.
+ * Stub
+ * @param quantStr Stub
+ * @return Stub
+ * @throws ParseException Stub
*/
- public static Phone parsePhone(String phone) throws ParseException {
- requireNonNull(phone);
- String trimmedPhone = phone.trim();
- if (!Phone.isValidPhone(trimmedPhone)) {
- throw new ParseException(Phone.MESSAGE_CONSTRAINTS);
+ public static Quantity parseQuantity(String quantStr, Unit unit) throws ParseException {
+ requireNonNull(quantStr);
+ double amount;
+ try {
+ amount = Double.parseDouble(quantStr);
+ } catch (NumberFormatException e) {
+ throw new ParseException("Invalid quantity: " + quantStr);
}
- return new Phone(trimmedPhone);
+ if (amount <= 0) {
+ throw new ParseException("Quantity has to be positive");
+ }
+ return new Quantity(amount, unit);
}
+
/**
- * Parses a {@code String address} into an {@code Address}.
- * Leading and trailing whitespaces will be trimmed.
- *
- * @throws ParseException if the given {@code address} is invalid.
+ * Parse the lightweight recipe ingredient string into an ingredient
+ * @param ingredientString Lightweight ingredient format for only recipe ingredients
+ * @return An ingredient instance
+ * @throws ParseException When ingredient is incorrectly formatted
*/
- public static Address parseAddress(String address) throws ParseException {
- requireNonNull(address);
- String trimmedAddress = address.trim();
- if (!Address.isValidAddress(trimmedAddress)) {
- throw new ParseException(Address.MESSAGE_CONSTRAINTS);
+ public static Ingredient parseRecipeIngredient(String ingredientString) throws ParseException {
+ int whitespaceIndex = ingredientString.lastIndexOf(" ");
+ if (whitespaceIndex == -1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ RecipeAddCommand.MESSAGE_INGREDIENT_USAGE));
}
- return new Address(trimmedAddress);
- }
+ String ingredientName = ingredientString.substring(0, whitespaceIndex);
+ String ingredientQuantity = ingredientString.substring(whitespaceIndex + 1);
- /**
- * Parses a {@code String email} into an {@code Email}.
- * Leading and trailing whitespaces will be trimmed.
- *
- * @throws ParseException if the given {@code email} is invalid.
- */
- public static Email parseEmail(String email) throws ParseException {
- requireNonNull(email);
- String trimmedEmail = email.trim();
- if (!Email.isValidEmail(trimmedEmail)) {
- throw new ParseException(Email.MESSAGE_CONSTRAINTS);
+ int unitIndex = ingredientQuantity.indexOf(ingredientQuantity.chars().filter(Character::isLetter)
+ .findFirst().orElse(-1));
+ if (unitIndex == -1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ RecipeAddCommand.MESSAGE_INGREDIENT_USAGE));
+ }
+ String quantityValue = ingredientQuantity.substring(0, unitIndex);
+ String quantityUnit = ingredientQuantity.substring(unitIndex);
+
+ try {
+ Quantity quantity = new Quantity(Double.parseDouble(quantityValue), Unit.parseUnit(quantityUnit));
+ return new Ingredient(new Name(ingredientName), quantity);
+ } catch (IllegalArgumentException fe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ RecipeAddCommand.MESSAGE_STEP_USAGE));
}
- return new Email(trimmedEmail);
}
/**
- * Parses a {@code String tag} into a {@code Tag}.
- * Leading and trailing whitespaces will be trimmed.
- *
- * @throws ParseException if the given {@code tag} is invalid.
+ * Stub
+ * @param unit Stub
+ * @return Stub
*/
- public static Tag parseTag(String tag) throws ParseException {
- requireNonNull(tag);
- String trimmedTag = tag.trim();
- if (!Tag.isValidTagName(trimmedTag)) {
- throw new ParseException(Tag.MESSAGE_CONSTRAINTS);
- }
- return new Tag(trimmedTag);
+ public static Unit parseUnitOfIngredient(String unit) {
+ requireNonNull(unit);
+ return Unit.parseUnit(unit);
}
/**
- * Parses {@code Collection tags} into a {@code Set}.
+ * Parse a recipeStep for the RecipeAddCommand.
+ * @param recipeStepString The string format for the RecipeAddCommand
+ * @return The recipeStep instance
+ * @throws ParseException When step is incorrectly formatted
*/
- public static Set parseTags(Collection tags) throws ParseException {
- requireNonNull(tags);
- final Set tagSet = new HashSet<>();
- for (String tagName : tags) {
- tagSet.add(parseTag(tagName));
+ public static RecipeStep parseRecipeStep(String recipeStepString) throws ParseException {
+ try {
+ return RecipeStep.parseRecipeStep(recipeStepString);
+ } catch (RecipeStepFormatException rsfe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ RecipeAddCommand.MESSAGE_INGREDIENT_USAGE));
}
- return tagSet;
}
+
+ // /**
+ // * Parses a {@code String tag} into a {@code Tag}.
+ // * Leading and trailing whitespaces will be trimmed.
+ // *
+ // * @throws ParseException if the given {@code tag} is invalid.
+ // */
+ // public static Tag parseTag(String tag) throws ParseException {
+ // requireNonNull(tag);
+ // String trimmedTag = tag.trim();
+ // if (!Tag.isValidTagName(trimmedTag)) {
+ // throw new ParseException(Tag.MESSAGE_CONSTRAINTS);
+ // }
+ // return new Tag(trimmedTag);
+ // }
+ //
+ // /**
+ // * Parses {@code Collection tags} into a {@code Set}.
+ // */
+ // public static Set parseTags(Collection tags) throws ParseException {
+ // requireNonNull(tags);
+ // final Set tagSet = new HashSet<>();
+ // for (String tagName : tags) {
+ // tagSet.add(parseTag(tagName));
+ // }
+ // return tagSet;
+ // }
}
diff --git a/src/main/java/seedu/address/logic/parser/RecipeAddCommandParser.java b/src/main/java/seedu/address/logic/parser/RecipeAddCommandParser.java
new file mode 100644
index 00000000000..6f7da4faca9
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/RecipeAddCommandParser.java
@@ -0,0 +1,132 @@
+package seedu.address.logic.parser;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+import seedu.address.logic.commands.RecipeAddCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.Name;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.recipe.Recipe;
+import seedu.address.model.recipe.RecipeStep;
+
+/**
+ * Parses input arguments and creates a new AddRecipeCommand object
+ */
+public class RecipeAddCommandParser implements Parser {
+ public static final String MESSAGE_SUCCESS_NAME = "Name has been set!";
+
+ public static final String MESSAGE_SUCCESS_INGREDIENT = "Ingredient added: %1$s";
+ public static final String MESSAGE_FAIL_INGREDIENT = "Failed to add ingredient";
+ public static final String MESSAGE_SUCCESS_STEP = "Step added successfully to recipe";
+ public static final String MESSAGE_FAIL_STEP = "Failed to add step";
+
+ private Name name;
+ private final List ingredients;
+ private final List recipeSteps;
+
+ /**
+ * Initialises the lists
+ */
+ public RecipeAddCommandParser() {
+ ingredients = new ArrayList<>();
+ recipeSteps = new ArrayList<>();
+ }
+
+ /**
+ * Adds a name to the parameter buffer
+ * @param name The name of the recipe
+ * @return The success message upon adding the name
+ */
+ public String addName(String name) throws ParseException {
+ this.name = ParserUtil.parseName(name);
+ return MESSAGE_SUCCESS_NAME;
+ }
+
+ /**
+ * Adds a step into the parameter buffer
+ * @param step A step in the recipe
+ * @return The outcome of parsing and adding the step
+ */
+ public String addStep(String step) {
+ try {
+ RecipeStep toAdd = ParserUtil.parseRecipeStep(step);
+ recipeSteps.add(toAdd);
+ return MESSAGE_SUCCESS_STEP;
+ } catch (ParseException pe) {
+ return MESSAGE_FAIL_STEP;
+ }
+ }
+
+ /**
+ * Adds an ingredient into the parameter buffer
+ * @param ingredient AN ingredient in the recipe
+ * @return The outcome of parsing and adding the ingredient
+ */
+ public String addIngredient(String ingredient) {
+ try {
+ Ingredient toAdd = ParserUtil.parseRecipeIngredient(ingredient);
+ ingredients.add(toAdd);
+ return String.format(MESSAGE_SUCCESS_INGREDIENT, ingredient);
+ } catch (ParseException pe) {
+ return MESSAGE_FAIL_INGREDIENT;
+ }
+ }
+
+ /**
+ * Generate the RecipeAddCommand from the buffered parameters
+ * @return The Command instance
+ */
+ public RecipeAddCommand generateCommand() {
+ recipeSteps.sort(Comparator.comparingInt(RecipeStep::getStepNumber));
+ Recipe recipe = new Recipe(name, List.copyOf(ingredients), List.copyOf(recipeSteps));
+ return new RecipeAddCommand(recipe);
+ }
+
+ // Method unused. Each line must be parsed in sequence to detect parsing errors on a line-by-line basis.
+
+ @Override
+ public RecipeAddCommand parse(String args) throws ParseException {
+ String[] lines = args.split("\\r?\\n");
+
+ Name name = new Name(lines[0]);
+ List ingredientStrings = new ArrayList<>();
+ List stepStrings = new ArrayList<>();
+
+ boolean stepstarted = false;
+ for (int i = 1; i < lines.length; i++) {
+ if (lines[i].equals("steps start") || lines[i].equals("step start")) {
+ stepstarted = true;
+ } else if (stepstarted) {
+ stepStrings.add(lines[i]);
+ } else {
+ ingredientStrings.add(lines[i]);
+ }
+ }
+
+ System.out.println(args);
+ List ingredientList = new ArrayList<>();
+ List stepList = new ArrayList<>();
+ for (String ingredientString : ingredientStrings) {
+ ingredientList.add(ParserUtil.parseRecipeIngredient(ingredientString));
+ }
+ for (String recipeString : stepStrings) {
+ stepList.add(ParserUtil.parseRecipeStep(recipeString));
+ }
+
+ stepList.sort(Comparator.comparingInt(RecipeStep::getStepNumber));
+ Recipe recipe = new Recipe(name, ingredientList, stepList);
+
+ return new RecipeAddCommand(recipe);
+ }
+
+ /**
+ * Reset the parser for a new recipe to add.
+ */
+ public void reset() {
+ ingredients.clear();
+ recipeSteps.clear();
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/RecipeViewCommandParser.java b/src/main/java/seedu/address/logic/parser/RecipeViewCommandParser.java
new file mode 100644
index 00000000000..059a4749ce4
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/RecipeViewCommandParser.java
@@ -0,0 +1,37 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_RECIPE_DISPLAYED_INDEX;
+
+import seedu.address.logic.commands.RecipeViewCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.recipe.RecipeUuidMatchesPredicate;
+import seedu.address.model.recipe.UniqueId;
+
+/**
+ * Parser for the RecipeViewCommand, returns a RecipeViewCommand
+ */
+public class RecipeViewCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the StockCommand
+ * and returns a StockCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public RecipeViewCommand parse(String args) throws ParseException {
+ int id;
+ String trimmedArgs = args.trim();
+ try {
+ id = Integer.parseInt(trimmedArgs);
+ } catch (NumberFormatException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, RecipeViewCommand.MESSAGE_USAGE), pe);
+ }
+
+ if (id <= 0) {
+ throw new ParseException(MESSAGE_INVALID_RECIPE_DISPLAYED_INDEX);
+ }
+
+ return new RecipeViewCommand(new RecipeUuidMatchesPredicate(new UniqueId(id)));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/SearchCommandParser.java b/src/main/java/seedu/address/logic/parser/SearchCommandParser.java
new file mode 100644
index 00000000000..ce0f6a64f7a
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/SearchCommandParser.java
@@ -0,0 +1,26 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.logic.commands.SearchCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.recipe.RecipeIngredientNameMatchesPredicate;
+
+/**
+ * Parser for the RecipeViewCommand, returns a RecipeViewCommand
+ */
+public class SearchCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the StockCommand
+ * and returns a StockCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public SearchCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SearchCommand.MESSAGE_USAGE));
+ }
+ return new SearchCommand(new RecipeIngredientNameMatchesPredicate(trimmedArgs));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/StockCommandParser.java b/src/main/java/seedu/address/logic/parser/StockCommandParser.java
new file mode 100644
index 00000000000..8a6edd4cb44
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/StockCommandParser.java
@@ -0,0 +1,34 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_INGREDIENTS;
+
+import java.util.Arrays;
+
+import seedu.address.logic.commands.StockCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.ingredient.NameContainsKeywordsPredicate;
+
+// Todo Add JavaDocs
+/**
+ * Stub
+ */
+public class StockCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the StockCommand
+ * and returns a StockCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public StockCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ // show the entire stock for when there are no arguments - check!
+ if (trimmedArgs.isEmpty()) {
+ return new StockCommand(PREDICATE_SHOW_ALL_INGREDIENTS);
+ }
+
+ String[] nameKeywords = trimmedArgs.split("\\s+");
+
+ return new StockCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/UseCommandParser.java b/src/main/java/seedu/address/logic/parser/UseCommandParser.java
new file mode 100644
index 00000000000..1f2dd20687f
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/UseCommandParser.java
@@ -0,0 +1,71 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_UNIT;
+
+import java.util.stream.Stream;
+
+import seedu.address.logic.commands.UseCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.Name;
+import seedu.address.model.ingredient.Quantity;
+import seedu.address.model.ingredient.Unit;
+import seedu.address.model.ingredient.exceptions.UnitFormatException;
+
+/**
+ * Parses input arguments and creates a new AddCommand object
+ */
+public class UseCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the UseCommand
+ * and returns an UseCommand object for execution.
+ * Currently only works for when quantity is specified
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public UseCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_UNIT);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UseCommand.MESSAGE_USAGE));
+ }
+
+ if ((!arePrefixesPresent(argMultimap, PREFIX_QUANTITY) && arePrefixesPresent(argMultimap, PREFIX_UNIT))
+ || (!arePrefixesPresent(argMultimap, PREFIX_UNIT)
+ && arePrefixesPresent(argMultimap, PREFIX_QUANTITY))) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UseCommand.MESSAGE_USAGE));
+ }
+
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_QUANTITY, PREFIX_UNIT);
+
+ Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_QUANTITY, PREFIX_UNIT)) {
+ return new UseCommand(name, null);
+ }
+
+ Unit unit;
+ try {
+ unit = ParserUtil.parseUnitOfIngredient(argMultimap.getValue(PREFIX_UNIT).get());
+ } catch (UnitFormatException e) {
+ throw new ParseException("This is not a valid unit!");
+ }
+
+ Quantity quantity = ParserUtil.parseQuantity(argMultimap.getValue(PREFIX_QUANTITY).get(), unit);
+
+ return new UseCommand(name, quantity);
+ }
+
+ /**
+ * Returns true if none of the prefixes contains empty {@code Optional} values in the given
+ * {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
deleted file mode 100644
index 73397161e84..00000000000
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package seedu.address.model;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.List;
-
-import javafx.collections.ObservableList;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.UniquePersonList;
-
-/**
- * Wraps all data at the address-book level
- * Duplicates are not allowed (by .isSamePerson comparison)
- */
-public class AddressBook implements ReadOnlyAddressBook {
-
- private final UniquePersonList persons;
-
- /*
- * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
- * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
- *
- * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication
- * among constructors.
- */
- {
- persons = new UniquePersonList();
- }
-
- public AddressBook() {}
-
- /**
- * Creates an AddressBook using the Persons in the {@code toBeCopied}
- */
- public AddressBook(ReadOnlyAddressBook toBeCopied) {
- this();
- resetData(toBeCopied);
- }
-
- //// list overwrite operations
-
- /**
- * Replaces the contents of the person list with {@code persons}.
- * {@code persons} must not contain duplicate persons.
- */
- public void setPersons(List persons) {
- this.persons.setPersons(persons);
- }
-
- /**
- * Resets the existing data of this {@code AddressBook} with {@code newData}.
- */
- public void resetData(ReadOnlyAddressBook newData) {
- requireNonNull(newData);
-
- setPersons(newData.getPersonList());
- }
-
- //// person-level operations
-
- /**
- * Returns true if a person with the same identity as {@code person} exists in the address book.
- */
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return persons.contains(person);
- }
-
- /**
- * Adds a person to the address book.
- * The person must not already exist in the address book.
- */
- public void addPerson(Person p) {
- persons.add(p);
- }
-
- /**
- * Replaces the given person {@code target} in the list with {@code editedPerson}.
- * {@code target} must exist in the address book.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the address book.
- */
- public void setPerson(Person target, Person editedPerson) {
- requireNonNull(editedPerson);
-
- persons.setPerson(target, editedPerson);
- }
-
- /**
- * Removes {@code key} from this {@code AddressBook}.
- * {@code key} must exist in the address book.
- */
- public void removePerson(Person key) {
- persons.remove(key);
- }
-
- //// util methods
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("persons", persons)
- .toString();
- }
-
- @Override
- public ObservableList getPersonList() {
- return persons.asUnmodifiableObservableList();
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof AddressBook)) {
- return false;
- }
-
- AddressBook otherAddressBook = (AddressBook) other;
- return persons.equals(otherAddressBook.persons);
- }
-
- @Override
- public int hashCode() {
- return persons.hashCode();
- }
-}
diff --git a/src/main/java/seedu/address/model/Inventory.java b/src/main/java/seedu/address/model/Inventory.java
new file mode 100644
index 00000000000..a6ce3e25a57
--- /dev/null
+++ b/src/main/java/seedu/address/model/Inventory.java
@@ -0,0 +1,179 @@
+package seedu.address.model;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.List;
+
+import javafx.collections.ObservableList;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.ingredient.Quantity;
+import seedu.address.model.ingredient.UniqueIngredientList;
+import seedu.address.model.ingredient.Unit;
+import seedu.address.model.ingredient.exceptions.IngredientNotFoundException;
+
+/**
+ * Wraps all data at the address-book level
+ * Duplicates are not allowed (by .isSameIngredient comparison)
+ */
+public class Inventory implements ReadOnlyInventory {
+
+ private final UniqueIngredientList ingredients;
+
+ /*
+ * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
+ * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
+ *
+ * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication
+ * among constructors.
+ */
+ {
+ ingredients = new UniqueIngredientList();
+ }
+
+ public Inventory() {}
+
+ /**
+ * Creates an Inventory using the Ingredients in the {@code toBeCopied}
+ */
+ public Inventory(ReadOnlyInventory toBeCopied) {
+ this();
+ resetData(toBeCopied);
+ }
+
+ //// list overwrite operations
+
+ /**
+ * Replaces the contents of the ingredient list with {@code ingredients}.
+ * {@code ingredients} must not contain duplicate ingredients.
+ */
+ public void setIngredients(List ingredients) {
+ this.ingredients.setIngredients(ingredients);
+ }
+
+ /**
+ * Resets the existing data of this {@code Inventory} with {@code newData}.
+ */
+ public void resetData(ReadOnlyInventory newData) {
+ requireNonNull(newData);
+ setIngredients(newData.getIngredientList());
+ }
+
+ //// ingredient-level operations
+
+ /**
+ * Returns true if a ingredient with the same identity as {@code ingredient} exists in the inventory.
+ */
+ public boolean hasIngredient(Name ingredientName) {
+ requireNonNull(ingredientName);
+
+ for (Ingredient ingredient : ingredients) {
+ if (ingredient.getName().equals(ingredientName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Quantity getQuantityOf(Name ingredientName) {
+ requireNonNull(ingredientName);
+ for (Ingredient ingredient : ingredients) {
+ if (ingredient.getName().equals(ingredientName)) {
+ return ingredient.getQuantity();
+ }
+ }
+ //If ingredient can't be found, then return 0 quantity
+ return new Quantity(0, Unit.GRAM);
+ }
+
+ /**
+ * Adds a ingredient to the inventory.
+ * The ingredient must not already exist in the inventory.
+ */
+ public void addIngredient(Ingredient p) {
+ if (ingredients.contains(p)) {
+ for (Ingredient ingredient : ingredients) {
+ if (ingredient.getName().equals(p.getName())) {
+ ingredients.replace(ingredient, ingredient.combineWith(p));
+ return;
+ }
+ }
+ } else {
+ ingredients.add(p);
+ }
+ }
+
+ /**
+ * Replaces the given ingredient {@code target} in the list with {@code editedIngredient}.
+ * {@code target} must exist in the inventory.
+ * The ingredient identity of {@code editedIngredient} must not be the same as
+ * another existing ingredient in the inventory.
+ */
+ public void useIngredient(Name ingredientName, Quantity quantity) {
+ requireAllNonNull(ingredientName, quantity);
+ for (Ingredient ingredient : ingredients) {
+ if (ingredient.getName().equals(ingredientName)) {
+ if (ingredient.getQuantity().isLessThanOrEqualsTo(quantity)) {
+ ingredients.remove(ingredient);
+ } else {
+ ingredients.replace(ingredient, ingredient.use(quantity));
+ }
+ return;
+ }
+ }
+ throw new IngredientNotFoundException();
+ }
+
+ /**
+ * Removes {@code key} from this {@code Inventory}.
+ * {@code key} must exist in the inventory.
+ */
+ public void removeIngredient(Name ingredientName) {
+ for (Ingredient ingredient : ingredients) {
+ if (ingredient.getName().equals(ingredientName)) {
+ removeIngredient(ingredientName);
+ break;
+ }
+ }
+ throw new IngredientNotFoundException();
+ }
+
+ public void clear() {
+ ingredients.clear();
+ }
+ //// util methods
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("ingredients", ingredients)
+ .toString();
+ }
+
+ @Override
+ public ObservableList getIngredientList() {
+ return ingredients.asUnmodifiableObservableList();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Inventory)) {
+ return false;
+ }
+
+ Inventory otherInventory = (Inventory) other;
+ return ingredients.equals(otherInventory.ingredients);
+ }
+
+ @Override
+ public int hashCode() {
+ return ingredients.hashCode();
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..9aa59d7e485 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -5,14 +5,19 @@
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
-import seedu.address.model.person.Person;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.ingredient.Quantity;
+import seedu.address.model.recipe.Recipe;
+import seedu.address.model.recipe.UniqueId;
+import seedu.address.model.recipe.exceptions.RecipeNotFoundException;
/**
* The API of the Model component.
*/
public interface Model {
/** {@code Predicate} that always evaluate to true */
- Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true;
+ Predicate PREDICATE_SHOW_ALL_INGREDIENTS = unused -> true;
+ Predicate PREDICATE_SHOW_ALL_RECIPES = unused -> true;
/**
* Replaces user prefs data with the data in {@code userPrefs}.
@@ -35,53 +40,79 @@ public interface Model {
void setGuiSettings(GuiSettings guiSettings);
/**
- * Returns the user prefs' address book file path.
+ * Returns the user prefs' inventory file path.
*/
- Path getAddressBookFilePath();
+ Path getInventoryFilePath();
/**
- * Sets the user prefs' address book file path.
+ * Sets the user prefs' inventory file path.
*/
- void setAddressBookFilePath(Path addressBookFilePath);
+ void setInventoryFilePath(Path inventoryFilePath);
/**
- * Replaces address book data with the data in {@code addressBook}.
+ * Replaces inventory data with the data in {@code inventory}.
*/
- void setAddressBook(ReadOnlyAddressBook addressBook);
+ void setInventory(ReadOnlyInventory inventory);
- /** Returns the AddressBook */
- ReadOnlyAddressBook getAddressBook();
+ /** Returns the Inventory */
+ ReadOnlyInventory getInventory();
+
+ boolean hasIngredient(Name ingredientName);
+
+ Quantity getQuantityOf(Name ingredientName);
+
+ void deleteIngredient(Name ingredientName);
+
+ void deleteIngredients();
/**
- * Returns true if a person with the same identity as {@code person} exists in the address book.
+ * Adds the given ingredient.
+ * {@code ingredient} must not already exist in the inventory.
*/
- boolean hasPerson(Person person);
+ void addIngredient(Ingredient ingredient);
+
+
+ void useIngredient(Name ingredientName, Quantity quantity);
+
+ /** Returns an unmodifiable view of the filtered ingredient list */
+ ObservableList getFilteredIngredientList();
/**
- * Deletes the given person.
- * The person must exist in the address book.
+ * Updates the filter of the filtered ingredient list to filter by the given {@code predicate}.
+ * @throws NullPointerException if {@code predicate} is null.
*/
- void deletePerson(Person target);
+ void updateFilteredIngredientList(Predicate predicate);
/**
- * Adds the given person.
- * {@code person} must not already exist in the address book.
+ * Replaces recipe data with the data in {@code recipeBook}.
*/
- void addPerson(Person person);
+ void setRecipeBook(ReadOnlyRecipeBook recipeBook);
+
+ /** Returns the RecipeBook */
+ ReadOnlyRecipeBook getRecipeBook();
+
+ //boolean hasRecipe(Name recipeName);
+
+ boolean hasRecipe(UniqueId uuid);
+
+ void deleteRecipe(Recipe recipe);
+
+ void addRecipe(Recipe recipe);
+
+ Recipe getRecipe(UniqueId uuid);
/**
- * Replaces the given person {@code target} with {@code editedPerson}.
- * {@code target} must exist in the address book.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the address book.
+ * Gets the full Recipe based on the unique {@code recipeId} of the Recipe.
+ * @throws RecipeNotFoundException if {@code recipe} is not found.
*/
- void setPerson(Person target, Person editedPerson);
+ String getFullRecipe(int recipeId);
- /** Returns an unmodifiable view of the filtered person list */
- ObservableList getFilteredPersonList();
+ /** Returns an unmodifiable view of the filtered recipe list */
+ ObservableList getFilteredRecipeList();
/**
- * Updates the filter of the filtered person list to filter by the given {@code predicate}.
+ * Updates the filter of the filtered recipe list to filter by the given {@code predicate}.
* @throws NullPointerException if {@code predicate} is null.
*/
- void updateFilteredPersonList(Predicate predicate);
+ void updateFilteredRecipeList(Predicate predicate);
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 57bc563fde6..36a25c3cb10 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -11,33 +11,39 @@
import javafx.collections.transformation.FilteredList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
-import seedu.address.model.person.Person;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.ingredient.Quantity;
+import seedu.address.model.recipe.Recipe;
+import seedu.address.model.recipe.UniqueId;
/**
- * Represents the in-memory model of the address book data.
+ * Represents the in-memory model of the inventory data.
*/
public class ModelManager implements Model {
private static final Logger logger = LogsCenter.getLogger(ModelManager.class);
-
- private final AddressBook addressBook;
+ private final Inventory inventory;
private final UserPrefs userPrefs;
- private final FilteredList filteredPersons;
+ private final RecipeBook recipeBook;
+ private final FilteredList filteredIngredients;
+ private final FilteredList filteredRecipe;
/**
- * Initializes a ModelManager with the given addressBook and userPrefs.
+ * Initializes a ModelManager with the given inventory and userPrefs.
*/
- public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) {
- requireAllNonNull(addressBook, userPrefs);
+ public ModelManager(ReadOnlyInventory inventory, ReadOnlyUserPrefs userPrefs, ReadOnlyRecipeBook recipeBook) {
+ requireAllNonNull(inventory, userPrefs, recipeBook);
- logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs);
+ logger.fine("Initializing with inventory: " + inventory + " and user prefs " + userPrefs);
- this.addressBook = new AddressBook(addressBook);
+ this.inventory = new Inventory(inventory);
this.userPrefs = new UserPrefs(userPrefs);
- filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
+ this.recipeBook = new RecipeBook(recipeBook);
+ filteredIngredients = new FilteredList<>(this.inventory.getIngredientList());
+ filteredRecipe = new FilteredList<>(this.recipeBook.getRecipeList());
}
public ModelManager() {
- this(new AddressBook(), new UserPrefs());
+ this(new Inventory(), new UserPrefs(), new RecipeBook());
}
//=========== UserPrefs ==================================================================================
@@ -65,67 +71,128 @@ public void setGuiSettings(GuiSettings guiSettings) {
}
@Override
- public Path getAddressBookFilePath() {
- return userPrefs.getAddressBookFilePath();
+ public Path getInventoryFilePath() {
+ return userPrefs.getInventoryFilePath();
+ }
+
+ @Override
+ public void setInventoryFilePath(Path inventoryFilePath) {
+ requireNonNull(inventoryFilePath);
+ userPrefs.setInventoryFilePath(inventoryFilePath);
+ }
+
+ //=========== StockBook ================================================================================
+
+ @Override
+ public void setInventory(ReadOnlyInventory stock) {
+ this.inventory.resetData(stock);
+ }
+
+ @Override
+ public ReadOnlyInventory getInventory() {
+ return inventory;
+ }
+
+ @Override
+ public boolean hasIngredient(Name ingredientName) {
+ requireNonNull(ingredientName);
+
+ return inventory.hasIngredient(ingredientName);
+ }
+
+ @Override
+ public Quantity getQuantityOf(Name ingredientName) {
+ requireNonNull(ingredientName);
+ return inventory.getQuantityOf(ingredientName);
}
@Override
- public void setAddressBookFilePath(Path addressBookFilePath) {
- requireNonNull(addressBookFilePath);
- userPrefs.setAddressBookFilePath(addressBookFilePath);
+ public void deleteIngredient(Name ingredientName) {
+ inventory.removeIngredient(ingredientName);
}
- //=========== AddressBook ================================================================================
+ @Override
+ public void deleteIngredients() {
+ inventory.clear();
+ }
+ @Override
+ public void addIngredient(Ingredient ingredient) {
+ inventory.addIngredient(ingredient);
+ updateFilteredIngredientList(PREDICATE_SHOW_ALL_INGREDIENTS);
+ }
@Override
- public void setAddressBook(ReadOnlyAddressBook addressBook) {
- this.addressBook.resetData(addressBook);
+ public void useIngredient(Name ingredientName, Quantity quantity) {
+ requireAllNonNull(ingredientName, quantity);
+ inventory.useIngredient(ingredientName, quantity);
+ updateFilteredIngredientList(PREDICATE_SHOW_ALL_INGREDIENTS);
}
+ //=========== RecipeBook =============================================================
+
@Override
- public ReadOnlyAddressBook getAddressBook() {
- return addressBook;
+ public void setRecipeBook(ReadOnlyRecipeBook recipeBook) {
+ requireNonNull(recipeBook);
+ this.recipeBook.resetData(recipeBook);
}
@Override
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return addressBook.hasPerson(person);
+ public ReadOnlyRecipeBook getRecipeBook() {
+ return this.recipeBook;
}
@Override
- public void deletePerson(Person target) {
- addressBook.removePerson(target);
+ public boolean hasRecipe(UniqueId uuid) {
+ requireNonNull(uuid);
+ return recipeBook.hasRecipe(uuid);
}
@Override
- public void addPerson(Person person) {
- addressBook.addPerson(person);
- updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ public void deleteRecipe(Recipe recipe) {
+ this.recipeBook.removeRecipe(recipe);
+ }
+ @Override
+ public void addRecipe(Recipe recipe) {
+ requireNonNull(recipe);
+ this.recipeBook.addRecipe(recipe);
}
@Override
- public void setPerson(Person target, Person editedPerson) {
- requireAllNonNull(target, editedPerson);
+ public Recipe getRecipe(UniqueId uuid) {
+ return this.recipeBook.getRecipe(uuid);
+ }
- addressBook.setPerson(target, editedPerson);
+ @Override
+ public String getFullRecipe(int recipeId) {
+ return this.recipeBook.getFullRecipe(recipeId);
}
- //=========== Filtered Person List Accessors =============================================================
+ //=========== Filtered Ingredient List Accessors =============================================================
/**
- * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of
- * {@code versionedAddressBook}
+ * Returns an unmodifiable view of the list of {@code Ingredient} backed by the internal list of
+ * {@code versionedInventory}
*/
@Override
- public ObservableList getFilteredPersonList() {
- return filteredPersons;
+ public ObservableList getFilteredIngredientList() {
+ return filteredIngredients;
+ }
+
+ @Override
+ public void updateFilteredIngredientList(Predicate predicate) {
+ requireNonNull(predicate);
+ filteredIngredients.setPredicate(predicate);
+ }
+
+ @Override
+ public ObservableList getFilteredRecipeList() {
+ return filteredRecipe;
}
@Override
- public void updateFilteredPersonList(Predicate predicate) {
+ public void updateFilteredRecipeList(Predicate predicate) {
requireNonNull(predicate);
- filteredPersons.setPredicate(predicate);
+ this.filteredRecipe.setPredicate(predicate);
}
@Override
@@ -140,9 +207,11 @@ public boolean equals(Object other) {
}
ModelManager otherModelManager = (ModelManager) other;
- return addressBook.equals(otherModelManager.addressBook)
+ return inventory.equals(otherModelManager.inventory)
&& userPrefs.equals(otherModelManager.userPrefs)
- && filteredPersons.equals(otherModelManager.filteredPersons);
+ && recipeBook.equals(otherModelManager.recipeBook)
+ && filteredIngredients.equals(otherModelManager.filteredIngredients)
+ && filteredRecipe.equals(otherModelManager.filteredRecipe);
}
}
diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/Name.java
similarity index 87%
rename from src/main/java/seedu/address/model/person/Name.java
rename to src/main/java/seedu/address/model/Name.java
index 173f15b9b00..29096b43feb 100644
--- a/src/main/java/seedu/address/model/person/Name.java
+++ b/src/main/java/seedu/address/model/Name.java
@@ -1,10 +1,10 @@
-package seedu.address.model.person;
+package seedu.address.model;
import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.AppUtil.checkArgument;
/**
- * Represents a Person's name in the address book.
+ * Represents an Ingredient's name in the inventory.
* Guarantees: immutable; is valid as declared in {@link #isValidName(String)}
*/
public class Name {
@@ -56,7 +56,8 @@ public boolean equals(Object other) {
}
Name otherName = (Name) other;
- return fullName.equals(otherName.fullName);
+ //TODO Change to case-insensitive comparison
+ return fullName.toLowerCase().equals(otherName.fullName.toLowerCase());
}
@Override
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
deleted file mode 100644
index 6ddc2cd9a29..00000000000
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package seedu.address.model;
-
-import javafx.collections.ObservableList;
-import seedu.address.model.person.Person;
-
-/**
- * Unmodifiable view of an address book
- */
-public interface ReadOnlyAddressBook {
-
- /**
- * Returns an unmodifiable view of the persons list.
- * This list will not contain any duplicate persons.
- */
- ObservableList getPersonList();
-
-}
diff --git a/src/main/java/seedu/address/model/ReadOnlyInventory.java b/src/main/java/seedu/address/model/ReadOnlyInventory.java
new file mode 100644
index 00000000000..0bb3f7d5b0a
--- /dev/null
+++ b/src/main/java/seedu/address/model/ReadOnlyInventory.java
@@ -0,0 +1,17 @@
+package seedu.address.model;
+
+import javafx.collections.ObservableList;
+import seedu.address.model.ingredient.Ingredient;
+
+/**
+ * Unmodifiable view of an inventory
+ */
+public interface ReadOnlyInventory {
+
+ /**
+ * Returns an unmodifiable view of the ingredients list.
+ * This list will not contain any duplicate ingredients.
+ */
+ ObservableList getIngredientList();
+
+}
diff --git a/src/main/java/seedu/address/model/ReadOnlyRecipeBook.java b/src/main/java/seedu/address/model/ReadOnlyRecipeBook.java
new file mode 100644
index 00000000000..77585d52431
--- /dev/null
+++ b/src/main/java/seedu/address/model/ReadOnlyRecipeBook.java
@@ -0,0 +1,15 @@
+package seedu.address.model;
+
+import javafx.collections.ObservableList;
+import seedu.address.model.recipe.Recipe;
+
+/**
+ * Unmodifiable view of the recipe book.
+ */
+public interface ReadOnlyRecipeBook {
+ /**
+ * Returns an unmodifiable view of the recipe list.
+ * This list will not contain any duplicate recipes.
+ */
+ ObservableList getRecipeList();
+}
diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java
index befd58a4c73..baceeaef03b 100644
--- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java
+++ b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java
@@ -11,6 +11,8 @@ public interface ReadOnlyUserPrefs {
GuiSettings getGuiSettings();
- Path getAddressBookFilePath();
+ Path getInventoryFilePath();
+
+ Path getRecipeBookFilePath();
}
diff --git a/src/main/java/seedu/address/model/RecipeBook.java b/src/main/java/seedu/address/model/RecipeBook.java
new file mode 100644
index 00000000000..a6ef70344d2
--- /dev/null
+++ b/src/main/java/seedu/address/model/RecipeBook.java
@@ -0,0 +1,137 @@
+package seedu.address.model;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import javafx.collections.ObservableList;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.recipe.Recipe;
+import seedu.address.model.recipe.RecipeList;
+import seedu.address.model.recipe.UniqueId;
+import seedu.address.model.recipe.exceptions.RecipeNotFoundException;
+
+/**
+ * Wraps all recipe data at the recipe book level
+ */
+public class RecipeBook implements ReadOnlyRecipeBook {
+ private final RecipeList recipeList;
+
+ {
+ recipeList = new RecipeList();
+ }
+
+ public RecipeBook() {
+
+ }
+
+ /**
+ * Creates a new recipe book with the specified {@code recipeBook}
+ */
+ public RecipeBook(ReadOnlyRecipeBook recipeBook) {
+ this();
+ resetData(recipeBook);
+ }
+
+ /**
+ * Replaces the contents of the ingredient list with {@code ingredients}.
+ * {@code ingredients} must not contain duplicate ingredients.
+ */
+ public void setRecipes(List recipes) {
+ this.recipeList.setRecipes(recipes);
+ }
+
+ /**
+ * Resets the existing data of this {@code Inventory} with {@code newData}.
+ */
+ public void resetData(ReadOnlyRecipeBook newData) {
+ requireNonNull(newData);
+ setRecipes(newData.getRecipeList());
+ }
+
+ /**
+ * Checks if the recipe exists in the recipe book.
+ */
+ public boolean hasRecipe(UniqueId uuid) {
+ requireNonNull(uuid);
+ for (Recipe recipe : this.recipeList) {
+ if (recipe.getUuid().equals(uuid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Adds a new {@code recipe} to the recipe book.
+ */
+ public void addRecipe(Recipe toAdd) {
+ recipeList.add(toAdd);
+ }
+
+ /**
+ * Removes a recipe from the recipe book
+ */
+ public void removeRecipe(Recipe recipe) {
+ try {
+ this.recipeList.remove(recipe);
+ } catch (RecipeNotFoundException e) {
+ throw new RecipeNotFoundException();
+ }
+ }
+
+ /**
+ * Clears the recipe list
+ */
+ public void clear() {
+ this.recipeList.clear();
+ }
+
+ /**
+ * Gets the full recipe of the specified {@code recipeId}
+ */
+ public String getFullRecipe(int recipeId) {
+ return this.recipeList.getFullRecipe(recipeId);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("recipe", this.recipeList)
+ .toString();
+ }
+
+ @Override
+ public ObservableList getRecipeList() {
+ return recipeList.asUnmodifiableObservableList();
+ }
+
+ public Recipe getRecipe(UniqueId uuid) {
+ for (Recipe recipe : this.recipeList) {
+ if (recipe.getUuid().equals(uuid)) {
+ return recipe;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof RecipeBook)) {
+ return false;
+ }
+
+ RecipeBook otherRecipeBook = (RecipeBook) other;
+ return recipeList.equals(otherRecipeBook.recipeList);
+ }
+
+ @Override
+ public int hashCode() {
+ return recipeList.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java
index 6be655fb4c7..edab49d554e 100644
--- a/src/main/java/seedu/address/model/UserPrefs.java
+++ b/src/main/java/seedu/address/model/UserPrefs.java
@@ -14,7 +14,8 @@
public class UserPrefs implements ReadOnlyUserPrefs {
private GuiSettings guiSettings = new GuiSettings();
- private Path addressBookFilePath = Paths.get("data" , "addressbook.json");
+ private Path inventoryFilePath = Paths.get("data" , "inventory.json");
+ private Path recipeBookFilePath = Paths.get("data", "recipeBook.json");
/**
* Creates a {@code UserPrefs} with default values.
@@ -35,7 +36,7 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) {
public void resetData(ReadOnlyUserPrefs newUserPrefs) {
requireNonNull(newUserPrefs);
setGuiSettings(newUserPrefs.getGuiSettings());
- setAddressBookFilePath(newUserPrefs.getAddressBookFilePath());
+ setInventoryFilePath(newUserPrefs.getInventoryFilePath());
}
public GuiSettings getGuiSettings() {
@@ -47,13 +48,22 @@ public void setGuiSettings(GuiSettings guiSettings) {
this.guiSettings = guiSettings;
}
- public Path getAddressBookFilePath() {
- return addressBookFilePath;
+ public Path getInventoryFilePath() {
+ return inventoryFilePath;
}
- public void setAddressBookFilePath(Path addressBookFilePath) {
- requireNonNull(addressBookFilePath);
- this.addressBookFilePath = addressBookFilePath;
+ public void setInventoryFilePath(Path inventoryFilePath) {
+ requireNonNull(inventoryFilePath);
+ this.inventoryFilePath = inventoryFilePath;
+ }
+
+ public Path getRecipeBookFilePath() {
+ return recipeBookFilePath;
+ }
+
+ public void setRecipeBookFilePath(Path recipeBookFilePath) {
+ requireNonNull(recipeBookFilePath);
+ this.recipeBookFilePath = recipeBookFilePath;
}
@Override
@@ -69,19 +79,19 @@ public boolean equals(Object other) {
UserPrefs otherUserPrefs = (UserPrefs) other;
return guiSettings.equals(otherUserPrefs.guiSettings)
- && addressBookFilePath.equals(otherUserPrefs.addressBookFilePath);
+ && inventoryFilePath.equals(otherUserPrefs.inventoryFilePath);
}
@Override
public int hashCode() {
- return Objects.hash(guiSettings, addressBookFilePath);
+ return Objects.hash(guiSettings, inventoryFilePath);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Gui Settings : " + guiSettings);
- sb.append("\nLocal data file location : " + addressBookFilePath);
+ sb.append("\nLocal data file location : " + inventoryFilePath);
return sb.toString();
}
diff --git a/src/main/java/seedu/address/model/ingredient/Ingredient.java b/src/main/java/seedu/address/model/ingredient/Ingredient.java
new file mode 100644
index 00000000000..484c743cd0f
--- /dev/null
+++ b/src/main/java/seedu/address/model/ingredient/Ingredient.java
@@ -0,0 +1,103 @@
+package seedu.address.model.ingredient;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Objects;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.Name;
+
+/**
+ * Represents a Ingredient in the inventory.
+ * Guarantees: details are present and not null, field values are validated, immutable.
+ */
+public class Ingredient {
+
+ // Identity fields
+ private final Name name;
+ private Quantity quantity;
+
+ /**
+ * Every field must be present and not null.
+ */
+ public Ingredient(Name name, Quantity quantity) {
+ // requireAllNonNull(name, phone, email, address, tags);
+ requireAllNonNull(name);
+ this.name = name;
+ this.quantity = quantity;
+ }
+
+ /**
+ * Get the name of this ingredient.
+ * @return
+ */
+ public Name getName() {
+ return name;
+ }
+
+ /**
+ * Get the quantity of this ingredient.
+ * @return
+ */
+ public Quantity getQuantity() {
+ return quantity;
+ }
+
+ /**
+ * Combines another ingredient of the same type with this ingredient, combining their quantities .
+ * @param otherIngredient
+ */
+ public Ingredient combineWith(Ingredient otherIngredient) {
+ //otherIngredient is guaranteed to be of the same type as this ingredient.
+ return new Ingredient(name, this.quantity.add(otherIngredient.quantity));
+ }
+
+ /**
+ * Returns true if both ingredients have the same name.
+ * This defines a weaker notion of equality between two ingredients.
+ */
+ public boolean isSameIngredient(Ingredient otherIngredient) {
+ if (otherIngredient.equals(this)) {
+ return true;
+ }
+
+ return otherIngredient != null
+ && otherIngredient.getName().equals(getName());
+ }
+
+ /**
+ * Returns true if both ingredients have the same identity and data fields.
+ * This defines a stronger notion of equality between two ingredients.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Ingredient)) {
+ return false;
+ }
+
+ Ingredient otherIngredient = (Ingredient) other;
+ return name.equals(otherIngredient.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("name", name)
+ .add("quantity", quantity)
+ .toString();
+ }
+
+ public Ingredient use(Quantity quantity) {
+ return new Ingredient(name, this.quantity.subtract(quantity));
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/ingredient/NameContainsKeywordsPredicate.java
similarity index 82%
rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
rename to src/main/java/seedu/address/model/ingredient/NameContainsKeywordsPredicate.java
index 62d19be2977..dcbc9de2ed5 100644
--- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
+++ b/src/main/java/seedu/address/model/ingredient/NameContainsKeywordsPredicate.java
@@ -1,4 +1,4 @@
-package seedu.address.model.person;
+package seedu.address.model.ingredient;
import java.util.List;
import java.util.function.Predicate;
@@ -7,9 +7,9 @@
import seedu.address.commons.util.ToStringBuilder;
/**
- * Tests that a {@code Person}'s {@code Name} matches any of the keywords given.
+ * Tests that a {@code Ingredient}'s {@code Name} matches any of the keywords given.
*/
-public class NameContainsKeywordsPredicate implements Predicate {
+public class NameContainsKeywordsPredicate implements Predicate {
private final List keywords;
public NameContainsKeywordsPredicate(List keywords) {
@@ -17,9 +17,9 @@ public NameContainsKeywordsPredicate(List keywords) {
}
@Override
- public boolean test(Person person) {
+ public boolean test(Ingredient ingredient) {
return keywords.stream()
- .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword));
+ .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(ingredient.getName().fullName, keyword));
}
@Override
diff --git a/src/main/java/seedu/address/model/ingredient/Quantity.java b/src/main/java/seedu/address/model/ingredient/Quantity.java
new file mode 100644
index 00000000000..f66b9833a7f
--- /dev/null
+++ b/src/main/java/seedu/address/model/ingredient/Quantity.java
@@ -0,0 +1,109 @@
+package seedu.address.model.ingredient;
+
+// TODO Add JavaDocs
+/**
+ * Stub
+ */
+public class Quantity {
+ private double value;
+ private Unit unit;
+
+ /**
+ * Constructs a Quantity instance.
+ * @param value The numerical value of the quantity.
+ * @param unit The unit of the quantity.
+ */
+ public Quantity(double value, Unit unit) {
+ this.value = value;
+ this.unit = unit;
+ }
+
+ public double getValue() {
+ return value;
+ }
+ /**
+ * Adds a quantity to this quantity.
+ * @param otherQuantity
+ */
+ protected Quantity add(Quantity otherQuantity) {
+ return new Quantity(value + otherQuantity.convertToUnit(unit).value, unit);
+ }
+
+ /**
+ * Subtracts a quantity from this quantity.
+ * @param quantity
+ */
+ protected Quantity subtract(Quantity quantity) {
+ return new Quantity(value - quantity.convertToUnit(unit).value, unit);
+ }
+
+ /**
+ * Converts this quantity to a specified unit.
+ * @param toUnit
+ * @return The converted quantity.
+ */
+ protected Quantity convertToUnit(Unit toUnit) {
+ return new Quantity(value * Unit.getConversionRatio(this.unit, toUnit), toUnit);
+ }
+
+ /**
+ * Scales this quantity by a factor.
+ * @param factor
+ * @return The scaled quantity.
+ */
+ protected Quantity scale(double factor) {
+ return new Quantity(value * factor, unit);
+ }
+
+ @Override
+ public String toString() {
+ return value + " " + unit.toString();
+ }
+
+ /**
+ * Parses the string argument as a Quantity.
+ * @param quantityString The string to be parsed.
+ * @return The parsed Quantity object.
+ */
+ public static Quantity parseQuantity(String quantityString) {
+ String[] splitStr = quantityString.split(" ");
+
+ double val = Double.parseDouble(splitStr[0]);
+ Unit unit = Unit.parseUnit(splitStr[1]);
+
+ return new Quantity(val, unit);
+ }
+
+ // TODO Add JavaDocs
+ /**
+ * Stub
+ * @param quantity Stub
+ * @return Stub
+ */
+ public boolean isLessThan(Quantity quantity) {
+ return value < quantity.convertToUnit(unit).value;
+ }
+
+ /**
+ * Returns whether the ingredient's quantity is less than or equal to the specified quantity
+ * @param quantity
+ * @return
+ */
+ public boolean isLessThanOrEqualsTo(Quantity quantity) {
+ return value <= quantity.convertToUnit(unit).value;
+ }
+
+ @Override
+ public boolean equals(Object otherQuantity) {
+ if (otherQuantity == this) {
+ return true;
+ }
+ if (otherQuantity instanceof Quantity) {
+ Quantity other = (Quantity) otherQuantity;
+ if (this.value == other.value && this.unit == other.unit) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/seedu/address/model/ingredient/UniqueIngredientList.java b/src/main/java/seedu/address/model/ingredient/UniqueIngredientList.java
new file mode 100644
index 00000000000..a88402b7f24
--- /dev/null
+++ b/src/main/java/seedu/address/model/ingredient/UniqueIngredientList.java
@@ -0,0 +1,149 @@
+package seedu.address.model.ingredient;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Iterator;
+import java.util.List;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.model.ingredient.exceptions.DuplicateIngredientException;
+import seedu.address.model.ingredient.exceptions.IngredientNotFoundException;
+
+/**
+ * A list of ingredients that enforces uniqueness between its elements and does not allow nulls.
+ * An ingredient is considered unique by comparing using {@code Ingredient#isSameIngredient(Ingredient)}.
+ * As such, adding and updating of ingredients uses Ingredient#isSameIngredient(Ingredient) for equality
+ * so as to ensure that the ingredient being added or updated is unique in terms of identity
+ * in the UniqueIngredientlist.
+ *
+ * Supports a minimal set of list operations.
+ *
+ * @see Ingredient#isSameIngredient(Ingredient)
+ */
+public class UniqueIngredientList implements Iterable {
+
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList =
+ FXCollections.unmodifiableObservableList(internalList);
+
+ /**
+ * Returns true if the list contains a specified ingredient as the given argument.
+ */
+ public boolean contains(Ingredient toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::isSameIngredient);
+ }
+
+ /**
+ * Adds an ingredient to the list.
+ * If the ingredient is already in the list, then add the quantity to the existing quantity.
+ * @param toAdd The ingredient to add to the list.
+ */
+ public void add(Ingredient toAdd) {
+ requireNonNull(toAdd);
+ internalList.add(toAdd);
+ }
+
+ /**
+ * Removes the equivalent ingredient from the list.
+ * The ingredient must exist in the list.
+ */
+ public void remove(Ingredient toRemove) {
+ requireNonNull(toRemove);
+ if (!internalList.remove(toRemove)) {
+ throw new IngredientNotFoundException();
+ }
+ }
+
+ // TODO Add JavaDocs
+ /**
+ * Stub
+ * @param toReplace Stub
+ * @param toAdd Stub
+ */
+ public void replace(Ingredient toReplace, Ingredient toAdd) {
+ requireAllNonNull(toReplace, toAdd);
+ int index = internalList.indexOf(toReplace);
+ internalList.remove(toReplace);
+ internalList.add(index, toAdd);
+ }
+
+ /**
+ * Empties the list.
+ */
+ public void clear() {
+ internalList.clear();
+ }
+
+
+ public void setIngredients(UniqueIngredientList replacement) {
+ requireNonNull(replacement);
+ internalList.setAll(replacement.internalList);
+ }
+
+ /**
+ * Replaces the contents of this list with {@code ingredients}.
+ * {@code ingredients} must not contain duplicate ingredients.
+ */
+ public void setIngredients(List ingredients) {
+ requireAllNonNull(ingredients);
+ if (!ingredientsAreUnique(ingredients)) {
+ throw new DuplicateIngredientException();
+ }
+
+ internalList.setAll(ingredients);
+ }
+
+ /**
+ * Returns the backing list as an unmodifiable {@code ObservableList}.
+ */
+ public ObservableList asUnmodifiableObservableList() {
+ return internalUnmodifiableList;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return internalList.iterator();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UniqueIngredientList)) {
+ return false;
+ }
+
+ UniqueIngredientList otherUniqueIngredientList = (UniqueIngredientList) other;
+ return internalList.equals(otherUniqueIngredientList.internalList);
+ }
+
+ @Override
+ public int hashCode() {
+ return internalList.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return internalList.toString();
+ }
+
+ /**
+ * Returns true if {@code ingredients} contains only unique ingredients.
+ */
+ private boolean ingredientsAreUnique(List ingredients) {
+ for (int i = 0; i < ingredients.size() - 1; i++) {
+ for (int j = i + 1; j < ingredients.size(); j++) {
+ if (ingredients.get(i).isSameIngredient(ingredients.get(j))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/seedu/address/model/ingredient/Unit.java b/src/main/java/seedu/address/model/ingredient/Unit.java
new file mode 100644
index 00000000000..632a0db15ae
--- /dev/null
+++ b/src/main/java/seedu/address/model/ingredient/Unit.java
@@ -0,0 +1,90 @@
+package seedu.address.model.ingredient;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import seedu.address.model.ingredient.exceptions.UnitConversionException;
+import seedu.address.model.ingredient.exceptions.UnitFormatException;
+
+// TODO Add JavaDocs
+/**
+ * Stub
+ */
+public enum Unit {
+ GRAM,
+ KILOGRAM,
+ PIECE;
+
+ public static final String MESSAGE_CONSTRAINTS = "Unit should be GRAM, KILOGRAM or PIECE";
+ //May need to update this to 3-dimensional matrix for different ingredient conversions.
+ private static final Map> conversionMatrix = new HashMap<>();
+ private static final Map unitAliases = new HashMap<>();
+
+ //Set the values of the conversion matrix.
+ static {
+ for (int i = 0; i < Unit.values().length; i++) {
+ //Set the diagonal of the conversion matrix to 1.
+ addConversionRatio(Unit.values()[i], Unit.values()[i], 1);
+ }
+ addConversionRatio(KILOGRAM, GRAM, 1000);
+ }
+
+ static {
+ addUnitAliases(new String[]{"g", "gram", "GRAM"}, GRAM);
+ addUnitAliases(new String[]{"kg", "kilogram", "KILOGRAM"}, KILOGRAM);
+ addUnitAliases(new String[]{"pc", "pcs", "piece", "pieces", "PIECE", "PIECES"}, PIECE);
+ }
+
+ /**
+ * Adds conversion ratio for a given pair of units, also adds the reciprocal for the reverse pair.
+ * @param from The unit to convert from.
+ * @param to The unit to convert to.
+ * @param ratio The ratio to multiply the value by.
+ */
+ private static void addConversionRatio(Unit from, Unit to, double ratio) {
+ conversionMatrix.computeIfAbsent(from, x -> new HashMap<>());
+ conversionMatrix.get(from).put(to, ratio);
+
+ //Assume commutative conversion for all units
+ conversionMatrix.computeIfAbsent(to, x -> new HashMap<>());
+ conversionMatrix.get(to).put(from, 1 / ratio);
+ }
+
+ private static void addUnitAliases(String[] aliases, Unit unit) {
+ for (String alias : aliases) {
+ unitAliases.put(alias, unit);
+ }
+ }
+
+ /**
+ * Get the conversion ratio between two units.
+ * @param from The unit to convert from.
+ * @param to The unit to convert to.
+ * @return The conversion ratio.
+ */
+ protected static double getConversionRatio(Unit from, Unit to) {
+ if (!conversionMatrix.get(from).containsKey(to)) {
+ throw new UnitConversionException(from, to);
+ }
+ return conversionMatrix.get(from).get(to);
+ }
+
+
+ @Override
+ public String toString() {
+ return name();
+ }
+
+ /**
+ * Parses the string argument as a Unit.
+ * @param str The string argument to be parsed.
+ * @return The parsed Unit.
+ */
+ public static Unit parseUnit(String str) {
+ if (unitAliases.containsKey(str)) {
+ return unitAliases.get(str);
+ } else {
+ throw new UnitFormatException(str);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/model/ingredient/exceptions/DuplicateIngredientException.java b/src/main/java/seedu/address/model/ingredient/exceptions/DuplicateIngredientException.java
new file mode 100644
index 00000000000..5db42053110
--- /dev/null
+++ b/src/main/java/seedu/address/model/ingredient/exceptions/DuplicateIngredientException.java
@@ -0,0 +1,11 @@
+package seedu.address.model.ingredient.exceptions;
+
+/**
+ * Signals that the operation will result in duplicate Ingredients
+ * (Ingredients are considered duplicates if they have the same identity).
+ */
+public class DuplicateIngredientException extends RuntimeException {
+ public DuplicateIngredientException() {
+ super("Operation would result in duplicate ingredients");
+ }
+}
diff --git a/src/main/java/seedu/address/model/ingredient/exceptions/IngredientNotFoundException.java b/src/main/java/seedu/address/model/ingredient/exceptions/IngredientNotFoundException.java
new file mode 100644
index 00000000000..8267253ae4e
--- /dev/null
+++ b/src/main/java/seedu/address/model/ingredient/exceptions/IngredientNotFoundException.java
@@ -0,0 +1,11 @@
+package seedu.address.model.ingredient.exceptions;
+
+/**
+ * Signals that the operation is unable to find the specified ingredient.
+ */
+public class IngredientNotFoundException extends RuntimeException {
+ private static String message = "Ingredient does not exist in Inventory ";
+ public IngredientNotFoundException() {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/address/model/ingredient/exceptions/UnitConversionException.java b/src/main/java/seedu/address/model/ingredient/exceptions/UnitConversionException.java
new file mode 100644
index 00000000000..9bcb97fae98
--- /dev/null
+++ b/src/main/java/seedu/address/model/ingredient/exceptions/UnitConversionException.java
@@ -0,0 +1,14 @@
+package seedu.address.model.ingredient.exceptions;
+
+import seedu.address.model.ingredient.Unit;
+
+/**
+ * Signals that the operation is unable to convert the current unit to the specified unit.
+ */
+public class UnitConversionException extends RuntimeException {
+ private static String message = "Unit %1$s cannot be converted to %2$s!";
+
+ public UnitConversionException(Unit fromUnit, Unit toUnit) {
+ super(String.format(message, fromUnit.toString(), toUnit.toString()));
+ }
+}
diff --git a/src/main/java/seedu/address/model/ingredient/exceptions/UnitFormatException.java b/src/main/java/seedu/address/model/ingredient/exceptions/UnitFormatException.java
new file mode 100644
index 00000000000..fe515a45b16
--- /dev/null
+++ b/src/main/java/seedu/address/model/ingredient/exceptions/UnitFormatException.java
@@ -0,0 +1,13 @@
+package seedu.address.model.ingredient.exceptions;
+
+import seedu.address.model.ingredient.Unit;
+
+/**
+ * Signals that the operation is unable to find the specified Unit.
+ */
+public class UnitFormatException extends IllegalArgumentException {
+ private static String template = "Unit %s is not a valid unit! " + Unit.MESSAGE_CONSTRAINTS;
+ public UnitFormatException(String str) {
+ super(String.format(template, str));
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java
deleted file mode 100644
index 469a2cc9a1e..00000000000
--- a/src/main/java/seedu/address/model/person/Address.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-/**
- * Represents a Person's address in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)}
- */
-public class Address {
-
- public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank";
-
- /*
- * The first character of the address must not be a whitespace,
- * otherwise " " (a blank string) becomes a valid input.
- */
- public static final String VALIDATION_REGEX = "[^\\s].*";
-
- public final String value;
-
- /**
- * Constructs an {@code Address}.
- *
- * @param address A valid address.
- */
- public Address(String address) {
- requireNonNull(address);
- checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS);
- value = address;
- }
-
- /**
- * Returns true if a given string is a valid email.
- */
- public static boolean isValidAddress(String test) {
- return test.matches(VALIDATION_REGEX);
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Address)) {
- return false;
- }
-
- Address otherAddress = (Address) other;
- return value.equals(otherAddress.value);
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java
deleted file mode 100644
index c62e512bc29..00000000000
--- a/src/main/java/seedu/address/model/person/Email.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-/**
- * Represents a Person's email in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)}
- */
-public class Email {
-
- private static final String SPECIAL_CHARACTERS = "+_.-";
- public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain "
- + "and adhere to the following constraints:\n"
- + "1. The local-part should only contain alphanumeric characters and these special characters, excluding "
- + "the parentheses, (" + SPECIAL_CHARACTERS + "). The local-part may not start or end with any special "
- + "characters.\n"
- + "2. This is followed by a '@' and then a domain name. The domain name is made up of domain labels "
- + "separated by periods.\n"
- + "The domain name must:\n"
- + " - end with a domain label at least 2 characters long\n"
- + " - have each domain label start and end with alphanumeric characters\n"
- + " - have each domain label consist of alphanumeric characters, separated only by hyphens, if any.";
- // alphanumeric and special characters
- private static final String ALPHANUMERIC_NO_UNDERSCORE = "[^\\W_]+"; // alphanumeric characters except underscore
- private static final String LOCAL_PART_REGEX = "^" + ALPHANUMERIC_NO_UNDERSCORE + "([" + SPECIAL_CHARACTERS + "]"
- + ALPHANUMERIC_NO_UNDERSCORE + ")*";
- private static final String DOMAIN_PART_REGEX = ALPHANUMERIC_NO_UNDERSCORE
- + "(-" + ALPHANUMERIC_NO_UNDERSCORE + ")*";
- private static final String DOMAIN_LAST_PART_REGEX = "(" + DOMAIN_PART_REGEX + "){2,}$"; // At least two chars
- private static final String DOMAIN_REGEX = "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX;
- public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX;
-
- public final String value;
-
- /**
- * Constructs an {@code Email}.
- *
- * @param email A valid email address.
- */
- public Email(String email) {
- requireNonNull(email);
- checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS);
- value = email;
- }
-
- /**
- * Returns if a given string is a valid email.
- */
- public static boolean isValidEmail(String test) {
- return test.matches(VALIDATION_REGEX);
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Email)) {
- return false;
- }
-
- Email otherEmail = (Email) other;
- return value.equals(otherEmail.value);
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java
deleted file mode 100644
index abe8c46b535..00000000000
--- a/src/main/java/seedu/address/model/person/Person.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package seedu.address.model.person;
-
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.model.tag.Tag;
-
-/**
- * Represents a Person in the address book.
- * Guarantees: details are present and not null, field values are validated, immutable.
- */
-public class Person {
-
- // Identity fields
- private final Name name;
- private final Phone phone;
- private final Email email;
-
- // Data fields
- private final Address address;
- private final Set tags = new HashSet<>();
-
- /**
- * Every field must be present and not null.
- */
- public Person(Name name, Phone phone, Email email, Address address, Set tags) {
- requireAllNonNull(name, phone, email, address, tags);
- this.name = name;
- this.phone = phone;
- this.email = email;
- this.address = address;
- this.tags.addAll(tags);
- }
-
- public Name getName() {
- return name;
- }
-
- public Phone getPhone() {
- return phone;
- }
-
- public Email getEmail() {
- return email;
- }
-
- public Address getAddress() {
- return address;
- }
-
- /**
- * Returns an immutable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
- */
- public Set getTags() {
- return Collections.unmodifiableSet(tags);
- }
-
- /**
- * Returns true if both persons have the same name.
- * This defines a weaker notion of equality between two persons.
- */
- public boolean isSamePerson(Person otherPerson) {
- if (otherPerson == this) {
- return true;
- }
-
- return otherPerson != null
- && otherPerson.getName().equals(getName());
- }
-
- /**
- * Returns true if both persons have the same identity and data fields.
- * This defines a stronger notion of equality between two persons.
- */
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Person)) {
- return false;
- }
-
- Person otherPerson = (Person) other;
- return name.equals(otherPerson.name)
- && phone.equals(otherPerson.phone)
- && email.equals(otherPerson.email)
- && address.equals(otherPerson.address)
- && tags.equals(otherPerson.tags);
- }
-
- @Override
- public int hashCode() {
- // use this method for custom fields hashing instead of implementing your own
- return Objects.hash(name, phone, email, address, tags);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("name", name)
- .add("phone", phone)
- .add("email", email)
- .add("address", address)
- .add("tags", tags)
- .toString();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/person/Phone.java
deleted file mode 100644
index d733f63d739..00000000000
--- a/src/main/java/seedu/address/model/person/Phone.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-/**
- * Represents a Person's phone number in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)}
- */
-public class Phone {
-
-
- public static final String MESSAGE_CONSTRAINTS =
- "Phone numbers should only contain numbers, and it should be at least 3 digits long";
- public static final String VALIDATION_REGEX = "\\d{3,}";
- public final String value;
-
- /**
- * Constructs a {@code Phone}.
- *
- * @param phone A valid phone number.
- */
- public Phone(String phone) {
- requireNonNull(phone);
- checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS);
- value = phone;
- }
-
- /**
- * Returns true if a given string is a valid phone number.
- */
- public static boolean isValidPhone(String test) {
- return test.matches(VALIDATION_REGEX);
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Phone)) {
- return false;
- }
-
- Phone otherPhone = (Phone) other;
- return value.equals(otherPhone.value);
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java
deleted file mode 100644
index cc0a68d79f9..00000000000
--- a/src/main/java/seedu/address/model/person/UniquePersonList.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-
-import java.util.Iterator;
-import java.util.List;
-
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
-import seedu.address.model.person.exceptions.DuplicatePersonException;
-import seedu.address.model.person.exceptions.PersonNotFoundException;
-
-/**
- * A list of persons that enforces uniqueness between its elements and does not allow nulls.
- * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of
- * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is
- * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so
- * as to ensure that the person with exactly the same fields will be removed.
- *
- * Supports a minimal set of list operations.
- *
- * @see Person#isSamePerson(Person)
- */
-public class UniquePersonList implements Iterable {
-
- private final ObservableList internalList = FXCollections.observableArrayList();
- private final ObservableList internalUnmodifiableList =
- FXCollections.unmodifiableObservableList(internalList);
-
- /**
- * Returns true if the list contains an equivalent person as the given argument.
- */
- public boolean contains(Person toCheck) {
- requireNonNull(toCheck);
- return internalList.stream().anyMatch(toCheck::isSamePerson);
- }
-
- /**
- * Adds a person to the list.
- * The person must not already exist in the list.
- */
- public void add(Person toAdd) {
- requireNonNull(toAdd);
- if (contains(toAdd)) {
- throw new DuplicatePersonException();
- }
- internalList.add(toAdd);
- }
-
- /**
- * Replaces the person {@code target} in the list with {@code editedPerson}.
- * {@code target} must exist in the list.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the list.
- */
- public void setPerson(Person target, Person editedPerson) {
- requireAllNonNull(target, editedPerson);
-
- int index = internalList.indexOf(target);
- if (index == -1) {
- throw new PersonNotFoundException();
- }
-
- if (!target.isSamePerson(editedPerson) && contains(editedPerson)) {
- throw new DuplicatePersonException();
- }
-
- internalList.set(index, editedPerson);
- }
-
- /**
- * Removes the equivalent person from the list.
- * The person must exist in the list.
- */
- public void remove(Person toRemove) {
- requireNonNull(toRemove);
- if (!internalList.remove(toRemove)) {
- throw new PersonNotFoundException();
- }
- }
-
- public void setPersons(UniquePersonList replacement) {
- requireNonNull(replacement);
- internalList.setAll(replacement.internalList);
- }
-
- /**
- * Replaces the contents of this list with {@code persons}.
- * {@code persons} must not contain duplicate persons.
- */
- public void setPersons(List persons) {
- requireAllNonNull(persons);
- if (!personsAreUnique(persons)) {
- throw new DuplicatePersonException();
- }
-
- internalList.setAll(persons);
- }
-
- /**
- * Returns the backing list as an unmodifiable {@code ObservableList}.
- */
- public ObservableList asUnmodifiableObservableList() {
- return internalUnmodifiableList;
- }
-
- @Override
- public Iterator iterator() {
- return internalList.iterator();
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof UniquePersonList)) {
- return false;
- }
-
- UniquePersonList otherUniquePersonList = (UniquePersonList) other;
- return internalList.equals(otherUniquePersonList.internalList);
- }
-
- @Override
- public int hashCode() {
- return internalList.hashCode();
- }
-
- @Override
- public String toString() {
- return internalList.toString();
- }
-
- /**
- * Returns true if {@code persons} contains only unique persons.
- */
- private boolean personsAreUnique(List persons) {
- for (int i = 0; i < persons.size() - 1; i++) {
- for (int j = i + 1; j < persons.size(); j++) {
- if (persons.get(i).isSamePerson(persons.get(j))) {
- return false;
- }
- }
- }
- return true;
- }
-}
diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java
deleted file mode 100644
index d7290f59442..00000000000
--- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package seedu.address.model.person.exceptions;
-
-/**
- * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same
- * identity).
- */
-public class DuplicatePersonException extends RuntimeException {
- public DuplicatePersonException() {
- super("Operation would result in duplicate persons");
- }
-}
diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java
deleted file mode 100644
index fa764426ca7..00000000000
--- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package seedu.address.model.person.exceptions;
-
-/**
- * Signals that the operation is unable to find the specified person.
- */
-public class PersonNotFoundException extends RuntimeException {}
diff --git a/src/main/java/seedu/address/model/recipe/Recipe.java b/src/main/java/seedu/address/model/recipe/Recipe.java
new file mode 100644
index 00000000000..ec8f09c76c2
--- /dev/null
+++ b/src/main/java/seedu/address/model/recipe/Recipe.java
@@ -0,0 +1,188 @@
+package seedu.address.model.recipe;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import seedu.address.model.Name;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.ingredient.exceptions.IngredientNotFoundException;
+
+/**
+ * Represents a recipe in the recipe book.
+ * Guarantees: details are present and not null, field values are validated, immutable.
+ */
+public class Recipe {
+
+ private final UniqueId uuid;
+ private final Name name;
+ private final List ingredientList;
+ private final List recipeSteps;
+
+ /**
+ * Creates a new Recipe.
+ */
+ public Recipe(Name name, List ingredientList, List recipeSteps) {
+ requireAllNonNull(name, ingredientList, recipeSteps);
+ this.uuid = new UniqueId();
+ this.name = name;
+ this.ingredientList = ingredientList;
+ this.recipeSteps = recipeSteps;
+ }
+
+ /** Creates a new Recipe with the specified {@code id}. */
+ public Recipe(int id, Name name, List ingredientList, List recipeSteps) {
+ requireAllNonNull(id, name, ingredientList, recipeSteps);
+ this.uuid = UniqueId.importUniqueId(id);
+ this.name = name;
+ this.ingredientList = ingredientList;
+ this.recipeSteps = recipeSteps;
+ }
+
+ public Name getName() {
+ return this.name;
+ }
+
+ public int getId() {
+ return this.uuid.getId();
+ }
+
+ public UniqueId getUuid() {
+ return this.uuid;
+ }
+
+ public List getIngredients() {
+ return this.ingredientList;
+ }
+
+ public List getRecipeSteps() {
+ return this.recipeSteps.stream().map(RecipeStep::toString).collect(Collectors.toList());
+ }
+
+ /** Check if the current recipe contains the specified ingredient name. */
+ public boolean containsIngredient(Name ingredientName) {
+ requireNonNull(ingredientName);
+ return ingredientList.stream().anyMatch(x -> x.getName().equals(ingredientName));
+ }
+
+ /**
+ * Changes the specified recipe step with the new instructions.
+ */
+ public Recipe modifyRecipeStep(int stepNumber, String newStep) {
+ requireNonNull(newStep);
+ if (stepNumber > recipeSteps.size()) {
+ throw new IllegalArgumentException("Specified step number cannot exceed the current steps of recipe");
+ }
+ List stepCopy = new ArrayList<>(this.recipeSteps);
+ stepCopy.set(stepNumber - 1, stepCopy.get(stepNumber - 1).modifyStep(newStep));
+ return new Recipe(this.uuid.getId(), this.name, this.ingredientList, stepCopy);
+ }
+
+ /**
+ * Changes the specified recipe step with a new step number.
+ */
+ public Recipe modifyRecipeStep(int stepNumber, int newStepNumber) {
+ if (stepNumber > recipeSteps.size()) {
+ throw new IllegalArgumentException("Specified step number cannot exceed the current steps of recipe");
+ }
+ List stepCopy = new ArrayList<>(this.recipeSteps);
+ stepCopy.set(stepNumber - 1, stepCopy.get(stepNumber - 1).modifyStep(newStepNumber));
+ return new Recipe(this.uuid.getId(), this.name, this.ingredientList, stepCopy);
+ }
+
+ /**
+ * Changes the specified ingredient with a new ingredient.
+ */
+ public Recipe modifyIngredients(String oldIngredient, Ingredient newIngredient) {
+ requireAllNonNull(oldIngredient, newIngredient);
+ for (Ingredient ingredient : ingredientList) {
+ if (ingredient.getName().equals(new Name(oldIngredient))) {
+ List ingredientListCopy = new ArrayList<>(ingredientList);
+ ingredientListCopy.remove(ingredient);
+ ingredientListCopy.add(newIngredient);
+ return new Recipe(this.uuid.getId(), this.name, ingredientListCopy, this.recipeSteps);
+ }
+ }
+ throw new IngredientNotFoundException();
+ }
+
+ /**
+ * Adds ingredient to ingredient list in a recipe
+ * @param ingredient ingredient that will be added
+ * @return the edited Recipe with the added ingredient
+ */
+ public Recipe addIngredient(Ingredient ingredient) {
+ List ingredientListCopy = new ArrayList<>(ingredientList);
+ ingredientListCopy.add(ingredient);
+ return new Recipe(this.uuid.getId(), this.name, ingredientListCopy, this.recipeSteps);
+ }
+
+ /**
+ * Returns the full recipe.
+ */
+ public String getFullRecipe() {
+ StringBuilder ingredients = new StringBuilder();
+ for (Ingredient ingredient : ingredientList) {
+ ingredients.append(ingredient.getName()).append(" ").append(ingredient.getQuantity()).append("\n");
+ }
+ StringBuilder steps = new StringBuilder();
+ for (RecipeStep recipeStep : recipeSteps) {
+ steps.append(recipeStep.toString()).append("\n");
+ }
+ return String.format("%d. %s\n%s%s", this.getId(), this.name, ingredients, steps.toString().stripTrailing());
+ }
+
+ /**
+ * Returns the ingredients needed in textual format.
+ */
+ public String getIngredientsText() {
+ StringBuilder ingredients = new StringBuilder();
+ int counter = 1;
+ for (Ingredient ingredient : ingredientList) {
+ //String str = String.valueOf(70 - ingredient.getName().toString().length());
+ // Should find a way to align in javafx as alignment in javafx and terminal is different
+ String str = "";
+ ingredients.append(String.format("%d. %s %" + str + "s\n",
+ counter, ingredient.getName(), ingredient.getQuantity()));
+ counter++;
+ }
+ return ingredients.toString();
+ }
+
+ /**
+ * Returns the steps needed in textual format.
+ */
+ public String getStepsText() {
+ StringBuilder steps = new StringBuilder();
+ for (RecipeStep recipeStep : recipeSteps) {
+ steps.append(recipeStep.toString()).append("\n");
+ }
+ return steps.toString();
+ }
+
+ @Override
+ public String toString() {
+ return this.name.toString();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof Recipe)) {
+ return false;
+ }
+
+ Recipe otherRecipe = (Recipe) other;
+ return this.uuid.getId() == otherRecipe.uuid.getId()
+ && this.name.equals(otherRecipe.name)
+ && this.ingredientList.equals(otherRecipe.ingredientList)
+ && this.recipeSteps.equals(otherRecipe.recipeSteps);
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/recipe/RecipeIngredientNameMatchesPredicate.java b/src/main/java/seedu/address/model/recipe/RecipeIngredientNameMatchesPredicate.java
new file mode 100644
index 00000000000..af86d14389d
--- /dev/null
+++ b/src/main/java/seedu/address/model/recipe/RecipeIngredientNameMatchesPredicate.java
@@ -0,0 +1,48 @@
+package seedu.address.model.recipe;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.ingredient.Ingredient;
+
+/**
+ * Tests that a {@code Ingredient}'s {@code Name} matches any of the keywords given.
+ */
+public class RecipeIngredientNameMatchesPredicate implements Predicate {
+ private final String name;
+
+ public RecipeIngredientNameMatchesPredicate(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean test(Recipe recipe) {
+ for (Ingredient ingredient : recipe.getIngredients()) {
+ if (ingredient.getName().toString().toLowerCase().equals(name.toLowerCase())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof RecipeIngredientNameMatchesPredicate)) {
+ return false;
+ }
+
+ RecipeIngredientNameMatchesPredicate otherNameContainsKeywordsPredicate =
+ (RecipeIngredientNameMatchesPredicate) other;
+ return this.name.equals(otherNameContainsKeywordsPredicate.name);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("name", name).toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/recipe/RecipeList.java b/src/main/java/seedu/address/model/recipe/RecipeList.java
new file mode 100644
index 00000000000..85a5bb96895
--- /dev/null
+++ b/src/main/java/seedu/address/model/recipe/RecipeList.java
@@ -0,0 +1,100 @@
+package seedu.address.model.recipe;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Iterator;
+import java.util.List;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.model.recipe.exceptions.RecipeNotFoundException;
+
+/**
+ * A list of recipe that does not allow nulls.
+ */
+public class RecipeList implements Iterable {
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList =
+ FXCollections.unmodifiableObservableList(internalList);
+
+ /** Adds a new {@code Recipe} to the list. */
+ public void add(Recipe toAdd) {
+ requireNonNull(toAdd);
+ internalList.add(toAdd);
+ }
+
+ /** Removes a {@code Recipe} from the list. */
+ public void remove(Recipe toRemove) {
+ requireNonNull(toRemove);
+ if (!internalList.remove(toRemove)) {
+ throw new RecipeNotFoundException();
+ }
+ }
+
+ /**
+ * Empties the list.
+ */
+ public void clear() {
+ internalList.clear();
+ }
+
+ /** Returns an immutable list for JavaFX. */
+ public ObservableList asUnmodifiableObservableList() {
+ return internalUnmodifiableList;
+ }
+
+ /** Replaces the current recipe list with the {@code replacement} list. */
+ public void setRecipes(RecipeList replacement) {
+ requireNonNull(replacement);
+ internalList.setAll(replacement.internalList);
+ }
+
+ /**
+ * Replaces the contents of this list with {@code ingredients}.
+ * {@code ingredients} must not contain duplicate ingredients.
+ */
+ public void setRecipes(List recipeList) {
+ requireAllNonNull(recipeList);
+ internalList.setAll(recipeList);
+ }
+
+ /**
+ * Returns the full recipe representation of the specified {@code recipeId}.
+ * @throws RecipeNotFoundException if recipe does not exist.
+ */
+ public String getFullRecipe(int recipeId) {
+ return this.internalList.stream().filter(x -> x.getId() == recipeId).findFirst()
+ .orElseThrow(RecipeNotFoundException::new).getFullRecipe();
+ }
+
+ @Override
+ public Iterator iterator() {
+ return internalList.iterator();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof RecipeList)) {
+ return false;
+ }
+
+ RecipeList otherRecipeList = (RecipeList) other;
+ return internalList.equals(otherRecipeList.internalList);
+ }
+
+ @Override
+ public int hashCode() {
+ return internalList.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return internalList.toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/recipe/RecipeStep.java b/src/main/java/seedu/address/model/recipe/RecipeStep.java
new file mode 100644
index 00000000000..4ce2d7a500e
--- /dev/null
+++ b/src/main/java/seedu/address/model/recipe/RecipeStep.java
@@ -0,0 +1,76 @@
+package seedu.address.model.recipe;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import seedu.address.model.recipe.exceptions.RecipeStepFormatException;
+
+/**
+ * An abstraction class for a recipe step.
+ * Wraps a step number and a string instruction.
+ */
+public class RecipeStep {
+ private static final Pattern recipeStepPattern =
+ Pattern.compile("(?[0-9]+)\\. (?\\S.*)");
+ private final String instruction;
+ private final int stepNumber;
+
+ /** Creates a new recipe step with the specified {@code stepNumber} and {@code instruction}. */
+ public RecipeStep(String instruction, int stepNumber) {
+ requireAllNonNull(instruction, stepNumber);
+ this.instruction = instruction;
+ this.stepNumber = stepNumber;
+ }
+
+ /** Modifies the current recipe step with the specified {@code newInstruction}. */
+ public RecipeStep modifyStep(String newInstruction) {
+ requireNonNull(newInstruction);
+ return new RecipeStep(newInstruction, this.stepNumber);
+ }
+
+ /** Modifies the current recipe step with the specified {@code newStepNumber} */
+ public RecipeStep modifyStep(int newStepNumber) {
+ return new RecipeStep(this.instruction, newStepNumber);
+ }
+
+ /** Parses the {@code str} as a {@code RecipeStep}. */
+ public static RecipeStep parseRecipeStep(String str) {
+ Matcher matcher = recipeStepPattern.matcher(str);
+ if (matcher.find()) {
+ int stepNumber = Integer.parseInt(matcher.group("stepNumber"));
+ String stepInstruction = matcher.group("stepInstruction");
+ return new RecipeStep(stepInstruction, stepNumber);
+ } else {
+ throw new RecipeStepFormatException();
+ }
+ }
+
+ /** Returns the step number. */
+ public int getStepNumber() {
+ return stepNumber;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%d. %s", this.stepNumber, this.instruction);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof RecipeStep)) {
+ return false;
+ }
+
+ RecipeStep otherRecipeStep = (RecipeStep) other;
+ return this.stepNumber == otherRecipeStep.stepNumber
+ && this.instruction.equals(otherRecipeStep.instruction);
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/recipe/RecipeUuidMatchesPredicate.java b/src/main/java/seedu/address/model/recipe/RecipeUuidMatchesPredicate.java
new file mode 100644
index 00000000000..e7741b9c84c
--- /dev/null
+++ b/src/main/java/seedu/address/model/recipe/RecipeUuidMatchesPredicate.java
@@ -0,0 +1,45 @@
+package seedu.address.model.recipe;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.ToStringBuilder;
+
+/**
+ * Tests that a {@code Ingredient}'s {@code Name} matches any of the keywords given.
+ */
+public class RecipeUuidMatchesPredicate implements Predicate {
+ private final UniqueId uuid;
+
+ public RecipeUuidMatchesPredicate(UniqueId uuid) {
+ this.uuid = uuid;
+ }
+
+ public UniqueId getUuid() {
+ return this.uuid;
+ }
+
+ @Override
+ public boolean test(Recipe recipe) {
+ return this.uuid.equals(recipe.getUuid());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof RecipeUuidMatchesPredicate)) {
+ return false;
+ }
+
+ RecipeUuidMatchesPredicate otherNameContainsKeywordsPredicate = (RecipeUuidMatchesPredicate) other;
+ return this.uuid.equals(otherNameContainsKeywordsPredicate.uuid);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("uuid", this.uuid).toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/recipe/UniqueId.java b/src/main/java/seedu/address/model/recipe/UniqueId.java
new file mode 100644
index 00000000000..f631619dbde
--- /dev/null
+++ b/src/main/java/seedu/address/model/recipe/UniqueId.java
@@ -0,0 +1,49 @@
+package seedu.address.model.recipe;
+
+/**
+ * A wrapper for a unique immutable id.
+ */
+public class UniqueId {
+ private static int lastId = 0;
+ private final int id;
+
+ /** Creates a new unique ID which is 1 increment of the previous id, starts from 1 */
+ public UniqueId() {
+ this.id = ++lastId;
+ }
+
+ /** Constructs a unique ID with the given {@code id} */
+ public UniqueId(int id) {
+ this.id = id;
+ }
+
+ /** Imports a unique ID with the given {@code id}, updates the previous id if the imported id is larger */
+ public static UniqueId importUniqueId(int id) {
+ lastId = Math.max(lastId, id);
+ return new UniqueId(id);
+ }
+
+ public int getId() {
+ return this.id;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UniqueId)) {
+ return false;
+ }
+
+ UniqueId otherUuid = (UniqueId) other;
+ return this.id == otherUuid.id;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(id);
+ }
+}
diff --git a/src/main/java/seedu/address/model/recipe/exceptions/RecipeNotFoundException.java b/src/main/java/seedu/address/model/recipe/exceptions/RecipeNotFoundException.java
new file mode 100644
index 00000000000..66ccc7cca85
--- /dev/null
+++ b/src/main/java/seedu/address/model/recipe/exceptions/RecipeNotFoundException.java
@@ -0,0 +1,12 @@
+package seedu.address.model.recipe.exceptions;
+
+/**
+ * Signals that the operation is unable to find the specified recipe.
+ */
+public class RecipeNotFoundException extends RuntimeException {
+ private static String message = "Recipe does not exist in Recipe Book";
+ public RecipeNotFoundException() {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/recipe/exceptions/RecipeStepFormatException.java b/src/main/java/seedu/address/model/recipe/exceptions/RecipeStepFormatException.java
new file mode 100644
index 00000000000..b9639826934
--- /dev/null
+++ b/src/main/java/seedu/address/model/recipe/exceptions/RecipeStepFormatException.java
@@ -0,0 +1,11 @@
+package seedu.address.model.recipe.exceptions;
+
+/**
+ * Signals that the operation is unable to parse the {@link seedu.address.model.recipe.RecipeStep}.
+ */
+public class RecipeStepFormatException extends IllegalArgumentException {
+ private static String template = "Not a valid recipe step!";
+ public RecipeStepFormatException() {
+ super(template);
+ }
+}
diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java
index f1a0d4e233b..ca02fe7f59c 100644
--- a/src/main/java/seedu/address/model/tag/Tag.java
+++ b/src/main/java/seedu/address/model/tag/Tag.java
@@ -1,62 +1,62 @@
-package seedu.address.model.tag;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-/**
- * Represents a Tag in the address book.
- * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)}
- */
-public class Tag {
-
- public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric";
- public static final String VALIDATION_REGEX = "\\p{Alnum}+";
-
- public final String tagName;
-
- /**
- * Constructs a {@code Tag}.
- *
- * @param tagName A valid tag name.
- */
- public Tag(String tagName) {
- requireNonNull(tagName);
- checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS);
- this.tagName = tagName;
- }
-
- /**
- * Returns true if a given string is a valid tag name.
- */
- public static boolean isValidTagName(String test) {
- return test.matches(VALIDATION_REGEX);
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Tag)) {
- return false;
- }
-
- Tag otherTag = (Tag) other;
- return tagName.equals(otherTag.tagName);
- }
-
- @Override
- public int hashCode() {
- return tagName.hashCode();
- }
-
- /**
- * Format state as text for viewing.
- */
- public String toString() {
- return '[' + tagName + ']';
- }
-
-}
+//package seedu.address.model.tag;
+//
+//import static java.util.Objects.requireNonNull;
+//import static seedu.address.commons.util.AppUtil.checkArgument;
+//
+///**
+// * Represents a Tag in the inventory.
+// * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)}
+// */
+//public class Tag {
+//
+// public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric";
+// public static final String VALIDATION_REGEX = "\\p{Alnum}+";
+//
+// public final String tagName;
+//
+// /**
+// * Constructs a {@code Tag}.
+// *
+// * @param tagName A valid tag name.
+// */
+// public Tag(String tagName) {
+// requireNonNull(tagName);
+// checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS);
+// this.tagName = tagName;
+// }
+//
+// /**
+// * Returns true if a given string is a valid tag name.
+// */
+// public static boolean isValidTagName(String test) {
+// return test.matches(VALIDATION_REGEX);
+// }
+//
+// @Override
+// public boolean equals(Object other) {
+// if (other == this) {
+// return true;
+// }
+//
+// // instanceof handles nulls
+// if (!(other instanceof Tag)) {
+// return false;
+// }
+//
+// Tag otherTag = (Tag) other;
+// return tagName.equals(otherTag.tagName);
+// }
+//
+// @Override
+// public int hashCode() {
+// return tagName.hashCode();
+// }
+//
+// /**
+// * Format state as text for viewing.
+// */
+// public String toString() {
+// return '[' + tagName + ']';
+// }
+//
+//}
diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java
index 1806da4facf..812db8c30ee 100644
--- a/src/main/java/seedu/address/model/util/SampleDataUtil.java
+++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java
@@ -1,60 +1,211 @@
package seedu.address.model.util;
-import java.util.Arrays;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import seedu.address.model.AddressBook;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import java.util.ArrayList;
+
+import seedu.address.model.Inventory;
+import seedu.address.model.Name;
+import seedu.address.model.ReadOnlyInventory;
+import seedu.address.model.ReadOnlyRecipeBook;
+import seedu.address.model.RecipeBook;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.ingredient.Quantity;
+import seedu.address.model.ingredient.Unit;
+import seedu.address.model.recipe.Recipe;
+import seedu.address.model.recipe.RecipeStep;
/**
- * Contains utility methods for populating {@code AddressBook} with sample data.
+ * Contains utility methods for populating {@code Inventory} with sample data.
*/
public class SampleDataUtil {
- public static Person[] getSamplePersons() {
- return new Person[] {
- new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"),
- new Address("Blk 30 Geylang Street 29, #06-40"),
- getTagSet("friends")),
- new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"),
- new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"),
- getTagSet("colleagues", "friends")),
- new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"),
- new Address("Blk 11 Ang Mo Kio Street 74, #11-04"),
- getTagSet("neighbours")),
- new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"),
- new Address("Blk 436 Serangoon Gardens Street 26, #16-43"),
- getTagSet("family")),
- new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"),
- new Address("Blk 47 Tampines Street 20, #17-35"),
- getTagSet("classmates")),
- new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"),
- new Address("Blk 45 Aljunied Street 85, #11-31"),
- getTagSet("colleagues"))
+ public static Ingredient[] getSampleIngredients() {
+ return new Ingredient[] {
+ new Ingredient(new Name("Flour"), new Quantity(3, Unit.KILOGRAM)),
+ new Ingredient(new Name("Butter"), new Quantity(50, Unit.GRAM)),
+ new Ingredient(new Name("Baking Soda"), new Quantity(50, Unit.GRAM)),
+ new Ingredient(new Name("Chocolate Chip"), new Quantity(300, Unit.GRAM)),
+ new Ingredient(new Name("Vanilla Extract"), new Quantity(100, Unit.GRAM)),
+ new Ingredient(new Name("Sugar"), new Quantity(2, Unit.KILOGRAM)),
+ new Ingredient(new Name("Cocoa Powder"), new Quantity(200, Unit.GRAM)),
+ new Ingredient(new Name("Corn Starch"), new Quantity(500, Unit.GRAM)),
};
}
- public static ReadOnlyAddressBook getSampleAddressBook() {
- AddressBook sampleAb = new AddressBook();
- for (Person samplePerson : getSamplePersons()) {
- sampleAb.addPerson(samplePerson);
+ public static ReadOnlyInventory getSampleInventory() {
+ Inventory sampleAb = new Inventory();
+ for (Ingredient sampleIngredient : getSampleIngredients()) {
+ sampleAb.addIngredient(sampleIngredient);
}
return sampleAb;
}
- /**
- * Returns a tag set containing the list of strings given.
- */
- public static Set getTagSet(String... strings) {
- return Arrays.stream(strings)
- .map(Tag::new)
- .collect(Collectors.toSet());
+ public static Recipe[] getSampleRecipes() {
+ // Recipe 1 (Cookie)
+ Ingredient flour = new Ingredient(new Name("Flour"), new Quantity(200, Unit.GRAM));
+ Ingredient milk = new Ingredient(new Name("Milk"), new Quantity(100, Unit.GRAM));
+ Ingredient chocolateChips = new Ingredient(new Name("Chocolate Chip"), new Quantity(50, Unit.GRAM));
+ ArrayList ingredientList = new ArrayList();
+ ingredientList.add(flour);
+ ingredientList.add(milk);
+ ingredientList.add(chocolateChips);
+
+ RecipeStep first = new RecipeStep("Mix flour with Milk", 1);
+ RecipeStep second = new RecipeStep("Add Chocolate Chips", 2);
+ RecipeStep third = new RecipeStep("Bake at 180C for 20 minutes", 3);
+ ArrayList recipeSteps = new ArrayList();
+ recipeSteps.add(first);
+ recipeSteps.add(second);
+ recipeSteps.add(third);
+
+ // Recipe 2 (Cake)
+ Ingredient cakeFlour = new Ingredient(new Name("Flour"), new Quantity(200, Unit.GRAM));
+ Ingredient egg = new Ingredient(new Name("Egg"), new Quantity(2, Unit.PIECE));
+ Ingredient cream = new Ingredient(new Name("Cream"), new Quantity(50, Unit.GRAM));
+ ArrayList ingredientListCake = new ArrayList();
+ ingredientListCake.add(cakeFlour);
+ ingredientListCake.add(egg);
+ ingredientListCake.add(cream);
+
+ RecipeStep cakeFirst = new RecipeStep("Mix flour with Egg", 1);
+ RecipeStep cakeSecond = new RecipeStep("Add Cream", 2);
+ RecipeStep cakeThird = new RecipeStep("Bake at 180C for 20 minutes", 3);
+ ArrayList cakeRecipeSteps = new ArrayList();
+ cakeRecipeSteps.add(cakeFirst);
+ cakeRecipeSteps.add(cakeSecond);
+ cakeRecipeSteps.add(cakeThird);
+
+ // Recipe 3 (Brioche)
+ Ingredient briocheFlour = new Ingredient(new Name("Flour"), new Quantity(300, Unit.GRAM));
+ Ingredient briocheEgg = new Ingredient(new Name("Egg"), new Quantity(3, Unit.PIECE));
+ Ingredient briocheSugar = new Ingredient(new Name("Sugar"), new Quantity(80, Unit.GRAM));
+ Ingredient briocheMilk = new Ingredient(new Name("Milk"), new Quantity(70, Unit.GRAM));
+ ArrayList ingredientListBrioche = new ArrayList();
+ ingredientListBrioche.add(briocheFlour);
+ ingredientListBrioche.add(briocheEgg);
+ ingredientListBrioche.add(briocheSugar);
+ ingredientListBrioche.add(briocheMilk);
+
+ RecipeStep briocheFirst = new RecipeStep("Mix flour with Egg", 1);
+ RecipeStep briocheSecond = new RecipeStep("Add Sugar", 2);
+ RecipeStep briocheThird = new RecipeStep("Pour in the milk", 3);
+ RecipeStep briocheFourth = new RecipeStep("Bake at 350C for 1 hour", 4);
+ ArrayList briocheRecipeSteps = new ArrayList();
+ briocheRecipeSteps.add(briocheFirst);
+ briocheRecipeSteps.add(briocheSecond);
+ briocheRecipeSteps.add(briocheThird);
+ briocheRecipeSteps.add(briocheFourth);
+
+ // Recipe 4 (Croissant)
+ Ingredient croissantFlour = new Ingredient(new Name("Flour"), new Quantity(300, Unit.GRAM));
+ Ingredient croissantButter = new Ingredient(new Name("Butter"), new Quantity(100, Unit.GRAM));
+ Ingredient croissantMilk = new Ingredient(new Name("Milk"), new Quantity(70, Unit.GRAM));
+ Ingredient croissantSugar = new Ingredient(new Name("Sugar"), new Quantity(50, Unit.GRAM));
+ ArrayList ingredientListCroissant = new ArrayList();
+ ingredientListCroissant.add(croissantFlour);
+ ingredientListCroissant.add(croissantButter);
+ ingredientListCroissant.add(croissantMilk);
+ ingredientListCroissant.add(croissantSugar);
+
+ RecipeStep croissantFirst = new RecipeStep("Mix flour with Butter", 1);
+ RecipeStep croissantSecond = new RecipeStep("Slowly mix milk in", 2);
+ RecipeStep croissantThird = new RecipeStep("Pour sugar in", 3);
+ RecipeStep croissantFourth = new RecipeStep("Bake at 270C for 40 minutes", 4);
+ ArrayList croissantRecipeSteps = new ArrayList();
+ croissantRecipeSteps.add(croissantFirst);
+ croissantRecipeSteps.add(croissantSecond);
+ croissantRecipeSteps.add(croissantThird);
+ croissantRecipeSteps.add(croissantFourth);
+
+ // Recipe 5 (Chocolate Muffin)
+ Ingredient chocolateFlour = new Ingredient(new Name("Flour"), new Quantity(200, Unit.GRAM));
+ Ingredient chocolateSugar = new Ingredient(new Name("Sugar"), new Quantity(100, Unit.GRAM));
+ Ingredient chocolateVanilla = new Ingredient(new Name("Vanilla"), new Quantity(50, Unit.GRAM));
+ Ingredient chocolateChocolate = new Ingredient(new Name("Chocolate Chip"), new Quantity(50, Unit.GRAM));
+ ArrayList ingredientListChocolate = new ArrayList();
+ ingredientListChocolate.add(chocolateFlour);
+ ingredientListChocolate.add(chocolateSugar);
+ ingredientListChocolate.add(chocolateVanilla);
+ ingredientListChocolate.add(chocolateChocolate);
+
+ RecipeStep chocolateFirst = new RecipeStep("Whisk flour and sugar in a bowl", 1);
+ RecipeStep chocolateSecond = new RecipeStep("Slowly add the vanilla", 2);
+ RecipeStep chocolateThird = new RecipeStep("Top the mixture with chocolate chips", 3);
+ RecipeStep chocolateFourth = new RecipeStep("Bake at 300C for 35 minutes", 4);
+ ArrayList chocolateRecipeSteps = new ArrayList();
+ chocolateRecipeSteps.add(chocolateFirst);
+ chocolateRecipeSteps.add(chocolateSecond);
+ chocolateRecipeSteps.add(chocolateThird);
+ chocolateRecipeSteps.add(chocolateFourth);
+
+ // Recipe 6 (Blueberry Muffin)
+ Ingredient blueberryFlour = new Ingredient(new Name("Flour"), new Quantity(200, Unit.GRAM));
+ Ingredient blueberrySugar = new Ingredient(new Name("Sugar"), new Quantity(100, Unit.GRAM));
+ Ingredient blueberryVanilla = new Ingredient(new Name("Vanilla"), new Quantity(50, Unit.GRAM));
+ Ingredient blueberryBlueberry = new Ingredient(new Name("Blueberry"), new Quantity(50, Unit.GRAM));
+ ArrayList ingredientListBlueberry = new ArrayList();
+ ingredientListBlueberry.add(blueberryFlour);
+ ingredientListBlueberry.add(blueberrySugar);
+ ingredientListBlueberry.add(blueberryVanilla);
+ ingredientListBlueberry.add(blueberryBlueberry);
+
+ RecipeStep blueberryFirst = new RecipeStep("Whisk flour and sugar in a bowl", 1);
+ RecipeStep blueberrySecond = new RecipeStep("Slowly add the vanilla", 2);
+ RecipeStep blueberryThird = new RecipeStep("Top the mixture with blueberries", 3);
+ RecipeStep blueberryFourth = new RecipeStep("Bake at 300C for 35 minutes", 4);
+ ArrayList blueberryRecipeSteps = new ArrayList();
+ blueberryRecipeSteps.add(blueberryFirst);
+ blueberryRecipeSteps.add(blueberrySecond);
+ blueberryRecipeSteps.add(blueberryThird);
+ blueberryRecipeSteps.add(blueberryFourth);
+
+ // Recipe 7 (Brownie)
+ Ingredient brownieSugar = new Ingredient(new Name("Sugar"), new Quantity(100, Unit.GRAM));
+ Ingredient brownieFlour = new Ingredient(new Name("Flour"), new Quantity(200, Unit.GRAM));
+ Ingredient brownieCocoa = new Ingredient(new Name("Cocoa"), new Quantity(150, Unit.GRAM));
+ ArrayList ingredientListBrownie = new ArrayList();
+ ingredientListBrownie.add(brownieSugar);
+ ingredientListBrownie.add(brownieFlour);
+ ingredientListBrownie.add(brownieCocoa);
+
+ RecipeStep brownieFirst = new RecipeStep("Mix ingredients in a bowl", 1);
+ RecipeStep brownieSecond = new RecipeStep("Bake at 325C for 45 minutes", 2);
+ ArrayList brownieRecipeSteps = new ArrayList();
+ brownieRecipeSteps.add(brownieFirst);
+ brownieRecipeSteps.add(brownieSecond);
+
+ // Recipe 8 (Mee Jiang Kueh)
+ Ingredient kuehEgg = new Ingredient(new Name("Egg"), new Quantity(1, Unit.PIECE));
+ Ingredient kuehBakingSoda = new Ingredient(new Name("Baking Soda"), new Quantity(50, Unit.GRAM));
+ Ingredient kuehSugar = new Ingredient(new Name("Sugar"), new Quantity(20, Unit.GRAM));
+ ArrayList ingredientListKueh = new ArrayList();
+ ingredientListKueh.add(kuehEgg);
+ ingredientListKueh.add(kuehBakingSoda);
+ ingredientListKueh.add(kuehSugar);
+
+ RecipeStep kuehFirst = new RecipeStep("Mix egg and sugar until sugar dissolves", 1);
+ RecipeStep kuehSecond = new RecipeStep("Sift in the baking soda", 2);
+ RecipeStep kuehThird = new RecipeStep("Cook in skillet at 180C for 30 minutes", 3);
+ ArrayList kuehRecipeSteps = new ArrayList();
+ kuehRecipeSteps.add(kuehFirst);
+ kuehRecipeSteps.add(kuehSecond);
+ kuehRecipeSteps.add(kuehThird);
+
+ return new Recipe[] {
+ new Recipe(new Name("Cookie"), ingredientList, recipeSteps),
+ new Recipe(new Name("Cake"), ingredientListCake, cakeRecipeSteps),
+ new Recipe(new Name("Brioche"), ingredientListBrioche, briocheRecipeSteps),
+ new Recipe(new Name("Croissant"), ingredientListCroissant, croissantRecipeSteps),
+ new Recipe(new Name("Chocolate Muffin"), ingredientListChocolate, chocolateRecipeSteps),
+ new Recipe(new Name("Blueberry Muffin"), ingredientListBlueberry, blueberryRecipeSteps),
+ new Recipe(new Name("Brownie"), ingredientListBrownie, brownieRecipeSteps),
+ new Recipe(new Name("Mee Jiang Kueh"), ingredientListKueh, kuehRecipeSteps),
+ };
}
+ public static ReadOnlyRecipeBook getSampleRecipeBook() {
+ RecipeBook sampleRb = new RecipeBook();
+ for (Recipe sampleRecipe : getSampleRecipes()) {
+ sampleRb.addRecipe(sampleRecipe);
+ }
+ return sampleRb;
+ }
}
diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/AddressBookStorage.java
deleted file mode 100644
index f2e015105ae..00000000000
--- a/src/main/java/seedu/address/storage/AddressBookStorage.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package seedu.address.storage;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Optional;
-
-import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.model.ReadOnlyAddressBook;
-
-/**
- * Represents a storage for {@link seedu.address.model.AddressBook}.
- */
-public interface AddressBookStorage {
-
- /**
- * Returns the file path of the data file.
- */
- Path getAddressBookFilePath();
-
- /**
- * Returns AddressBook data as a {@link ReadOnlyAddressBook}.
- * Returns {@code Optional.empty()} if storage file is not found.
- *
- * @throws DataLoadingException if loading the data from storage failed.
- */
- Optional readAddressBook() throws DataLoadingException;
-
- /**
- * @see #getAddressBookFilePath()
- */
- Optional readAddressBook(Path filePath) throws DataLoadingException;
-
- /**
- * Saves the given {@link ReadOnlyAddressBook} to the storage.
- * @param addressBook cannot be null.
- * @throws IOException if there was any problem writing to the file.
- */
- void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException;
-
- /**
- * @see #saveAddressBook(ReadOnlyAddressBook)
- */
- void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException;
-
-}
diff --git a/src/main/java/seedu/address/storage/InventoryStorage.java b/src/main/java/seedu/address/storage/InventoryStorage.java
new file mode 100644
index 00000000000..b037d31c30f
--- /dev/null
+++ b/src/main/java/seedu/address/storage/InventoryStorage.java
@@ -0,0 +1,45 @@
+package seedu.address.storage;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+
+import seedu.address.commons.exceptions.DataLoadingException;
+import seedu.address.model.ReadOnlyInventory;
+
+/**
+ * Represents a storage for {@link seedu.address.model.Inventory}.
+ */
+public interface InventoryStorage {
+
+ /**
+ * Returns the file path of the data file.
+ */
+ Path getInventoryFilePath();
+
+ /**
+ * Returns Inventory data as a {@link ReadOnlyInventory}.
+ * Returns {@code Optional.empty()} if storage file is not found.
+ *
+ * @throws DataLoadingException if loading the data from storage failed.
+ */
+ Optional readInventory() throws DataLoadingException;
+
+ /**
+ * @see #getInventoryFilePath()
+ */
+ Optional readInventory(Path filePath) throws DataLoadingException;
+
+ /**
+ * Saves the given {@link ReadOnlyInventory} to the storage.
+ * @param inventory cannot be null.
+ * @throws IOException if there was any problem writing to the file.
+ */
+ void saveInventory(ReadOnlyInventory inventory) throws IOException;
+
+ /**
+ * @see #saveInventory(ReadOnlyInventory)
+ */
+ void saveInventory(ReadOnlyInventory inventory, Path filePath) throws IOException;
+
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedIngredient.java b/src/main/java/seedu/address/storage/JsonAdaptedIngredient.java
new file mode 100644
index 00000000000..d74b981f047
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedIngredient.java
@@ -0,0 +1,57 @@
+package seedu.address.storage;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.Name;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.ingredient.Quantity;
+
+/**
+ * Jackson-friendly version of {@link Ingredient}.
+ */
+class JsonAdaptedIngredient {
+
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Ingredient's %s field is missing!";
+
+ private final String name;
+ private final String quantity;
+
+ /**
+ * Constructs a {@code JsonAdaptedIngredient} with the given ingredient details.
+ */
+ @JsonCreator
+ public JsonAdaptedIngredient(@JsonProperty("name") String name, @JsonProperty("quantity") String quantity) {
+ this.name = name;
+ this.quantity = quantity;
+ }
+
+ /**
+ * Converts a given {@code Ingredient} into this class for Jackson use.
+ */
+ public JsonAdaptedIngredient(Ingredient source) {
+ name = source.getName().fullName;
+ quantity = source.getQuantity().toString();
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted ingredient object into the model's {@code Ingredient} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted ingredient.
+ */
+ public Ingredient toModelType() throws IllegalValueException {
+ if (name == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
+ }
+ if (!Name.isValidName(name)) {
+ throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS);
+ }
+ final Name modelName = new Name(name);
+ final Quantity modelQuantity = Quantity.parseQuantity(quantity);
+
+ return new Ingredient(modelName, modelQuantity);
+ }
+
+
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
deleted file mode 100644
index bd1ca0f56c8..00000000000
--- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package seedu.address.storage;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
-
-/**
- * Jackson-friendly version of {@link Person}.
- */
-class JsonAdaptedPerson {
-
- public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!";
-
- private final String name;
- private final String phone;
- private final String email;
- private final String address;
- private final List tags = new ArrayList<>();
-
- /**
- * Constructs a {@code JsonAdaptedPerson} with the given person details.
- */
- @JsonCreator
- public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone,
- @JsonProperty("email") String email, @JsonProperty("address") String address,
- @JsonProperty("tags") List tags) {
- this.name = name;
- this.phone = phone;
- this.email = email;
- this.address = address;
- if (tags != null) {
- this.tags.addAll(tags);
- }
- }
-
- /**
- * Converts a given {@code Person} into this class for Jackson use.
- */
- public JsonAdaptedPerson(Person source) {
- name = source.getName().fullName;
- phone = source.getPhone().value;
- email = source.getEmail().value;
- address = source.getAddress().value;
- tags.addAll(source.getTags().stream()
- .map(JsonAdaptedTag::new)
- .collect(Collectors.toList()));
- }
-
- /**
- * Converts this Jackson-friendly adapted person object into the model's {@code Person} object.
- *
- * @throws IllegalValueException if there were any data constraints violated in the adapted person.
- */
- public Person toModelType() throws IllegalValueException {
- final List personTags = new ArrayList<>();
- for (JsonAdaptedTag tag : tags) {
- personTags.add(tag.toModelType());
- }
-
- if (name == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
- }
- if (!Name.isValidName(name)) {
- throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS);
- }
- final Name modelName = new Name(name);
-
- if (phone == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()));
- }
- if (!Phone.isValidPhone(phone)) {
- throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS);
- }
- final Phone modelPhone = new Phone(phone);
-
- if (email == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()));
- }
- if (!Email.isValidEmail(email)) {
- throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS);
- }
- final Email modelEmail = new Email(email);
-
- if (address == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()));
- }
- if (!Address.isValidAddress(address)) {
- throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS);
- }
- final Address modelAddress = new Address(address);
-
- final Set modelTags = new HashSet<>(personTags);
- return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags);
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedRecipe.java b/src/main/java/seedu/address/storage/JsonAdaptedRecipe.java
new file mode 100644
index 00000000000..1b884056295
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedRecipe.java
@@ -0,0 +1,78 @@
+package seedu.address.storage;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.Name;
+import seedu.address.model.ingredient.Ingredient;
+import seedu.address.model.recipe.Recipe;
+import seedu.address.model.recipe.RecipeStep;
+
+/**
+ * Jackson friendly version of {@link Recipe}.
+ */
+public class JsonAdaptedRecipe {
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Recipe's %s field is missing!";
+
+ private final int id;
+ private final String name;
+ private final List ingredients;
+ private final List steps;
+
+ /**
+ * Constructs a {@code JsonAdaptedRecipe} with the given ingredient details.
+ */
+ public JsonAdaptedRecipe(@JsonProperty("id") int id, @JsonProperty("name") String name,
+ @JsonProperty("ingredients") List ingredients,
+ @JsonProperty("steps") List steps) {
+
+ requireAllNonNull(id, name, ingredients, steps);
+ this.id = id;
+ this.name = name;
+ this.ingredients = ingredients;
+ this.steps = steps;
+ }
+
+ /**
+ * Converts a given {@code Recipe} into this class for Jackson use.
+ */
+ public JsonAdaptedRecipe(Recipe recipe) {
+ this.id = recipe.getId();
+ this.name = recipe.getName().fullName;
+ this.ingredients = recipe.getIngredients().stream().map(JsonAdaptedIngredient::new)
+ .collect(Collectors.toList());
+ this.steps = recipe.getRecipeSteps();
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted recipe object into the model's {@code Recipe} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted recipe.
+ */
+ public Recipe toModelType() throws IllegalValueException {
+ if (name == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
+ }
+ if (!Name.isValidName(name)) {
+ throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS);
+ }
+ final Name modelName = new Name(name);
+ final List modelIngredients = ingredients.stream().map(x -> {
+ try {
+ return x.toModelType();
+ } catch (IllegalValueException e) {
+ return null;
+ }
+ }).collect(Collectors.toList());
+
+ final List modelSteps = steps.stream().map(RecipeStep::parseRecipeStep)
+ .collect(Collectors.toList());
+
+ return new Recipe(id, modelName, modelIngredients, modelSteps);
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/address/storage/JsonAdaptedTag.java
deleted file mode 100644
index 0df22bdb754..00000000000
--- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package seedu.address.storage;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonValue;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.tag.Tag;
-
-/**
- * Jackson-friendly version of {@link Tag}.
- */
-class JsonAdaptedTag {
-
- private final String tagName;
-
- /**
- * Constructs a {@code JsonAdaptedTag} with the given {@code tagName}.
- */
- @JsonCreator
- public JsonAdaptedTag(String tagName) {
- this.tagName = tagName;
- }
-
- /**
- * Converts a given {@code Tag} into this class for Jackson use.
- */
- public JsonAdaptedTag(Tag source) {
- tagName = source.tagName;
- }
-
- @JsonValue
- public String getTagName() {
- return tagName;
- }
-
- /**
- * Converts this Jackson-friendly adapted tag object into the model's {@code Tag} object.
- *
- * @throws IllegalValueException if there were any data constraints violated in the adapted tag.
- */
- public Tag toModelType() throws IllegalValueException {
- if (!Tag.isValidTagName(tagName)) {
- throw new IllegalValueException(Tag.MESSAGE_CONSTRAINTS);
- }
- return new Tag(tagName);
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonInventoryStorage.java
similarity index 50%
rename from src/main/java/seedu/address/storage/JsonAddressBookStorage.java
rename to src/main/java/seedu/address/storage/JsonInventoryStorage.java
index 41e06f264e1..cb7996b2d05 100644
--- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
+++ b/src/main/java/seedu/address/storage/JsonInventoryStorage.java
@@ -12,47 +12,47 @@
import seedu.address.commons.exceptions.IllegalValueException;
import seedu.address.commons.util.FileUtil;
import seedu.address.commons.util.JsonUtil;
-import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyInventory;
/**
- * A class to access AddressBook data stored as a json file on the hard disk.
+ * A class to access Inventory data stored as a json file on the hard disk.
*/
-public class JsonAddressBookStorage implements AddressBookStorage {
+public class JsonInventoryStorage implements InventoryStorage {
- private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class);
+ private static final Logger logger = LogsCenter.getLogger(JsonInventoryStorage.class);
private Path filePath;
- public JsonAddressBookStorage(Path filePath) {
+ public JsonInventoryStorage(Path filePath) {
this.filePath = filePath;
}
- public Path getAddressBookFilePath() {
+ public Path getInventoryFilePath() {
return filePath;
}
@Override
- public Optional readAddressBook() throws DataLoadingException {
- return readAddressBook(filePath);
+ public Optional readInventory() throws DataLoadingException {
+ return readInventory(filePath);
}
/**
- * Similar to {@link #readAddressBook()}.
+ * Similar to {@link #readInventory()}.
*
* @param filePath location of the data. Cannot be null.
* @throws DataLoadingException if loading the data from storage failed.
*/
- public Optional readAddressBook(Path filePath) throws DataLoadingException {
+ public Optional readInventory(Path filePath) throws DataLoadingException {
requireNonNull(filePath);
- Optional jsonAddressBook = JsonUtil.readJsonFile(
- filePath, JsonSerializableAddressBook.class);
- if (!jsonAddressBook.isPresent()) {
+ Optional jsonInventory = JsonUtil.readJsonFile(
+ filePath, JsonSerializableInventory.class);
+ if (!jsonInventory.isPresent()) {
return Optional.empty();
}
try {
- return Optional.of(jsonAddressBook.get().toModelType());
+ return Optional.of(jsonInventory.get().toModelType());
} catch (IllegalValueException ive) {
logger.info("Illegal values found in " + filePath + ": " + ive.getMessage());
throw new DataLoadingException(ive);
@@ -60,21 +60,21 @@ public Optional readAddressBook(Path filePath) throws DataL
}
@Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException {
- saveAddressBook(addressBook, filePath);
+ public void saveInventory(ReadOnlyInventory inventory) throws IOException {
+ saveInventory(inventory, filePath);
}
/**
- * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}.
+ * Similar to {@link #saveInventory(ReadOnlyInventory)}.
*
* @param filePath location of the data. Cannot be null.
*/
- public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException {
- requireNonNull(addressBook);
+ public void saveInventory(ReadOnlyInventory inventory, Path filePath) throws IOException {
+ requireNonNull(inventory);
requireNonNull(filePath);
FileUtil.createIfMissing(filePath);
- JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath);
+ JsonUtil.saveJsonFile(new JsonSerializableInventory(inventory), filePath);
}
}
diff --git a/src/main/java/seedu/address/storage/JsonRecipeBookStorage.java b/src/main/java/seedu/address/storage/JsonRecipeBookStorage.java
new file mode 100644
index 00000000000..cc4a575e54a
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonRecipeBookStorage.java
@@ -0,0 +1,82 @@
+package seedu.address.storage;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.exceptions.DataLoadingException;
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.commons.util.FileUtil;
+import seedu.address.commons.util.JsonUtil;
+import seedu.address.model.ReadOnlyRecipeBook;
+
+/**
+ * A class to access RecipeBook data stored as a json file on the hard disk.
+ */
+public class JsonRecipeBookStorage implements RecipeBookStorage {
+
+ private static final Logger logger = LogsCenter.getLogger(JsonRecipeBookStorage.class);
+
+ private Path filePath;
+
+ public JsonRecipeBookStorage(Path filePath) {
+ this.filePath = filePath;
+ }
+
+ @Override
+ public Path getRecipeBookFilePath() {
+ return this.filePath;
+ }
+
+ @Override
+ public Optional readRecipeBook() throws DataLoadingException {
+ return readRecipeBook(this.filePath);
+ }
+
+ /**
+ * Similar to {@link #readRecipeBook()}.
+ *
+ * @param filePath location of the data. Cannot be null.
+ * @throws DataLoadingException if loading the data from storage failed.
+ */
+ @Override
+ public Optional readRecipeBook(Path filePath) throws DataLoadingException {
+ requireNonNull(filePath);
+
+ Optional jsonRecipeBook = JsonUtil.readJsonFile(
+ filePath, JsonSerializableRecipeBook.class);
+ if (!jsonRecipeBook.isPresent()) {
+ return Optional.empty();
+ }
+
+ try {
+ return Optional.of(jsonRecipeBook.get().toModelType());
+ } catch (IllegalValueException ive) {
+ logger.info("Illegal values found in " + filePath + ": " + ive.getMessage());
+ throw new DataLoadingException(ive);
+ }
+ }
+
+ @Override
+ public void saveRecipeBook(ReadOnlyRecipeBook recipeBook) throws IOException {
+ saveRecipeBook(recipeBook, this.filePath);
+ }
+
+ /**
+ * Similar to {@link #saveRecipeBook(ReadOnlyRecipeBook)}.
+ *
+ * @param filePath location of the data. Cannot be null.
+ */
+ @Override
+ public void saveRecipeBook(ReadOnlyRecipeBook recipeBook, Path filePath) throws IOException {
+ requireAllNonNull(recipeBook, filePath);
+
+ FileUtil.createIfMissing(filePath);
+ JsonUtil.saveJsonFile(new JsonSerializableRecipeBook(recipeBook), filePath);
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
deleted file mode 100644
index 5efd834091d..00000000000
--- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package seedu.address.storage;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonRootName;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.AddressBook;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
-
-/**
- * An Immutable AddressBook that is serializable to JSON format.
- */
-@JsonRootName(value = "addressbook")
-class JsonSerializableAddressBook {
-
- public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s).";
-
- private final List persons = new ArrayList<>();
-
- /**
- * Constructs a {@code JsonSerializableAddressBook} with the given persons.
- */
- @JsonCreator
- public JsonSerializableAddressBook(@JsonProperty("persons") List persons) {
- this.persons.addAll(persons);
- }
-
- /**
- * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use.
- *
- * @param source future changes to this will not affect the created {@code JsonSerializableAddressBook}.
- */
- public JsonSerializableAddressBook(ReadOnlyAddressBook source) {
- persons.addAll(source.getPersonList().stream().map(JsonAdaptedPerson::new).collect(Collectors.toList()));
- }
-
- /**
- * Converts this address book into the model's {@code AddressBook} object.
- *
- * @throws IllegalValueException if there were any data constraints violated.
- */
- public AddressBook toModelType() throws IllegalValueException {
- AddressBook addressBook = new AddressBook();
- for (JsonAdaptedPerson jsonAdaptedPerson : persons) {
- Person person = jsonAdaptedPerson.toModelType();
- if (addressBook.hasPerson(person)) {
- throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON);
- }
- addressBook.addPerson(person);
- }
- return addressBook;
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonSerializableInventory.java b/src/main/java/seedu/address/storage/JsonSerializableInventory.java
new file mode 100644
index 00000000000..caf75155ae2
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonSerializableInventory.java
@@ -0,0 +1,61 @@
+package seedu.address.storage;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonRootName;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.Inventory;
+import seedu.address.model.ReadOnlyInventory;
+import seedu.address.model.ingredient.Ingredient;
+
+/**
+ * An Immutable Inventory that is serializable to JSON format.
+ */
+@JsonRootName(value = "inventory")
+class JsonSerializableInventory {
+
+ public static final String MESSAGE_DUPLICATE_INGREDIENT = "Ingredients list contains duplicate ingredient(s).";
+
+ private final List ingredients = new ArrayList<>();
+
+ /**
+ * Constructs a {@code JsonSerializableInventory} with the given ingredients.
+ */
+ @JsonCreator
+ public JsonSerializableInventory(@JsonProperty("ingredients") List ingredients) {
+ this.ingredients.addAll(ingredients);
+ }
+
+ /**
+ * Converts a given {@code ReadOnlyInventory} into this class for Jackson use.
+ *
+ * @param source future changes to this will not affect the created {@code JsonSerializableInventory}.
+ */
+ public JsonSerializableInventory(ReadOnlyInventory source) {
+ ingredients.addAll(source.getIngredientList().stream()
+ .map(JsonAdaptedIngredient::new).collect(Collectors.toList()));
+ }
+
+ /**
+ * Converts this inventory into the model's {@code Inventory} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated.
+ */
+ public Inventory toModelType() throws IllegalValueException {
+ Inventory inventory = new Inventory();
+ for (JsonAdaptedIngredient jsonAdaptedIngredient : ingredients) {
+ Ingredient ingredient = jsonAdaptedIngredient.toModelType();
+ if (inventory.hasIngredient(ingredient.getName())) {
+ throw new IllegalValueException(MESSAGE_DUPLICATE_INGREDIENT);
+ }
+ inventory.addIngredient(ingredient);
+ }
+ return inventory;
+ }
+
+}
diff --git a/src/main/java/seedu/address/storage/JsonSerializableRecipeBook.java b/src/main/java/seedu/address/storage/JsonSerializableRecipeBook.java
new file mode 100644
index 00000000000..285c58f484f
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonSerializableRecipeBook.java
@@ -0,0 +1,59 @@
+package seedu.address.storage;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.ReadOnlyRecipeBook;
+import seedu.address.model.RecipeBook;
+import seedu.address.model.recipe.Recipe;
+
+/**
+ * An Immutable RecipeBook that is serializable to JSON format.
+ */
+public class JsonSerializableRecipeBook {
+
+ public static final String MESSAGE_DUPLICATE_RECIPE = "Recipes list contains duplicate recipe(s).";
+ private final List recipes = new ArrayList<>();
+
+ /**
+ * Constructs a {@code JsonSerializableRecipeBook} with the given ingredients.
+ */
+ @JsonCreator
+ public JsonSerializableRecipeBook(@JsonProperty("recipes") List recipes) {
+ this.recipes.addAll(recipes);
+ }
+
+ /**
+ * Converts a given {@code ReadOnlyRecipeBook} into this class for Jackson use.
+ *
+ * @param source future changes to this will not affect the created {@code JsonSerializableRecipeBook}.
+ */
+ public JsonSerializableRecipeBook(ReadOnlyRecipeBook source) {
+ recipes.addAll(source.getRecipeList().stream()
+ .map(JsonAdaptedRecipe::new).collect(Collectors.toList()));
+ }
+
+ /**
+ * Converts this inventory into the model's {@code RecipeBook} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated.
+ */
+ public RecipeBook toModelType() throws IllegalValueException {
+ RecipeBook recipeBook = new RecipeBook();
+ for (JsonAdaptedRecipe jsonAdaptedRecipe : recipes) {
+ Recipe recipe = jsonAdaptedRecipe.toModelType();
+ if (recipeBook.hasRecipe(recipe.getUuid())) {
+ throw new IllegalValueException(MESSAGE_DUPLICATE_RECIPE);
+ }
+ recipeBook.addRecipe(recipe);
+ }
+ return recipeBook;
+ }
+
+
+}
diff --git a/src/main/java/seedu/address/storage/RecipeBookStorage.java b/src/main/java/seedu/address/storage/RecipeBookStorage.java
new file mode 100644
index 00000000000..420d8d0ca4d
--- /dev/null
+++ b/src/main/java/seedu/address/storage/RecipeBookStorage.java
@@ -0,0 +1,44 @@
+package seedu.address.storage;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+
+import seedu.address.commons.exceptions.DataLoadingException;
+import seedu.address.model.ReadOnlyRecipeBook;
+
+/**
+ * Represents a storage for {@link seedu.address.model.RecipeBook}
+ */
+public interface RecipeBookStorage {
+ /**
+ * Returns the file path of the data file.
+ */
+ Path getRecipeBookFilePath();
+
+ /**
+ * Returns Inventory data as a {@link ReadOnlyRecipeBook}.
+ * Returns {@code Optional.empty()} if storage file is not found.
+ *
+ * @throws DataLoadingException if loading the data from storage failed.
+ */
+ Optional readRecipeBook() throws DataLoadingException;
+
+ /**
+ * @see #getRecipeBookFilePath()
+ */
+ Optional readRecipeBook(Path filePath) throws DataLoadingException;
+
+ /**
+ * Saves the given {@link ReadOnlyRecipeBook} to the storage.
+ *
+ * @param recipeBook cannot be null.
+ * @throws IOException if there was any problem writing to the file.
+ */
+ void saveRecipeBook(ReadOnlyRecipeBook recipeBook) throws IOException;
+
+ /**
+ * @see #saveRecipeBook(ReadOnlyRecipeBook)
+ */
+ void saveRecipeBook(ReadOnlyRecipeBook recipeBook, Path filePath) throws IOException;
+}
diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java
index 9fba0c7a1d6..bdf998cd478 100644
--- a/src/main/java/seedu/address/storage/Storage.java
+++ b/src/main/java/seedu/address/storage/Storage.java
@@ -5,14 +5,15 @@
import java.util.Optional;
import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyInventory;
+import seedu.address.model.ReadOnlyRecipeBook;
import seedu.address.model.ReadOnlyUserPrefs;
import seedu.address.model.UserPrefs;
/**
* API of the Storage component
*/
-public interface Storage extends AddressBookStorage, UserPrefsStorage {
+public interface Storage extends InventoryStorage, UserPrefsStorage, RecipeBookStorage {
@Override
Optional readUserPrefs() throws DataLoadingException;
@@ -21,12 +22,21 @@ public interface Storage extends AddressBookStorage, UserPrefsStorage {
void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException;
@Override
- Path getAddressBookFilePath();
+ Path getInventoryFilePath();
@Override
- Optional readAddressBook() throws DataLoadingException;
+ Optional readInventory() throws DataLoadingException;
@Override
- void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException;
+ void saveInventory(ReadOnlyInventory inventory) throws IOException;
+
+ @Override
+ Path getRecipeBookFilePath();
+
+ @Override
+ Optional readRecipeBook() throws DataLoadingException;
+
+ @Override
+ void saveRecipeBook(ReadOnlyRecipeBook recipeBook) throws IOException;
}
diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java
index 8b84a9024d5..d611b86758c 100644
--- a/src/main/java/seedu/address/storage/StorageManager.java
+++ b/src/main/java/seedu/address/storage/StorageManager.java
@@ -7,25 +7,29 @@
import seedu.address.commons.core.LogsCenter;
import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyInventory;
+import seedu.address.model.ReadOnlyRecipeBook;
import seedu.address.model.ReadOnlyUserPrefs;
import seedu.address.model.UserPrefs;
/**
- * Manages storage of AddressBook data in local storage.
+ * Manages storage of Inventory data in local storage.
*/
public class StorageManager implements Storage {
private static final Logger logger = LogsCenter.getLogger(StorageManager.class);
- private AddressBookStorage addressBookStorage;
+ private InventoryStorage inventoryStorage;
private UserPrefsStorage userPrefsStorage;
+ private RecipeBookStorage recipeBookStorage;
/**
- * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}.
+ * Creates a {@code StorageManager} with the given {@code InventoryStorage} and {@code UserPrefStorage}.
*/
- public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) {
- this.addressBookStorage = addressBookStorage;
+ public StorageManager(InventoryStorage inventoryStorage, UserPrefsStorage userPrefsStorage,
+ RecipeBookStorage recipeBookStorage) {
+ this.inventoryStorage = inventoryStorage;
this.userPrefsStorage = userPrefsStorage;
+ this.recipeBookStorage = recipeBookStorage;
}
// ================ UserPrefs methods ==============================
@@ -46,33 +50,61 @@ public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException {
}
- // ================ AddressBook methods ==============================
+ // ================ Inventory methods ==============================
@Override
- public Path getAddressBookFilePath() {
- return addressBookStorage.getAddressBookFilePath();
+ public Path getInventoryFilePath() {
+ return inventoryStorage.getInventoryFilePath();
}
@Override
- public Optional readAddressBook() throws DataLoadingException {
- return readAddressBook(addressBookStorage.getAddressBookFilePath());
+ public Optional readInventory() throws DataLoadingException {
+ return readInventory(inventoryStorage.getInventoryFilePath());
}
@Override
- public Optional readAddressBook(Path filePath) throws DataLoadingException {
+ public Optional readInventory(Path filePath) throws DataLoadingException {
logger.fine("Attempting to read data from file: " + filePath);
- return addressBookStorage.readAddressBook(filePath);
+ return inventoryStorage.readInventory(filePath);
}
@Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException {
- saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath());
+ public void saveInventory(ReadOnlyInventory inventory) throws IOException {
+ saveInventory(inventory, inventoryStorage.getInventoryFilePath());
}
@Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException {
+ public void saveInventory(ReadOnlyInventory inventory, Path filePath) throws IOException {
logger.fine("Attempting to write to data file: " + filePath);
- addressBookStorage.saveAddressBook(addressBook, filePath);
+ inventoryStorage.saveInventory(inventory, filePath);
}
+ // ================ RecipeBook methods ==============================
+
+ @Override
+ public Path getRecipeBookFilePath() {
+ return recipeBookStorage.getRecipeBookFilePath();
+ }
+
+ @Override
+ public Optional readRecipeBook() throws DataLoadingException {
+ return readRecipeBook(recipeBookStorage.getRecipeBookFilePath());
+ }
+
+ @Override
+ public Optional readRecipeBook(Path filePath) throws DataLoadingException {
+ logger.fine("Attempting to read recipe data from file: " + filePath);
+ return recipeBookStorage.readRecipeBook(filePath);
+ }
+
+ @Override
+ public void saveRecipeBook(ReadOnlyRecipeBook recipeBook) throws IOException {
+ saveRecipeBook(recipeBook, recipeBookStorage.getRecipeBookFilePath());
+ }
+
+ @Override
+ public void saveRecipeBook(ReadOnlyRecipeBook recipeBook, Path filePath) throws IOException {
+ logger.fine("Attempting to write to recipe data file: " + filePath);
+ recipeBookStorage.saveRecipeBook(recipeBook, filePath);
+ }
}
diff --git a/src/main/java/seedu/address/ui/FullRecipePanel.java b/src/main/java/seedu/address/ui/FullRecipePanel.java
new file mode 100644
index 00000000000..cb4539d3cad
--- /dev/null
+++ b/src/main/java/seedu/address/ui/FullRecipePanel.java
@@ -0,0 +1,52 @@
+package seedu.address.ui;
+
+import java.util.logging.Logger;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.recipe.Recipe;
+
+/**
+ * Panel containing the list of recipes.
+ */
+public class FullRecipePanel extends UiPart {
+ private static final String FXML = "FullRecipePanel.fxml";
+
+ /**
+ * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX.
+ * As a consequence, UI elements' variable names cannot be set to such keywords
+ * or an exception will be thrown by JavaFX during runtime.
+ *
+ * @see The issue on AddressBook level 4
+ */
+
+ public final Recipe recipe;
+
+ private final Logger logger = LogsCenter.getLogger(RecipeListPanel.class);
+
+ @FXML
+ private HBox cardPane;
+ @FXML
+ private Label name;
+ @FXML
+ private Label id;
+ @FXML
+ private Label ingredients;
+ @FXML
+ private Label steps;
+
+ /**
+ * Creates a {@code IngredientCode} with the given {@code Ingredient} and index to display.
+ */
+ public FullRecipePanel(Recipe recipe) { // To be updated
+ super(FXML);
+ this.recipe = recipe;
+ name.setText(recipe.getName().toString());
+ id.setText(" (uuid: " + recipe.getId() + ")");
+ ingredients.setText(recipe.getIngredientsText());
+ steps.setText(recipe.getStepsText());
+ }
+}
diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java
index 3f16b2fcf26..de03eda4b50 100644
--- a/src/main/java/seedu/address/ui/HelpWindow.java
+++ b/src/main/java/seedu/address/ui/HelpWindow.java
@@ -15,7 +15,7 @@
*/
public class HelpWindow extends UiPart {
- public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html";
+ public static final String USERGUIDE_URL = "https://ay2324s1-cs2103t-f10-3.github.io/tp/UserGuide.html";
public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL;
private static final Logger logger = LogsCenter.getLogger(HelpWindow.class);
diff --git a/src/main/java/seedu/address/ui/IngredientCard.java b/src/main/java/seedu/address/ui/IngredientCard.java
new file mode 100644
index 00000000000..ca2695c7905
--- /dev/null
+++ b/src/main/java/seedu/address/ui/IngredientCard.java
@@ -0,0 +1,49 @@
+package seedu.address.ui;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import seedu.address.model.ingredient.Ingredient;
+
+/**
+ * An UI component that displays information of a {@code Ingredient}.
+ */
+public class IngredientCard extends UiPart {
+
+ private static final String FXML = "IngredientListCard.fxml";
+
+ /**
+ * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX.
+ * As a consequence, UI elements' variable names cannot be set to such keywords
+ * or an exception will be thrown by JavaFX during runtime.
+ *
+ * @see The issue on AddressBook level 4
+ */
+
+ public final Ingredient ingredient;
+
+ @FXML
+ private HBox cardPane;
+ @FXML
+ private Label name;
+ @FXML
+ private Label quantity;
+ @FXML
+ private Label unit;
+
+ /**
+ * Creates a {@code IngredientCode} with the given {@code Ingredient} and index to display.
+ */
+ public IngredientCard(Ingredient ingredient, int displayedIndex) {
+ super(FXML);
+ this.ingredient = ingredient;
+ name.setText(this.ingredient.getName().fullName.toUpperCase());
+ //todo un-elegant code
+ String[] quantityAndUnit = this.ingredient.getQuantity().toString().split(" ");
+ String quantityStr = quantityAndUnit[0];
+ String unitStr = quantityAndUnit[1];
+ quantity.setText(quantityStr);
+ unit.setText(unitStr);
+ }
+}
diff --git a/src/main/java/seedu/address/ui/IngredientListPanel.java b/src/main/java/seedu/address/ui/IngredientListPanel.java
new file mode 100644
index 00000000000..130a5bff855
--- /dev/null
+++ b/src/main/java/seedu/address/ui/IngredientListPanel.java
@@ -0,0 +1,50 @@
+package seedu.address.ui;
+
+import java.util.logging.Logger;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
+import javafx.scene.layout.Region;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.ingredient.Ingredient;
+
+/**
+ * Panel containing the list of ingredients.
+ */
+public class IngredientListPanel extends UiPart {
+ private static final String FXML = "IngredientListPanel.fxml";
+ private final Logger logger = LogsCenter.getLogger(IngredientListPanel.class);
+
+ @FXML
+ private ListView ingredientListView;
+
+ /**
+ * Creates a {@code IngredientListPanel} with the given {@code ObservableList}.
+ */
+ public IngredientListPanel(ObservableList