diff --git a/.gitignore b/.gitignore
index 2873e189e1..f4c1c2664e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,6 @@ bin/
/text-ui-test/ACTUAL.TXT
text-ui-test/EXPECTED-UNIX.TXT
+
+# Ignore Data
+/data
\ No newline at end of file
diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..19e86fe56e
--- /dev/null
+++ b/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: seedu.duke.Duke
+
diff --git a/README.md b/README.md
index f82e2494b7..1c669c00dd 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Duke project template
+# Taste Of Mom's (TOM)
This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it.
diff --git a/build.gradle b/build.gradle
index d5e548e85f..73ee885022 100644
--- a/build.gradle
+++ b/build.gradle
@@ -11,6 +11,7 @@ repositories {
dependencies {
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0'
+ testImplementation 'junit:junit:4.13.1'
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0'
}
@@ -43,4 +44,5 @@ checkstyle {
run{
standardInput = System.in
+ enableAssertions = true;
}
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 0f072953ea..209a14f6e8 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -1,9 +1,9 @@
# About us
-Display | Name | Github Profile | Portfolio
---------|:----:|:--------------:|:---------:
-![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
+| Display | Name | Github Profile | Portfolio |
+|----------------------------------------------------------------------------|:----------------:|:---------------------------------------------:|:------------------------------------:|
+| | Leong Yat Pang | [Github](https://github.com/YatPang) | [Portfolio](team/leongyatpang.md) |
+| | Lim Hong Yao | [Github](http://github.com/LimHongYao) | [Portfolio](team/limhongyao.md) |
+| | Aung Phone Naing | [Github](https://github.com/Aung-Phone-Naing) | [Portfolio](team/aung-phone-naing.md) |
+| | Liu Ziyang | [Github](https://github.com/liuziyang020319) | [Portfolio](team/liuziyang020319.md) |
+
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 64e1f0ed2b..05c67090d0 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,38 +1,459 @@
# Developer Guide
+- [Developer Guide](#developer-guide)
+ - [Acknowledgements](#acknowledgements)
+ - [Setup and Prerequisites](#setup-and-prerequisites)
+ - [Design and implementation](#design-and-implementation)
+ - [Architecture](#architecture)
+ - [UI Component](#ui-component)
+ - [Parser Component](#parser-component)
+ - [Command Component](#command-component)
+ - [RecipeList Component](#recipelist-component)
+ - [Storage Component](#storage-component)
+ - [Recipe Manage Feature](#recipe-manage-feature)
+ - [Implementation](#implementation)
+ - [Example Usage](#example-usage)
+ - [Recipe Find Feature](#recipe-find-feature)
+ - [Implementation](#implementation-1)
+ - [Example Usage](#example-usage-1)
+ - [Recipe Steps Edit Feature](#recipe-steps-edit-feature)
+ - [Implementation](#implementation-2)
+ - [Example Usage](#example-usage-2)
+ - [Recipe Ingredients Edit Feature](#recipe-ingredients-edit-feature)
+ - [Implementation](#implementation-3)
+ - [Example Usage](#example-usage-3)
+ - [Recipe View Feature](#recipe-view-feature)
+ - [Implementation](#implementation-4)
+ - [Example Usage](#example-usage-4)
+ - [Help Feature](#help-feature)
+ - [Implementation](#implementation-5)
+ - [Example Usage](#example-usage-5)
+ - [Appendix A - Product Scope](#appendix-a---product-scope)
+ - [Target User Profile](#target-user-profile)
+ - [Value Proposition](#value-proposition)
+ - [Appendix B - User Stories](#appendix-b---user-stories)
+ - [Appendix C - Non-Functional Requirements](#appendix-c---non-functional-requirements)
+ - [Appendix D - Glossary](#appendix-d---glossary)
+ - [Appendix E - Instructions for Manual Testing](#appendix-e---instructions-for-manual-testing)
+ - [Initialization](#initialization)
+ - [Appendix E.1 - Adding a recipe](#appendix-e1---adding-a-recipe)
+ - [Appendix E.2 - Deleting a recipe](#appendix-e2---deleting-a-recipe)
## Acknowledgements
+1. [AB-3 Developer Guide](https://se-education.org/addressbook-level3/DeveloperGuide.html)
-{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+## Setup and Prerequisites
+1. Ensure you have `Java 11` installed.
+2. Ensure your local repository is synced with the main repository at
+[AY2223S2-CS2113-F13-1/tp](https://github.com/AY2223S2-CS2113-F13-1/tp).
+3. Download the latest `tp.main.jar` from [here](https://github.com/AY2223S2-CS2113-F13-1/tp/releases).
+4. Copy the file to the folder you want to use as home folder for the recipe manager.
+5. Use `Win+R` to open the command prompt and type `cmd` and press Enter.
+6. Then `cd` into the folder where you copied the jar file. e.g. `cd C:\Users\Lee\Desktop\MyRecipe`.
+7. Type `java -jar tp.main.jar` and press Enter to start the program.
-## Design & implementation
+## Design and implementation
-{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
+### Architecture
+**Main components of the architecture**
-## Product scope
-### Target user profile
+![image](./PlantUML/MainArchitecture.png)
-{Describe the target user profile}
+The ***Architecture Diagram*** given above explains the high-level design of the App.
-### Value proposition
+Given below is a quick overview of Duke(main) components and how they interact with each other.
+It is responsible for,
+* At app launch: Initializes all other components in the correct sequence, and connects them up with each other.
+* At shut down: Shuts down the components and invokes cleanup methods where necessary.
-{Describe the value proposition: what problem does it solve?}
+The rest of the App consists of five components.
-## User Stories
+* [**`UI`**](#ui-component): The UI of the App.
+* [**`Parser`**](#parser-component): Parses user input.
+* [**`Command`**](#command-component): The command executor.
+* [**`RecipeList`**](#recipelist-component): Holds the recipe data in the recipe manager by an ArrayList.
+* [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk.
-|Version| As a ... | I want to ... | So that I can ...|
-|--------|----------|---------------|------------------|
-|v1.0|new user|see usage instructions|refer to them when I forget how to use the application|
-|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list|
+**How the components interact with each other**
-## Non-Functional Requirements
+The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues
+the command `delete 1`.
-{Give non-functional requirements}
+![image](./PlantUML/ArchitectureInteract.png)
-## Glossary
+### UI Component
+The **API** of this component is specified in
+[`UI.java`](https://github.com/AY2223S2-CS2113-F13-1/tp/blob/master/src/main/java/seedu/duke/ui/UI.java).
-* *glossary item* - Definition
+
-## Instructions for manual testing
+UI class implements the StringLib interface for some output strings. It is responsible for the following tasks:
+* Prints the welcome and goodbye messages.
+* Prints the help message, which lists all the commands available and their examples.
+* Prints the log messages for some managing operations.
+* Prints the error messages for some exceptions.
+
+### Parser Component
+The **API** of this component is specified in
+[`Parser.java`](https://github.com/AY2223S2-CS2113-F13-1/tp/blob/master/src/main/java/seedu/duke/parser/Parser.java).
+
+![image](./PlantUML/ParserComponent.png)
+
+Parser class implements the StringLib interface for some output strings and uses `CommandType` enum.
+It is responsible for the following tasks:
+* Separates user input into various elements in `Strings` to obtain `Command`, `Recipes`, `Ingredients`,
+`Steps`, `Tag`, etc.
+* Uses `CommandType` to generate each type of command.
+* Separates `Recipes` into its elements, namely `Ingredients` and `Steps`.
+* Handles any other separation tasks in regard to handling various user input `Commands`.
+
+### Command Component
+The **API** of this component is specified in
+[`Command.java`](https://github.com/AY2223S2-CS2113-F13-1/tp/blob/master/src/main/java/seedu/duke/command/Command.java).
+
+![image](./PlantUML/CommandComponent.png)
+
+Command class the StringLib interface for some output strings. It also inherits CommandType Enums for determining the
+correct tasks to be executed. It is responsible for the following tasks:
+* Executes the particular command received from the user input, case-by-case.
+* Determines if the program termination command has been sent.
+
+### RecipeList Component
+The **API** for this component is specified in
+[`RecipeList.java`](https://github.com/AY2223S2-CS2113-F13-1/tp/blob/master/src/main/java/seedu/duke/recipe/RecipeList.java).
+
+![RecipeList Component](./PlantUML/RecipeListComponent.png)
+
+The class contains the list of `Recipe` objects stored in an `ArrayList` by the program.
+It provides methods for the addition, retrieval and removal of `Recipe` objects from the list.
+A `RecipeList#clearRecipeList()` method also exists to allow for quick deletion of all `Recipe` objects.
+
+Each `Recipe` object stores the name of the dish, as well as lists storing `Ingredient` and `Step`
+objects, to keep track of the ingredients and steps to create the dish respectively.
+
+While each `Recipe` object will have only one `IngredientList` and `StepList`, each of the lists
+are not limited in how many `Ingredient` and `Step` objects they can store respectively, except that
+there must at least be one `Ingredient` at any time.
+
+### Storage Component
+The **API** of this component is specified in
+[`Storage.java`](https://github.com/AY2223S2-CS2113-F13-1/tp/blob/master/src/main/java/seedu/duke/storage/Storage.java).
+
+![image](./PlantUML/StorageComponent.png)
+
+Storage class is responsible for the following tasks:
+* Creating the directory for storing save files.
+* Setting the file path for save files.
+* Creating and writing save files based on the recipe list.
+* Scanning the save file directory for saves from previous instances of the programme.
+* Loading valid save files found in the directory.
+
+### Recipe Manage Feature
+#### Implementation
+The recipe manage feature is facilitated by the `command`,`parser`,`recipe` package.
+It implements the following operations:
+
+- `RecipeList#addNewRecipe()` - Add a new recipe to the recipe list.
+- `RecipeList#getRecipeList()` - Get the recipe list.
+- `RecipeList#removeRecipe()` - Delete a recipe from the recipe list.
+- `RecipeList#clearRecipeList()` - Clear all recipes from the recipe list.
+
+#### Example Usage
+Given below is an example usage scenario and how the recipe manage mechanism behaves at each step.
+
+**Step 1.** The user launches the application for the first time, then inputs
+`add n/MaLaXiangGuo i/Beef, Mutton, Mushrooms t/Chinese s/1` to add a new recipe to the recipe manager. `Duke` calls
+the `parseCommands()` method in the `Parser` class to parse the user input, which will return a `Command` object.
+The `Command` object will then be executed by calling the `Command#execute()` method, which will call the
+`Parser#parseSteps()` method to get the steps of the recipe.
+
+**Step 2.** The user inputs `chop beef` to add a step for the recipe. `Parser#parseSteps()` receives all the steps and
+returns a `StepList` object. Then we return to the `Command#execute()` method in the `Command` class and call the
+`RecipeList#addNewRecipe()` to add the recipe to the recipe list.
+
+**Step 3.** The user inputs `list` to list all the recipes in the recipe list. `Duke` calls the `parseCommands()`
+method in the `Parser` class to parse the user input, which will return a `Command` object. The `Command` object will
+then be executed by calling the `Command#execute()` method, which will call the `RecipeList#getRecipeList()` to get the
+recipe list. Then we return to the `Command#execute()` method in the `Command` class and call the `UI#showRecipeList()`
+to show the recipe list.
+
+**Step 4.** The user inputs `delete 1` to delete the first recipe in the recipe list. `Duke` calls the
+`parseCommands()` method in the `Parser` class to parse the user input, which will return a `Command` object. The
+`Command` object will then be executed by calling the `Command#execute()` method, which will call the
+`RecipeList#removeRecipe()` to remove the recipe from the recipe list.
+
+**Step 5.** The user executes the `clear` to clear all the recipes in the recipe list. `Duke` calls the
+`parseCommands()` method in the `Parser` class to parse the user input, which will return a `Command` object.
+The `Command` object will then be executed by calling the `Command#execute()` method, which will call the
+`RecipeList#clearRecipeList()` to clear all the recipes from the recipe list.
+
+> The following sequence diagrams shows how the recipe manage feature works:
+![Sequence Diagram for Recipe Manage ADD and LIST](./PlantUML/RecipeManage_Add_List.png)
+![Sequence Diagram for Recipe Manage CLEAR and DELETE](./PlantUML/RecipeManage_Delete_Clear.png)
+
+
+### Recipe Find Feature
+#### Implementation
+The recipe find feature is facilitated by the `command`,`parser`,`recipe` package.
+It implements the following operations:
+- `RecipeList#searchRecipeList()` - Find a recipe from the recipe list.
+
+#### Example Usage
+Given below is an example usage scenario and how the recipe find mechanism behaves at each step.
+
+In the command line, the user inputs `find MaLaXiangGuo` to find a recipe from the recipe list. `Duke` calls the
+`parseCommands()` method in the `Parser` class to parse the user input, which will return a `Command` object.
+The `Command` object will then be executed by calling the `Command#execute()` method, which will call
+the `RecipeList#searchRecipeList()` to search the recipe list for the recipe. Eventually, the `UI` will be called to
+show the recipe list that contains the keyword with its index.
+
+> The following sequence diagram shows how the recipe find feature works:
+![Sequence Diagram for Recipe Find](./PlantUML/FindRecipe.png)
+
+### Recipe Steps Edit Feature
+#### Implementation
+The recipe steps edit feature is handled by the `command`, `recipe` and `stepList` classes.
+The following operations are implemented:
+* `RecipeList#getRecipe()` - Retrieves the `recipe` object to be edited.
+* `Recipe#getStepList()` - Retrieves the `StepList` of the recipe object to be edited.
+* `StepList#editStep()` - Takes in a `stepIndex` of the step to edit, and the user's input,
+then replaces the step stored at `stepIndex` with a newly created one.
+
+#### Example Usage
+The example usage is based on the assumption that there currently exists at least one step in
+the recipe specified.
+
+**Step 1.** In the command line, user inputs `editstep 1` to edit the step in the first `Recipe`
+object in the `RecipeList`. `Duke` calls the `parseCommands()` method in the `Parser` class to
+parse the user input, which returns a `Command` object of type `EDITSTEP`. Under
+`Command#execute()`, this object will be executed.
+
+**Step 2.** Under the `EDITSTEP` case, the number of steps in the specified recipe is first checked.
+If there is at least one step in the recipe, a further user input `1` is parsed to an `int` to specify
+the step number in the list. It is further converted to the 0-based indexing in the `stepList` by subtracting 1,
+to create the required `stepIndex`.
+
+**Step 3.** The `editStep` method under `StepList` then takes in a user input for the description of the
+replacement step. It then creates a new `Step` object with this description, then replaces the `Step` object stored at
+`stepIndex`with this new `Step` object.
+
+> The following sequence diagram shows how the recipe steps edit feature works:
+![Sequence Diagram for Recipe Edit Step](./PlantUML/EditRecipeSteps.png)
+
+### Recipe Ingredients Edit Feature
+#### Implementation
+
+The recipe Ingredient edit feature is handled by the `command`, `recipe` and `ingredientList` classes.
+ The following operations are implemented:
+
+* `RecipeList#getRecipe()` - Retrieves the recipe object to be edited.
+* `Recipe#getIngredientList()` - Retrieves the IngredientList of the recipe object to be edited.
+* `IngredientList#editIngredient()` - Takes in an ingredientIndex of the ingredient to edit, and the user's input,
+then replaces the ingredient stored at ingredientIndex with a newly created one.
+
+#### Example Usage
+
+The example usage is based on the assumption that there currently exists at least one ingredient in
+the recipe specified.
+
+**Step 1.** In the command line, user inputs `editingredient 1` to edit the ingredient in the first `Recipe`
+object in the `RecipeList`. `Duke` calls the `parseCommands()` method in the `Parser` class to
+parse the user input, which returns a `Command` object of type `EDITINGREDIENT`. Under
+`Command#execute()`, this object will be executed.
+
+**Step 2.** Under the `EDITINGREDIENT` case, the number of steps in the specified recipe is first checked.
+If there is at least one ingredient in the recipe, a further user input `1` is parsed to an `int` to specify
+the ingredient number in the list. It is further converted to the 0-based indexing in the `ingredientList` by subtracting 1,
+to create the required `ingredientIndex`.
+
+**Step 3.** The `editIngredient` method under `IngredientList` then takes in a user input for the description of the
+replacement ingredient. It then creates a new `Ingredient` object with this description, then replaces the `Ingredient` object stored at
+`ingredientIndex`with this new `Ingredient` object.
+
+> The following sequence diagram shows how the recipe ingredients edit feature works:
+![Sequence Diagram for Recipe Ingredients](./PlantUML/EditRecipeIngredients.png)
+
+
+### Recipe View Feature
+#### Implementation
+
+Viewing recipes is handled by the `command`, `recipe` and `ui` classes
+The following operations are implemented:
+* `RecipeList#getRecipeFromList()` - Retrieves a Recipe from the RecipeList
+* `Recipe#getIngredientList()` - Retrieves the IngredientList for the Recipe
+* `Recipe#getStepList()` - Retrieves the StepList for the Recipe
+* `UI#showRecipeViewed()`- Prints the IngredientList and StepList for the recipe
+
+#### Example Usage
+The example usage is based on the assumption that there currently exists at least
+one Recipe stored in the RecipeList.
+
+**Step 1.** In the command line, the user inputs `view 1` to view the first `Recipe` object
+in the `RecipeList`. `Duke` calls the `parseCommands()` method in the `Parser` class to
+parse the user input, which returns a `Command` object of type `VIEW`. Under
+`Command#execute()`, this object will be executed.
+
+**Step 2.** Under the `VIEW` case, the second part of the user's input `1` is parsed to an `int` to obtain the item
+number in the list.
+
+**Step 3.** The method `RecipeList#getRecipeFromList()` is called to retrieve the desired recipe
+to be viewed. This method then converts the 1-based item number to the 0-based indexing of `RecipeList`,
+then returns the `Recipe` object stored at that index using `recipeList.get()`
+
+**Step 4.** The `Command#execute()` method under case `VIEW` then calls `UI#ShowRecipeViewed` with the retrieved
+`Recipe` object as an input parameter. This method calls `Recipe#getIngredientList()` to obtain the
+`IngredientList` object for the Recipe, then calls `IngredientList#showList()` to print out the ingredients
+for the recipe. The method then follows a similar approach for the steps in the recipe, calling `Recipe#getStepList`
+and then `StepList#showStepList()`.
+
+> The following sequence diagram shows how the recipe view feature works:
+![Sequence Diagram for Recipe View](./PlantUML/RecipeView.png)
+
+### Help Feature
+#### Implementation
+The help feature's main functionality is to show users the full list of commands they can use on TOM.
+It is facilitated by the `command`,`parser`,`ui` package. It implements the following operations:
+
+- `UI#showHelp()` - Prints the help message.
+- `Parser#parseCommands()` - Parse user input into a Command object containing commandType and fullDescription.
+- `Command#excecute()` - Carry out respective tasks based on commandType given.
+
+#### Example Usage
+
+Given below is an example usage scenario and how the help mechanism behaves at each step.
+
+**Step 1.** The user launches the application for the first time, then inputs `help` to see all possible commands that
+can be executed. `Duke` calls the `parseCommands()` method in the `Parser` class to parse the user input, which will
+return a `Command` object. The `Command` object will then be executed by calling the `Command#execute()` method,
+which will call the `UI#showHelp()` method to show all possible commands of the recipe.
+
+> The following sequence diagram shows how the help feature works:
+![Sequence Diagram for Help](./PlantUML/Help.png)
+
+## Appendix A - Product Scope
+### Target User Profile
+
+Product is geared towards users who are familiar with CLI (e.g. Computing professionals, university students).
+The user is ideally someone who is conscious about their health and would like to learn/improve their cooking.
+
+### Value Proposition
+
+The user will be able to keep a database of recipes for home cooking, and be able to view both the recipes as well as
+attributes such as calorie count and required ingredients.
+It will also allow them to follow recipes in a step-by-step fashion with additional assistance functions such as timers.
+The user will be able to keep close tabs on their nutrition based on the recipes that they decide to cook and add to their meal plans.
+
+## Appendix B - User Stories
+
+| Version | As a ... | I want to be able to ... | So that I can... |
+|:-------:|:---------------:|:-------------------------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------:|
+| v1.0 | potential user | read the User Guide easily | I get to know the feature of the app and get started quickly |
+| v1.0 | potential user | add ingredients list to a particular recipes | refer to it when I go shopping for ingredients |
+| v1.0 | new user | initially see the estimated cooking time for recipes | choose the faster ones when in a rush |
+| v1.0 | new user | see basic instructions for the first time I use the app | avoid having to keep referring back to the user guide. |
+| v1.0 | new user | ask the app to provide a format guide for me when I type the wrong format for an instruction | get started quickly. |
+| v1.0 | new user | see a full list of recipes currently available | get an overview of what is already available |
+| v1.0 | new user | add recipes | add the ones i like |
+| v1.0 | new user | delete recipes | remove the ones i don't like |
+| v1.0 | new user | edit the recipe | correct spelling mistakes when typing the recipe |
+| v1.0 | new user | go through the recipe line by line | follow the recipe in real time while i cook |
+| v1.0 | long-term user | mark the steps I have done for the recipe | be aware of my next steps as I am cooking. |
+| v2.0 | proficient user | trigger certain recipes to be displayed once the app launches | get quick access to my regular cooking recipes |
+| v2.0 | proficient user | customize the shortcut commands | customize the keystrokes to my own preferences |
+| v2.0 | potential user | see the app with sample data and can easily manage to delete it after the exploration of the app. | Iunderstand the function of the app easily. |
+| v2.0 | potential user | craft meal plans for different days | plan my meals ahead for the week |
+| v2.0 | new user | register my dietary requirements / restrictions | avoid eating food I cannot eat. |
+| v2.0 | new user | start an automatic timer when required | avoid forgetting to set a timer when the recipe calls for it |
+| v2.0 | new user | see that the app can provide fuzzy search | access recipes even if I type the name wrongly |
+| v2.0 | new user | learn more about the shortcut commands | easily navigate through the app interface quicker |
+| v2.0 | long-time user | press any key if prompted to continue to the next step | use my elbow instead of my oily hands on the keyboard |
+| v2.0 | long-time user | get warnings if my fat/sugar intake based on recent dishes is too high | better regulate my diet |
+| v2.0 | long-term user | sort through stored recipes based on the dishes' nutritional value. | better regulate my diet. |
+| v2.0 | long-term user | sort by cuisines. | select a particular cuisine |
+| v2.0 | long-term user | add my own notes when I’m done cooking | comment about and adjust the recipe to my liking (e.g. less sweet, less salty) |
+| v2.0 | long-term user | hide dishes that I am tired of or tried and do not like | spend less time filtering through dishes |
+| v2.0 | long-term user | “favorite” dishes that I enjoy | quickly select them |
+| v2.0 | expert user | get the app to randomly suggest one of my favorite snacks | have help in making decisions on what snacks to eat when I hesitate to choose one |
+| v2.0 | expert user | rate and comment on the recipe | choose my favorite recipe by using the ratings |
+
+
+## Appendix C - Non-Functional Requirements
+
+* Users should be able to run on **any common operating system (Windows, Mac, Linux).**
+* Users should not need to manipulate any files in the directory **manually**.
+* Users should be able to run all functions of the program **on the CLI only (i.e. keyboard inputs only)**.
+
+## Appendix D - Glossary
+
+* *Recipe* - A set of instructions for preparing a food item. In our implementation it should contain the dish's name,
+ingredients required and steps to make the dish
+* *Cuisine* - A category of food originating from a given country or religion
+
+## Appendix E - Instructions for Manual Testing
+
+### Initialization
+1. Download the `.jar` file and place it in an empty folder.
+2. Open the folder location in a command-line interface. (E.g. Terminal for Windows)
+3. Type in `java -jar .\.jar` where `JAR_FILE_NAME` is the name of the `.jar` file downloaded.
+4. A successful launch should show a `data` folder being created and a welcome message as such:
+> ![Welcome Message](./Pictures/welcomeMessage.png)
+
+### Appendix E.1 - Adding a recipe
+Adding a person by using the `add` command and the recipe to be added.
+1. Test case:
+ `add n/Hotpot i/Beef, Potatoes, Carrots t/Chinese s/4`
+ `chop beef`
+ `add potatoes`
+ `add carrots`
+ `cook 5 minutes`
+
+ Expected: Recipe is added to the list and the message is shown in the result display.
+ ```
+ Got it. I've added this recipe:
+ [Chinese] Hotpot
+ Now you have 1 recipes in the list.
+ ```
+2. Test case:
+ `add i/Beef, Potatoes, Carrots t/Chinese s/0`
+
+ Expected: No recipe is added. Error details shown in the result display.
+ ```
+ Error in description of inputs!
+ Exception occurred:
+ Recipe is missing the "NAME" or "INGREDIENTS" or "TAG" or "SUM of the STEPs,
+ or there is more than one
+ "NAME" or "INGREDIENTS" or "TAG" or "SUM of the STEPs"!
+ ```
+
+3. Other incorrect add commands to try:
+ 1. `add`,
+ 2. `add x` (where x does not follow the correct format),
+ 3. `add n/` (where name is empty).
+
+ Expected: Similar to previous.
+
+### Appendix E.2 - Deleting a recipe
+Deleting a person by using the `delete` command and the index of the recipe to be deleted.
+1. Test case: `delete 1`
+
+ Expected: First recipe is deleted from the list and the message is shown in the result display.
+ ```
+ Noted. I've removed this recipe:
+ [TAG] NAME
+ you have XX recipes in the list.
+ ```
+2. Test case: `delete 0`
+
+ Expected: No recipe is deleted. Error details shown in the result display.
+ ```
+ Please enter a valid index!
+ Valid range: 1 to 1
+ ```
+3. Other incorrect delete commands to try:
+ 1. `delete`,
+ 2. `delete x` (where x is larger than the list size),
+ 3. `delete x` (where x is a negative integer or zero),
+ 4. `delete XX` (where XX is not a number).
+
+ Expected: Similar to previous.
-{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing}
diff --git a/docs/Pictures/welcomeMessage.png b/docs/Pictures/welcomeMessage.png
new file mode 100644
index 0000000000..118ef249d5
Binary files /dev/null and b/docs/Pictures/welcomeMessage.png differ
diff --git a/docs/PlantUML/ArchitectureInteract.png b/docs/PlantUML/ArchitectureInteract.png
new file mode 100644
index 0000000000..2fea73136f
Binary files /dev/null and b/docs/PlantUML/ArchitectureInteract.png differ
diff --git a/docs/PlantUML/ArchitectureInteract.puml b/docs/PlantUML/ArchitectureInteract.puml
new file mode 100644
index 0000000000..979d390398
--- /dev/null
+++ b/docs/PlantUML/ArchitectureInteract.puml
@@ -0,0 +1,48 @@
+@startuml ArchitectureInteract
+box Manager #White
+Actor User #Blue
+participant ":UI" as UI #Gray
+participant ":Duke" as Duke #Gold
+participant ":Parser" as Parser #Yellow
+participant ":Command" as Command #Beige
+participant ":RecipeList" as RecipeList #OrangeRed
+participant ":Storage" as Storage #DimGray
+end box
+
+activate Duke #Gold
+Duke -> UI : readCommand()
+activate UI #Gray
+User -> UI : "delete 1"
+UI --> Duke : "delete 1"
+deactivate UI
+Duke -> Parser : parseCommand()
+activate Parser #Yellow
+Parser --> Duke
+deactivate Parser
+Duke -> Command : execute()
+activate Command #Beige
+Command -> RecipeList : removeRecipe()
+activate RecipeList #OrangeRed
+RecipeList --> Command
+deactivate RecipeList
+Command -> UI : showRecipeDeleted()
+activate UI #Gray
+UI -> User : delete successful message
+UI --> Command
+deactivate UI
+Command -> Storage : writeSavedFile()
+activate Storage #DimGray
+loop number of recipes times
+Storage -> Storage : write()
+activate Storage #DimGray
+Storage --> Storage
+deactivate Storage
+end loop
+Storage --> Command
+deactivate Storage
+Command --> Duke
+Duke -> Command : delete
+deactivate Command
+destroy Command
+
+@enduml
\ No newline at end of file
diff --git a/docs/PlantUML/CommandComponent.png b/docs/PlantUML/CommandComponent.png
new file mode 100644
index 0000000000..4a4c17fcf5
Binary files /dev/null and b/docs/PlantUML/CommandComponent.png differ
diff --git a/docs/PlantUML/CommandComponent.puml b/docs/PlantUML/CommandComponent.puml
new file mode 100644
index 0000000000..570b390a8b
--- /dev/null
+++ b/docs/PlantUML/CommandComponent.puml
@@ -0,0 +1,26 @@
+@startuml
+
+class Command implements StringLib {
+ - type: CommandType
+ - fullDescription: String
+ + isExit(): boolean
+ + execute(recipeList: RecipeList, ui: UI): void
+}
+
+enum CommandType {
+ LIST
+ ADD
+ DELETE
+ FIND
+ CLEAR
+ VIEW
+ HELP
+ EXIT
+ UNKNOWN
+}
+
+Command --> CommandType
+IncompleteInputException --|> Exceptions : extends
+Command ..> IncompleteInputException : uses
+
+@enduml
\ No newline at end of file
diff --git a/docs/PlantUML/EditRecipeIngredients.png b/docs/PlantUML/EditRecipeIngredients.png
new file mode 100644
index 0000000000..2d7a5debf2
Binary files /dev/null and b/docs/PlantUML/EditRecipeIngredients.png differ
diff --git a/docs/PlantUML/EditRecipeIngredients.puml b/docs/PlantUML/EditRecipeIngredients.puml
new file mode 100644
index 0000000000..7ce93a9f86
--- /dev/null
+++ b/docs/PlantUML/EditRecipeIngredients.puml
@@ -0,0 +1,62 @@
+@startuml EditRecipeIngredients
+box Edit Ingredient #White
+Actor User
+participant ":UI" as UI #Gray
+participant ":Duke" as Duke #Gold
+participant ":Parser" as Parser #Yellow
+participant ":Command" as Command #Beige
+participant ":RecipeList" as RecipeList #OrangeRed
+participant ":StepList" as IngredientList #LightGreen
+end box
+
+activate Duke #Gold
+Duke -> UI : readCommand()
+activate UI #Gray
+User -> UI : "editingredient 2"
+UI --> Duke : "editingredient 2"
+deactivate UI
+Duke -> Parser : parseCommand()
+activate Parser #Yellow
+Parser --> Duke
+deactivate Parser
+Duke -> Command : execute()
+activate Command #Beige
+Command -> RecipeList : getRecipeFromList()
+activate RecipeList #OrangeRed
+RecipeList --> Command
+deactivate RecipeList
+Command -> IngredientList: getIngredientList()
+activate IngredientList #LightGreen
+IngredientList --> Command
+deactivate IngredientList
+Command -> IngredientList : showList()
+activate IngredientList #LightGreen
+IngredientList -> User : ingredient list message
+IngredientList --> Command
+deactivate IngredientList
+Command -> UI : showEditRecipeIngredientPrompt()
+activate UI #Gray
+UI -> User : edit ingredient request message
+UI --> Command
+deactivate UI
+Command -> UI : readCommand()
+activate UI #Gray
+User -> UI : "3"
+UI --> Command : "3"
+deactivate UI
+Command -> UI : readCommand()
+activate UI #Gray
+User -> UI : "Pineapples"
+UI --> Command : "Pineapples"
+deactivate UI
+Command -> IngredientList : editIngredient()
+activate IngredientList #LightGreen
+IngredientList -> User : edit successful message
+IngredientList --> Command
+deactivate IngredientList
+Duke -> Command : delete
+deactivate Command
+destroy Command
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/PlantUML/EditRecipeSteps.png b/docs/PlantUML/EditRecipeSteps.png
new file mode 100644
index 0000000000..a27fa6366b
Binary files /dev/null and b/docs/PlantUML/EditRecipeSteps.png differ
diff --git a/docs/PlantUML/EditRecipeSteps.puml b/docs/PlantUML/EditRecipeSteps.puml
new file mode 100644
index 0000000000..543dfad2cb
--- /dev/null
+++ b/docs/PlantUML/EditRecipeSteps.puml
@@ -0,0 +1,62 @@
+@startuml EditRecipeSteps
+box Edit Step #White
+Actor User
+participant ":UI" as UI #Gray
+participant ":Duke" as Duke #Gold
+participant ":Parser" as Parser #Yellow
+participant ":Command" as Command1 #Beige
+participant ":RecipeList" as RecipeList #OrangeRed
+participant ":StepList" as StepList #LightGreen
+end box
+
+activate Duke #Gold
+Duke -> UI : readCommand()
+activate UI #Gray
+User -> UI : "editstep 2"
+UI --> Duke : "editstep 2"
+deactivate UI
+Duke -> Parser : parseCommand()
+activate Parser #Yellow
+Parser --> Duke
+deactivate Parser
+Duke -> Command1 : execute()
+activate Command1 #Beige
+Command1 -> RecipeList : getRecipeFromList()
+activate RecipeList #OrangeRed
+RecipeList --> Command1
+deactivate RecipeList
+Command1 -> StepList: getStepList()
+activate StepList #LightGreen
+StepList --> Command1
+deactivate StepList
+Command1 -> StepList : showFullStepList()
+activate StepList #LightGreen
+StepList -> User : full steps list message
+StepList --> Command1
+deactivate StepList
+Command1 -> UI : showEditRecipeStepPrompt()
+activate UI #Gray
+UI -> User : edit step request message
+UI --> Command1
+deactivate UI
+Command1 -> UI : readCommand()
+activate UI #Gray
+User -> UI : "3"
+UI --> Command1 : "3"
+deactivate UI
+Command1 -> StepList : editStep()
+activate StepList #LightGreen
+StepList -> UI : readCommand()
+activate UI #Gray
+User -> UI : "Cook for 20 mins"
+UI --> StepList : "Cook for 20 mins"
+deactivate UI
+StepList -> User : edit successful message
+StepList --> Command1
+deactivate StepList
+Duke -> Command1 : delete
+deactivate Command1
+destroy Command1
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/PlantUML/FindRecipe.png b/docs/PlantUML/FindRecipe.png
new file mode 100644
index 0000000000..e4322a744b
Binary files /dev/null and b/docs/PlantUML/FindRecipe.png differ
diff --git a/docs/PlantUML/FindRecipe.puml b/docs/PlantUML/FindRecipe.puml
new file mode 100644
index 0000000000..a493ddee88
--- /dev/null
+++ b/docs/PlantUML/FindRecipe.puml
@@ -0,0 +1,34 @@
+@startuml FindRecipe
+box Find #White
+Actor User
+participant ":UI" as UI #Gray
+participant ":Duke" as Duke #Gold
+participant ":Parser" as Parser #Yellow
+participant ":Command" as Command #Beige
+participant ":RecipeList" as RecipeList #OrangeRed
+end box
+
+activate Duke #Gold
+Duke -> UI : readCommand()
+activate UI #Gray
+User -> UI : "Find MaLaXiangGuo"
+UI --> Duke : "Find MaLaXiangGuo"
+deactivate UI
+Duke -> Parser : parseCommand()
+activate Parser #Yellow
+Parser --> Duke
+deactivate Parser
+Duke -> Command : execute()
+activate Command #Beige
+Command -> RecipeList : searchRecipeList()
+activate RecipeList #OrangeRed
+RecipeList -> User : show recipe with index
+RecipeList --> Command
+deactivate RecipeList
+Command --> Duke
+Duke -> Command : delete
+deactivate Command
+destroy Command
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/PlantUML/Help.png b/docs/PlantUML/Help.png
new file mode 100644
index 0000000000..35be17feea
Binary files /dev/null and b/docs/PlantUML/Help.png differ
diff --git a/docs/PlantUML/Help.puml b/docs/PlantUML/Help.puml
new file mode 100644
index 0000000000..8a8f6c3350
--- /dev/null
+++ b/docs/PlantUML/Help.puml
@@ -0,0 +1,33 @@
+@startuml Help
+box Help #White
+Actor User
+participant ":UI" as UI #Gray
+participant ":Duke" as Duke #Gold
+participant ":Parser" as Parser #Yellow
+participant ":Command" as Command #Beige
+end box
+
+activate Duke #Gold
+Duke -> UI : readCommand()
+activate UI #Gray
+User -> UI : "help"
+UI --> Duke : "help"
+deactivate UI
+Duke -> Parser : parseCommand()
+activate Parser #Yellow
+Parser --> Duke
+deactivate Parser
+Duke -> Command : execute()
+activate Command #Beige
+Command -> UI : showHelp()
+activate UI #Gray
+UI -> User : help message
+UI --> Command
+deactivate UI
+Command --> Duke
+Duke -> Command : delete
+deactivate Command
+destroy Command
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/PlantUML/MainArchitecture.png b/docs/PlantUML/MainArchitecture.png
new file mode 100644
index 0000000000..a2e4381ba8
Binary files /dev/null and b/docs/PlantUML/MainArchitecture.png differ
diff --git a/docs/PlantUML/MainArchitecture.puml b/docs/PlantUML/MainArchitecture.puml
new file mode 100644
index 0000000000..44f4398d85
--- /dev/null
+++ b/docs/PlantUML/MainArchitecture.puml
@@ -0,0 +1,22 @@
+@startuml MainArchitecture
+
+archimate #Technology "User" as User <>
+archimate #Technology "Data" as Data <>
+
+
+rectangle RM {
+ rectangle UI
+ rectangle Duke
+ rectangle Parser
+ rectangle Storage
+ rectangle RecipeList
+ rectangle Command
+ User -down.> UI
+ Storage .> Data
+ Duke -up-> UI
+ Duke -> Command
+ Command -> RecipeList
+ Duke -up-> Parser
+ Storage -up-> RecipeList
+}
+@enduml
diff --git a/docs/PlantUML/ParserComponent.png b/docs/PlantUML/ParserComponent.png
new file mode 100644
index 0000000000..755b1d9f66
Binary files /dev/null and b/docs/PlantUML/ParserComponent.png differ
diff --git a/docs/PlantUML/ParserComponent.puml b/docs/PlantUML/ParserComponent.puml
new file mode 100644
index 0000000000..54d8a6e7a2
--- /dev/null
+++ b/docs/PlantUML/ParserComponent.puml
@@ -0,0 +1,23 @@
+@startuml ParserComponent
+
+class Parser implements StringLib {
+ +parseCommands(line: String): Command
+ +parseRecipe(description: String): ArrayList
+ -matchString(input: String, regex: String): boolean
+ -RECIPE_WRONG_NAME_INGREDIENTS_TAG_STEP: String
+ -RECIPE_MISSING_NAME: String
+ -RECIPE_MISSING_INGREDIENTS: String
+ -RECIPE_MISSING_TAG: String
+ -RECIPE_MISSING_STEP: String
+}
+
+class IncompleteInputException {
+}
+
+class Exceptions {
+}
+
+IncompleteInputException --|> Exceptions : extends
+Parser ..> IncompleteInputException : uses
+
+@enduml
\ No newline at end of file
diff --git a/docs/PlantUML/RecipeListComponent.png b/docs/PlantUML/RecipeListComponent.png
new file mode 100644
index 0000000000..1cfad64420
Binary files /dev/null and b/docs/PlantUML/RecipeListComponent.png differ
diff --git a/docs/PlantUML/RecipeListComponent.puml b/docs/PlantUML/RecipeListComponent.puml
new file mode 100644
index 0000000000..56a9a58d52
--- /dev/null
+++ b/docs/PlantUML/RecipeListComponent.puml
@@ -0,0 +1,68 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+class RecipeList {
+ - recipeList:ArrayList
+ - currRecipeNumber: int
+ + getRecipeList(): ArrayList
+ + getCurrRecipeNumber(): int
+ + getRecipeFromList(itemNum:int): Recipe
+ + getNewestRecipe(): Recipe
+ + addNewRecipe(recipe: Recipe)
+ + removeRecipe(index: int)
+ + clearRecipeList()
+}
+class Recipe {
+ - ingredientList: IngredientList
+ - stepList: StepList
+ - name: String
+ - tag: String
+ + getIngredientList(): IngredientList
+ + getStepList(): StepList
+ + getName(): Name
+ + getTag(): String
+ + toString(): String
+}
+
+class IngredientList {
+ - list: ArrayList
+ - currIngredientNumber: int
+ + getCurrIngredientNumber(): int
+ + addIngredient(item: Ingredient)
+ + removeIngredient(index: int)
+ + showList()
+ + getList(): ArrayList
+}
+
+class Ingredient {
+ - name: String
+ + getName(): String
+
+}
+
+class StepList {
+ - stepList: ArrayList
+ - currStepNumber: int
+ + addStep(step: Step)
+ + removeStep(stepIndex: int)
+ + showStepList()
+ + getList(): ArrayList
+}
+
+class Step {
+ - description: String
+ + getStep(): String
+ + toString(): String
+}
+class RecipeListEmptyError {
+}
+class Exceptions {
+}
+RecipeList -> "*" Recipe : stores >
+Recipe -> "1" IngredientList : contains >
+Recipe --> "1" StepList : contains >
+IngredientList --> Ingredient : stores >
+StepList --> Step : stores >
+RecipeList ..> RecipeListEmptyError : uses
+RecipeListEmptyError --|> Exceptions : extends
+@enduml
\ No newline at end of file
diff --git a/docs/PlantUML/RecipeManage_Add_List.png b/docs/PlantUML/RecipeManage_Add_List.png
new file mode 100644
index 0000000000..a4ae0a47aa
Binary files /dev/null and b/docs/PlantUML/RecipeManage_Add_List.png differ
diff --git a/docs/PlantUML/RecipeManage_Add_List.puml b/docs/PlantUML/RecipeManage_Add_List.puml
new file mode 100644
index 0000000000..728b7097f8
--- /dev/null
+++ b/docs/PlantUML/RecipeManage_Add_List.puml
@@ -0,0 +1,81 @@
+@startuml RecipeManage_Add_List
+box Add & List #White
+Actor User
+participant ":UI" as UI #Gray
+participant ":Duke" as Duke #Gold
+participant ":Parser" as Parser #Yellow
+participant ":Command" as Command1 #Beige
+participant ":Command" as Command2 #Beige
+participant ":RecipeList" as RecipeList #OrangeRed
+end box
+
+activate Duke #Gold
+Duke -> UI : readCommand()
+activate UI #Gray
+User -> UI : "add n/MaLaXiangGuo\n i/Beef, Mutton, Mushrooms t/Chinese s/1"
+UI --> Duke : "add n/MaLaXiangGuo\n i/Beef, Mutton, Mushrooms t/Chinese s/1"
+deactivate UI
+Duke -> Parser : parseCommand()
+activate Parser #Yellow
+Parser --> Duke
+deactivate Parser
+Duke -> Command1 : execute()
+activate Command1 #Beige
+Command1 -> Parser : parseRecipe()
+activate Parser #Yellow
+Parser --> Command1
+deactivate Parser
+Command1 -> Parser : parseIngredient()
+activate Parser #Yellow
+Parser --> Command1
+deactivate Parser
+Command1 -> Parser : parseSteps()
+activate Parser #Yellow
+loop sum of steps times
+Parser -> UI : readCommand()
+activate UI #Gray
+UI --> Parser
+deactivate UI
+Parser --> Command1
+end loop
+deactivate Parser
+Command1 -> RecipeList : addNewRecipe()
+activate RecipeList #OrangeRed
+RecipeList --> Command1
+deactivate RecipeList
+Command1 -> UI : showRecipeAdded()
+activate UI #Gray
+UI -> User : successful message
+UI --> Command1
+deactivate UI
+Command1 --> Duke
+Duke -> Command1 : delete
+deactivate Command1
+destroy Command1
+Duke -> UI : readCommand()
+activate UI #Gray
+User -> UI : "list"
+UI --> Duke : "list"
+deactivate UI
+Duke -> Parser : parseCommand()
+activate Parser #Yellow
+Parser --> Duke
+deactivate Parser
+Duke -> Command2 : execute()
+activate Command2 #Beige
+Command2 -> RecipeList : getRecipeList()
+activate RecipeList #OrangeRed
+RecipeList --> Command2
+deactivate RecipeList
+Command2 -> UI : showRecipeList()
+activate UI #Gray
+UI -> User : list of recipes message
+UI --> Command2
+deactivate UI
+Command2 --> Duke
+Duke -> Command2 : delete
+deactivate Command2
+destroy Command2
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/PlantUML/RecipeManage_Delete_Clear.png b/docs/PlantUML/RecipeManage_Delete_Clear.png
new file mode 100644
index 0000000000..ed5781b47c
Binary files /dev/null and b/docs/PlantUML/RecipeManage_Delete_Clear.png differ
diff --git a/docs/PlantUML/RecipeManage_Delete_Clear.puml b/docs/PlantUML/RecipeManage_Delete_Clear.puml
new file mode 100644
index 0000000000..6bdbd13060
--- /dev/null
+++ b/docs/PlantUML/RecipeManage_Delete_Clear.puml
@@ -0,0 +1,63 @@
+@startuml RecipeManage_Delete_Clear
+box Delete & Clear #White
+Actor User
+participant ":UI" as UI #Gray
+participant ":Duke" as Duke #Gold
+participant ":Parser" as Parser #Yellow
+participant ":Command" as Command3 #Beige
+participant ":Command" as Command4 #Beige
+participant ":RecipeList" as RecipeList #OrangeRed
+end box
+
+activate Duke #Gold
+Duke -> UI : readCommand()
+activate UI #Gray
+User -> UI : "delete 1"
+UI --> Duke : "delete 1"
+deactivate UI
+Duke -> Parser : parseCommand()
+activate Parser #Yellow
+Parser --> Duke
+deactivate Parser
+Duke -> Command3 : execute()
+activate Command3 #Beige
+Command3 -> RecipeList : removeRecipe()
+activate RecipeList #OrangeRed
+RecipeList --> Command3
+deactivate RecipeList
+Command3 -> UI : showRecipeDeleted()
+activate UI #Gray
+UI -> User : delete successful message
+UI --> Command3
+deactivate UI
+Command3 --> Duke
+Duke -> Command3 : delete
+deactivate Command3
+destroy Command3
+Duke -> UI : readCommand()
+activate UI #Gray
+User -> UI : "clear"
+UI --> Duke : "clear"
+deactivate UI
+Duke -> Parser : parseCommand()
+activate Parser
+Parser --> Duke
+deactivate Parser
+Duke -> Command4 : execute()
+activate Command4 #Beige
+Command4 -> RecipeList : clearRecipeList()
+activate RecipeList #OrangeRed
+RecipeList --> Command4
+deactivate RecipeList
+Command4 -> UI : showRecipeListCleared()
+activate UI #Gray
+UI -> User : clear successful message
+UI --> Command4
+deactivate UI
+Command4 --> Duke
+Duke -> Command4 : delete
+deactivate Command4
+destroy Command4
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/PlantUML/RecipeView.png b/docs/PlantUML/RecipeView.png
new file mode 100644
index 0000000000..444743dc0c
Binary files /dev/null and b/docs/PlantUML/RecipeView.png differ
diff --git a/docs/PlantUML/RecipeView.puml b/docs/PlantUML/RecipeView.puml
new file mode 100644
index 0000000000..a77f6fdb86
--- /dev/null
+++ b/docs/PlantUML/RecipeView.puml
@@ -0,0 +1,47 @@
+@startuml RecipeView
+box View #White
+Actor User
+participant ":UI" as UI #Gray
+participant ":Duke" as Duke #Gold
+participant ":Parser" as Parser #Yellow
+participant ":Command" as Command #Beige
+participant ":RecipeList" as RecipeList #OrangeRed
+participant ":IngredientList" as IngredientList #LightBlue
+participant ":StepList" as StepList #LightGreen
+end box
+
+activate Duke #Gold
+Duke -> UI : readCommand()
+activate UI #Gray
+User -> UI : "view 1"
+UI --> Duke : "view 1"
+deactivate UI
+Duke -> Parser : parseCommand()
+activate Parser #Yellow
+Parser --> Duke
+deactivate Parser
+Duke -> Command : execute()
+activate Command #Beige
+Command -> RecipeList : viewRecipe()
+activate RecipeList #OrangeRed
+RecipeList --> Command
+deactivate RecipeList
+Command -> UI : showRecipeViewed()
+activate UI #Gray
+UI -> IngredientList : getIngredientList()
+activate IngredientList #LightBlue
+IngredientList --> UI
+deactivate IngredientList
+UI -> StepList : getStepList()
+activate StepList #LightGreen
+StepList --> UI
+deactivate StepList
+UI -> User : recipe view message
+UI --> Command
+deactivate UI
+Duke -> Command : delete
+deactivate Command
+destroy Command
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/PlantUML/StorageComponent.png b/docs/PlantUML/StorageComponent.png
new file mode 100644
index 0000000000..508b49139b
Binary files /dev/null and b/docs/PlantUML/StorageComponent.png differ
diff --git a/docs/PlantUML/StorageComponent.puml b/docs/PlantUML/StorageComponent.puml
new file mode 100644
index 0000000000..42dabe362f
--- /dev/null
+++ b/docs/PlantUML/StorageComponent.puml
@@ -0,0 +1,79 @@
+@startuml
+
+@startuml
+
+class Recipe {
+ - name : String
+ - tag : String
+ - ingredientList : IngredientList
+ - stepList : StepList
+ + Recipe(String name, String tag, IngredientList ingredientList, StepList stepList)
+ + getName() : String
+ + getTag() : String
+ + getIngredientList() : IngredientList
+ + getStepList() : StepList
+}
+
+class Storage {
+ - DIRECTORY_CREATED : String
+ - DIRECTORY_EXISTS : String
+ - filePath : String
+ + setFilePath(String filePath) : void
+ + createDirectory() : void
+ + writeSavedFile() : void
+ + findValidSaveFiles() : ArrayList
+ + loadSaveFiles() : void
+}
+
+
+class Ingredient {
+ - name : String
+ + Ingredient(String name)
+ + getName() : String
+ + setName(String name) : void
+}
+
+class IngredientList {
+ - list : ArrayList
+ + IngredientList(ArrayList list)
+ + addIngredient(Ingredient ingredient) : void
+ + removeIngredient(int index) : void
+ + getList() : ArrayList
+}
+
+class RecipeList {
+ - recipeList : ArrayList
+ + addNewRecipe(Recipe recipe) : void
+ + removeRecipe(int index) : void
+ + getRecipeList() : ArrayList
+}
+
+class Step {
+ - step : String
+ + Step(String step)
+ + getStep() : String
+ + setStep(String step) : void
+}
+
+class StepList {
+ - list : ArrayList
+ + StepList(ArrayList list)
+ + addStep(Step step) : void
+ + removeStep(int index) : void
+ + getList() : ArrayList
+}
+
+
+
+StepList -> Step
+Storage --> Ingredient
+Storage --> IngredientList
+Storage -> Recipe
+Storage -> RecipeList
+Storage --> Step
+Storage --> StepList
+Recipe -> IngredientList
+Recipe -> StepList
+RecipeList -> Recipe
+IngredientList -> Ingredient
+@enduml
diff --git a/docs/PlantUML/UIcomponent.png b/docs/PlantUML/UIcomponent.png
new file mode 100644
index 0000000000..e34155191c
Binary files /dev/null and b/docs/PlantUML/UIcomponent.png differ
diff --git a/docs/PlantUML/UIcomponent.puml b/docs/PlantUML/UIcomponent.puml
new file mode 100644
index 0000000000..94e80bbcdc
--- /dev/null
+++ b/docs/PlantUML/UIcomponent.puml
@@ -0,0 +1,27 @@
+@startuml UIcomponent
+class UI implements StringLib {
+ -in: Scanner
+ +UI(): void
+ +showFindResults(ArrayList, String): void
+ +showRecipeList(ArrayList list): void
+ +showRecipeAdded(Recipe, int): void
+ +showRecipeDeleted(Recipe, int): void
+ +showRecipeEdited(Recipe, int): void
+ +showRecipeListCleared(): void
+ +showWelcome(): void
+ +showExit(): void
+ +showHelp(): void
+ +showLine(): void
+ +showStepInsertMessage(int): void
+ +showDudeMainError(Exception): void
+ +showUnrecognizableErrorMessage(): void
+ +showUnrecognizableCommandErrorMessage(): void
+ +showLoadingErrorMessage(Exception): void
+ +showAddingRecipeErrorMessage(Exception): void
+ +showDeletingTaskErrorMessage(Exception, CommandType): void
+ +showFindingTaskErrorMessage(Exception): void
+ +showRecipeViewed(Recipe): void
+ +showViewingRecipeErrorMessage(Exception): void
+ +showSave(): void
+}
+@enduml
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..cdf8641848 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,6 +1,8 @@
-# Duke
+# Taste of Mom's
+Taste of Mom's (TOM) is a desktop recipe manager application for managing recipes, optimized for use via a Command Line Interface (CLI).
-{Give product intro here}
+Our application will help students to **keep track and modify recipes** which they can learn to cook, so that they can enjoy the same taste of food made like as if they were at home as they live on campus.
+It also helps them filter recipes with ease.
Useful links:
* [User Guide](UserGuide.md)
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index abd9fbe891..41b5ac2949 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,42 +1,473 @@
-# User Guide
+---
+layout: page
+title: User Guide
+---
+
User Guide
+
Introduction
+Taste of Mom's (TOM) is a desktop recipe manager application for managing recipes, optimized for use via a Command Line Interface (CLI).
-## Introduction
+Our application will help students to **keep track and modify recipes** which they can learn to cook, so that they can enjoy the same taste of food made like as if they were at home as they live on campus.
+It also helps them filter recipes with ease.
+
Table of Contents
-{Give a product intro}
+* **[Quick Start](#quick-start)**
+* **[Features](#features)**
+ * **[Viewing help:`help`](#viewing-help-help)**
+ * **[Adding a recipe: `add`](#adding-a-recipe-add)**
+ * **[Adding an element to a recipe: `addtorecipe`](#adding-elements-to-recipe)**
+ * **[Editing steps for a recipe: `editstep`](#editing-a-recipe-step-editstep)**
+ * **[Editing ingredients for a recipe: `editingredient`](#editing-a-recipe-ingredient-editingredient)**
+ * **[Editing recipe with one line command:`edit`](#editing-a-recipe-edit)**
+ * **[Deleting a recipe: `delete`](#deleting-a-recipe-delete)**
+ * **[Deleting an element from a recipe: `deletefromrecipe`](#deleting-elements-from-recipe)**
+ * **[Finding recipes by name: `findname`](#finding-recipes-findname)**
+ * **[Finding recipes by tag: `findtag`](#finding-recipes-findtag)**
+ * **[Viewing a recipe: `view`](#viewing-a-recipe-view)**
+ * **[Listing all recipes: `list`](#listing-all-recipes-list)**
+ * **[Clearing all entries: `clear`](#clearing-all-entries-clear)**
+ * **[Exiting the program: `exit`](#exiting-the-program-exit)**
+ * **[Saving the data](#save-data)**
+ * **[Editing the data file](#edit-data)**
+* **[FAQ](#faq)**
+* **[Command Summary](#command-summary)**
-## Quick Start
+
Quick Start
-{Give steps to get started quickly}
+1. Ensure you have `Java 11` installed on your Computer.
+2. Download the latest `tp.main.jar` from [here](https://github.com/AY2223S2-CS2113-F13-1/tp/releases).
+3. Copy the file to an **empty folder** you want to use as home folder for the recipe manager.
+4. Open the command prompt.
+5. Then `cd` into the folder where you copied the jar file. e.g. `cd C:\Users\Lee\Desktop\MyRecipe`.
+6. Type `java -jar tp.main.jar` and press Enter to start the program.
+
Features
+
+
+Words in UPPER_CASE are the parameters to be supplied by the user.
+
+e.g. in add DESCRIPTION, DESCRIPTION is a parameter which can be used as add n/NAME i/INGREDIENTS t/TAG s/NUMBEROFSTEPS.
+
+
+Extraneous parameters for commands that do not take in parameters (such as exit and list) will be ignored.
-1. Ensure that you have Java 11 or above installed.
-1. Down the latest version of `Duke` from [here](http://link.to/duke).
+e.g. if the command specifies exit 123, it will be interpreted as exit.
+
+
+APP accept the same name for different recipes. For example, you can have two recipes named Hotpot.
+
+
+APP saves automatically when changes are made to the recipes on the recipe list or to the recipe list. There is no notification of the recipe list being saved.
+ The following commands will trigger the automatic saving after successful operation:
+1. add
+2. addtorecipe
+3. delete
+4. deletefromrecipe
+5. edit
+6. editstep
+7. editingredient
+
+
-## Features
+
Viewing help:help
-{Give detailed description of each feature}
+Shows a message explaining how to access the help page.
+**Format**: `help`
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
+
Adding a recipe: add
-Format: `todo n/TODO_NAME d/DEADLINE`
+Adds a recipe to the recipe manager. App will prompt you to start entering the steps to the recipe will automatically count the steps and add the recipe to the recipe list.
+**Format**: `add n/NAME i/INGREDIENTS t/TAG s/NUMBEROFSTEPS`
+**Hint**: Number of steps is the number of steps in the recipe, then you will be prompted to enter the steps.
+**Constraints**:
+* NAME, INGREDIENTS, TAG: These recipe parameters must only be ***alphanumeric*** in nature and should **not contain only numeric characters** or
+any other **special characters**.
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+**Examples**:
+```
+__________________________________________________________
+add n/Hotpot i/Beef, Potatoes, Carrots t/Chinese s/4
-Example of usage:
+Please enter the description of step 1:
+chop beef
-`todo n/Write the rest of the User Guide d/next week`
+Please enter the description of step 2:
+add potatoes
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
+Please enter the description of step 3:
+add carrots
-## FAQ
+Please enter the description of step 4:
+cook 5 minutes
-**Q**: How do I transfer my data to another computer?
+Got it. I've added this recipe:
+ [Chinese] Hotpot
+Now you have 3 recipes in the list.
-**A**: {your answer here}
+__________________________________________________________
+```
+
Adding an element to a recipe: addtorecipe
-## Command Summary
+Adds a step or ingredient to a particular recipe in the recipe list.
+App will prompt you to enter the exact index at which the element is to be added. Existing elements will be shifted down if there is any.
+**Format**: `addtorecipe --[s/i] id/[index] desc/[description of step/ingredient]`
+**Examples**:
+```
+__________________________________________________________
+addtorecipe --i id/1 desc/mala sauce
+
+The ingredient has been successfully added to the ingredient list!
+__________________________________________________________
+```
+```
+__________________________________________________________
+addtorecipe --s id/3 desc/Add mala sauce when water has reached a rolling boil.
+There are 4 steps in the list
+1. chop beef
+2. add potatoes
+3. add carrots
+4. cook 5 minutes
+Valid range: 1 to 5
+Enter "quit" to cancel.
+Enter step index below:
+4
+
+The step has been successfully added to the step list!
+__________________________________________________________
+```
+
App will add an element to the recipe with the given index.
+
If a step is added, the user will be prompted for an index which the new step will be given.
+
+
Editing a recipe's steps: editstep
+
+Edits a step for a recipe in the recipe manager.
+**Format**: `editstep INDEX`, then input the step number
+**Hint**: At the edit page, you can input `quit` if you decide not to edit.
+**Examples**:
+
+```
+__________________________________________________________
+editstep 2
+There is 1 step in the list
+1. chop beef
+Which step do you want to edit?
+Type 'quit' to exit the edit view
+1
+Enter the description of the step:
+go to haidilao
+Step has been edited:
+1. go to haidilao
+__________________________________________________________
+```
+```
+editstep 1
+There are 3 steps in the list
+1. chop beef
+2. add car
+3. eat food
+Which step do you want to edit?
+Type 'quit' to exit the edit view
+quit
+__________________________________________________________
+```
+The function will replace the specified step of a specified recipe with a new user input.
+First input the index of the recipe to edit, then input the step number you would like to edit.
+Following which, type in the description for the step.
+
+Note that to `exit` the whole program from the edit window, you have to `quit` first then `exit`.
+
+
Editing a recipe's ingredients: editingredient
+
+Edits an ingredient for a recipe in the recipe manager.
+**Format**: `editingredient INDEX`, then input the ingredient number
+**Hint**: At the edit page, you can input `quit` if you decide not to edit.
+**Examples**:
+
+```
+__________________________________________________________
+editingredient 1
+Here are 3 ingredients in the list:
+1. Beef
+2. Potatoes
+3. Carrots
+Which ingredient do you want to edit?
+Type 'quit' to exit the edit view
+2
+Enter the description of the ingredient:
+Chicken
+Ingredient has been edited:
+2. Chicken
+__________________________________________________________
+```
+```
+__________________________________________________________
+editingredient 1
+Here are 3 ingredients in the list:
+1. Beef
+2. Chicken
+3. Carrots
+Which ingredient do you want to edit?
+Type 'quit' to exit the edit view
+quit
+__________________________________________________________
+```
+
+
The function will replace the specified ingredient of a specified recipe with a new user input.
+First input the index of the recipe to edit, then input the ingredient number you would like to edit.
+Following which, type in the description for the ingredient.
+
+Note that to `exit` the whole program from the edit window, you have to `quit` first then `exit`.
+
+
Editing a recipe with one line command: edit
+
+For expert users, you can edit a recipe with one line command.
+For editing ingredients, you can use `edit --i` to edit ingredients.
+**Format**: `edit --i INDEXOFRECIPE INDEXOFINGREDIENT i/NEWINGREDIENT`
+**Examples**:
+```
+__________________________________________________________
+edit --i 1 2 i/pork
+Ingredient has been edited:
+2. pork
+__________________________________________________________
+```
+For editing steps, you can use `edit --s` to edit steps.
+**Format**: `edit --s INDEXOFRECIPE INDEXOFSTEP s/NEWSTEP`
+**Examples**:
+```
+__________________________________________________________
+edit --s 1 1 s/wash beef
+Step has been edited:
+1. wash beef
+__________________________________________________________
+```
+
+
Deleting a recipe: delete
+
+Deletes a recipe from the recipe list.
+**Format**: `delete INDEX`
+**Examples**:
+```
+__________________________________________________________
+delete 1
+
+Noted. I've removed this recipe:
+ [Chinese] Hotpot
+Now you have 1 recipe in the list.
+__________________________________________________________
+```
+
App will remove the recipe with the corresponding index.
+
+
Deleting an element from a recipe: deletefromrecipe
+
+Deletes either a step or an ingredient from a particular recipe in the recipe list.
+As shown in the format, the flag for `--[s/i]` can be used interchangeably, `s` to delete a step and `i` to delete an ingredient.
+**Format**: `deletefromrecipe --[s/i] id/[index]`
+**Examples**:
+```
+__________________________________________________________
+deletefromrecipe --i id/1
+There are 4 ingredients in the list:
+1. Beef
+2. Potatoes
+3. Carrots
+4. mala sauce
+Enter step index below:
+4
+The ingredient has been successfully deleted from the ingredient list!
+__________________________________________________________
+```
+```
+__________________________________________________________
+deletefromrecipe --s id/1
+There are 5 steps in the list
+1. chop beef
+2. add potatoes
+3. add carrots
+4. Add mala sauce when water has reached a rolling boil.
+5. cook 5 minutes
+Valid range: 1 to 5
+Enter "quit" to cancel.
+Enter step index below:
+4
+The step has been successfully deleted from the step list!
+__________________________________________________________
+```
+
App will remove the element of the second given index from the recipe with the first given index.
+
+
Finding recipes: findname
+
+Find recipes whose names contain any of the given keywords. The results will be displayed in a list, telling you the index of the recipe in the recipe list. Only the name of the recipe will be searched.
+
+**Format**: `findname KEYWORD`
+**Constraints**:
+* `KEYWORD` cannot be empty.
+
+**Examples**:
+```
+__________________________________________________________
+findname hotpot
+Here is the matching item:
+ [Chinese] Hotpot [Index: 1]
+__________________________________________________________
+```
+
+
Finding recipes: findtag
+
+Find recipes whose tag contain any of the given keywords. The results will be displayed in a list, telling you the index of the recipe in the recipe list. Only the tag of the recipe will be searched.
+**Format**: `findtag KEYWORD`
+**Constraints**:
+* `KEYWORD` cannot be empty.
+
+**Examples**:
+```
+__________________________________________________________
+findtag chinese
+Here is the matching item:
+ [Chinese] Hotpot [Index: 1]
+__________________________________________________________
+```
+
Viewing a recipe: view
+
+
Views a detailed recipe from the recipe manager if INDEX is entered.
+
+**Format**: `view INDEX` or `view NAME`
+**Constraints**:
+* INDEX: The index must be a positive integer within the range: `[1, total number of recipes in the list]`
+(Provided the list is not empty).
+* NAME: The **exact** full name of the recipe must be given as input, partial names will not be processed.
+
+**Examples**:
+
+* INDEX:
+
+```
+__________________________________________________________
+view 1
+Here is the recipe you requested, which is Chinese flavour:
+name: Hotpot
+__________________________________________________________
+Here are 3 ingredients in the list:
+1. Beef
+2. Potatoes
+3. Carrots
+__________________________________________________________
+There are 4 steps in the list
+Do you want to view step-by-step?
+Type "yes" if so
+yes
+To exit recipe view, type "quit"
+Else, press enter to continue
+1. chop beef
+
+2. add potatoes
+
+3. add carrots
+
+4. cook 5 minutes
+
+End of Recipe Steps!
+__________________________________________________________
+```
+
+* NAME:
+
+```
+__________________________________________________________
+view Hotpot
+Here is the recipe you requested, which is Chinese flavour:
+name: Hotpot
+__________________________________________________________
+Here are 3 ingredients in the list:
+1. Beef
+2. Potatoes
+3. Carrots
+__________________________________________________________
+There are 4 steps in the list
+Do you want to view step-by-step?
+Type "yes" if so
+yes
+To exit recipe view, type "quit"
+Else, press enter to continue
+1. chop beef
+
+2. add potatoes
+
+3. add carrots
+
+4. cook 5 minutes
+
+End of Recipe Steps!
+__________________________________________________________
+```
+
+
Listing all recipes: list
+
+This feature allows you to **list all recipes** you currently have. This feature is useful after using the add, edit and delete commands to make changes to the recipe list, as it shows you the updated list.
+**Format**: `list`
+**Examples**:
+* `list`
+
+
Clearing all entries: clear
+
+This feature allows you to **delete all recipes** that are currently stored on the recipe manager. Users may use this to clear all data when starting to use the app or when they wish to clear all data.
+**Format**: `clear`
+**Examples**:
+* `clear`
+
+
+The recipe manager 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
+
+The recipe data are saved as a text file `[JAR file location]/data/[NUMBER].txt`.
+Here is an example of a valid file, `data/1.txt`:
+```
+Hotpot
+Chinese
+3
+Beef
+Potatoes
+Carrots
+4
+chop beef
+add potatoes
+add carrots
+cook 5 minutes
+```
+The first line is the name of the dish. And the second line is the tag of the dish. The third line is number of the ingredient list X. The following X lines are the ingredients. Then the next line is the number of the step list Y. The following Y lines are the steps.
+
+***WARNING***:
+* Do **NOT** modify/delete the save files.
+* If you modify/delete the data file, and save data has **the wrong format**, the recipe manager will not process the data correctly.
+
+
FAQ
+Q: What happens if I don't format the parameter correctly?
+A: If you input the correct action name, the app will prompt you for the correct input format, otherwise the app will ignore this instruction.
+
+
Command Summary
+
+
+| Action | Format | Example |
+|-------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|
+| [**help**](#viewing-help-help) | `help` | |
+| [**add**](#adding-a-recipe-add) | `add n/NAME i/INGREDIENT ... t/TAG s/SUMOFSTEP [STEP]...` | `add n/Hotpot i/Beef, Potatoes, Carrots t/Chinese s/2` `add carrots` `cook 5 minutes` |
+| [**addtorecipe**](#adding-elements-to-recipe) | `addtorecipe --[s/i] id/[index] desc/[description of step/ingredient]` | `addtorecipe --i id/1 desc/mala sauce` |
+| [**editingredient**](#editing-a-recipe-ingredient-editingredient) | `editingredient INDEX` | `editingredient 1` |
+| [**editstep**](#editing-a-recipe-step-editstep) | `editstep INDEX` | `editstep 1` |
+| [**edit**](#editing-a-recipe-edit) | `edit --i INDEXOFRECIPE INDEXOFINGREDIENT i/NEWINGREDIENT` `edit --s INDEXOFRECIPE INDEXOFSTEP s/NEWSTEP` | `edit --i 1 2 i/pork` `edit --s 1 1 s/wash beef` |
+| [**delete**](#deleting-a-recipe-delete) | `delete INDEX` | `delete 1` |
+| [**deletefromrecipe**](#deleting-elements-from-recipe) | `deletefromrecipe --[s/i] id/[index]` | `deletefromrecipe --i id/1` |
+| [**findname**](#finding-recipes-findname) | `find KEYWORD ` | `find Hotpot` |
+| [**findtag**](#finding-recipes-findtag) | `find KEYWORD ` | `find Chinese` |
+| [**view**](#viewing-a-recipe-view) | `view INDEX` `view NAME` | `view 1` `view Hotpot` |
+| [**list**](#listing-all-recipes-list) | `list` | |
+| [**clear**](#clearing-all-entries-clear) | `clear` | |
+| [**exit**](#exiting-the-program-exit) | `exit` | |
-{Give a 'cheat sheet' of commands here}
-* Add todo `todo n/TODO_NAME d/DEADLINE`
diff --git a/docs/team/aung-phone-naing.md b/docs/team/aung-phone-naing.md
new file mode 100644
index 0000000000..4d14122db7
--- /dev/null
+++ b/docs/team/aung-phone-naing.md
@@ -0,0 +1,93 @@
+# Aung Phone Naing - Project Portfolio Page
+
+## Overview
+Taste of Mom's (TOM) is a **desktop recipe manager application for managing recipes**, optimized for use via a Command Line Interface (CLI).
+
+Our application will help students to **keep track and modify recipes** which they can learn to cook, so that they can enjoy the same taste of food made like as if they were at home as they live on campus.
+It also helps them filter recipes with ease.
+
+## Summary of Contributions
+Responsible for writing out the code base, basic structure of the app, setting up organization repository and managing issues tracker.
+
+### Code Contributed
+[RepoSense Link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=Aung%20Phone%20Naing&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+### Enhancements Implemented
+The following are my contributions to the project:
+
+* ### Skeletal Code Implementation
+ * **Parser, Command and Command Type Classes**
+ * **Description**: Provide the underlying methods and attributes necessary for developers to use and add on to,
+ which can help with identifying and handling user commands.
+ * **Justification**: These classes are the core of the program and central to standardising the way to handle user
+ inputs, providing a detailed structure in terms of executing various tasks.
+ * **Highlights**:
+ * The implementation requires a deep analysis of design alternatives to determine the best way to construct the
+ classes while ensuring they achieve the functional requirements.
+ * **Recipe and Recipe List Classes**
+ * **Description**: Provide the underlying methods and attributes necessary for developers to use and add on to,
+ which can help with managing of components in a recipe and the list of all recipes in the program.
+ * **Justification**: These classes are central to allowing users to create and manage recipes which make up the
+ essence of the app itself.
+ * **Highlights**:
+ * The classes provided as a base for which other developers use to expand to construct subclasses and attributes
+ to add further features to the program such as `StepList`, `Step`, `IngredientList` and `Ingredient`.
+ * **UI Class**
+ * **Description**: Provide methods and attributes to be used by other developers when it comes to handling user interactions.
+ * **Justification**: This provides a central platform in which developers can construct general methods for handling
+ similar user interactions which can be used across features.
+ * **Highlights**:
+ * The class ensures that there is a central location in which code can be easily refactored to handle any errors
+ in regard to user interactions.
+
+* ### New Features
+ * **View-name Command**
+ * **Description**: Provide users with the ability to view the contents of a specific recipe by simply using the
+ recipe name instead of index.
+ * **Justification**: This command provides users with greater convenience to view recipes that they already know the
+ name to so that they do not need to use the `Find` Command to get the recipe index before viewing.
+ * **Highlights**:
+ * This command requires comprehension of and working with code by other developers.
+ * A good comprehension of users' needs in order to determine the significance of this feature was required as well.
+ * **Invalid input handling for Add Command**
+ * **Description**: Catches any instance of users attempting to input invalid characters into recipe names, tags,
+ ingredients and steps.
+ * **Justification**: Such instances of inputting special characters "!@#$%" into names may potentially mess with
+ `Storage` or `Parser` class methods.
+ * **Highlights**:
+ * This prevents attempts by malicious users to crash the program or unintentional inputs leading to program termination.
+
+* ### Testing
+ Wrote tests for the following features:
+ * View
+ * Parser for adding a recipe
+
+### Contribution to Team-based Task
+
+1. Came up with the user stories.
+2. Managed releases v2.1 on GitHub.
+3. Managed issue tracker on GitHub. (e.g. adding labels, assigning issues to team members, closing issues, etc)
+4. Solving issues on Github: [#4](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/4) , [#7](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/7) , [#47](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/47) , [#48](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/48) , [#49](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/49) , [#65](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/65) , [#68](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/68) , [#72](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/72) , [#75](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/75) , [#76](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/76), [#84](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/84) , [#87](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/87) , [#91](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/91), [#126](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/126)
+
+### Documentation
+* **Contributions to User Guide**
+
+ Wrote the following segments of the user guide:
+ * `View` Feature
+ * `Help` Feature
+
+
+* **Contributions to Developer Guide**
+
+ Wrote the following segments of the developer guide:
+ * `Help` Feature
+ * Instructions for Manual Testing
+ * Sequence Diagrams for `View` and `Help` Features
+ * Class Diagrams for `Help` and `Command` Classes
+
+### Review/Mentoring Contributions
+* PRs reviewed (with non-trivial comments) : [#116](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/116) , [#115](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/115) , [#6](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/6)
+* PRs reviewed (all others) : [#1](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/1) , [#2](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/2) , [#27](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/27) , [#30](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/30) , [#38](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/38) , [#40](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/40) , [#45](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/45) , [#55](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/55) , [#57](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/57) , [#59](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/59) , [#107](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/107) , [#108](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/108) , [#114](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/114) , [#123](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/123) , [#131](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/131), [#133](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/133) , [#135](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/135) , [#142](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/142) , [#143](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/143) , [#148](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/148) , [#152](https://github.com/AY2223S2-CS2113-F13-1/tp/pull/152)
+
+### Contributions beyond the Project Team
+* [WELLNUS++](https://github.com/Aung-Phone-Naing/ped/issues)
\ No newline at end of file
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index ab75b391b8..0000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# John Doe - Project Portfolio Page
-
-## Overview
-
-
-### Summary of Contributions
diff --git a/docs/team/leongyatpang.md b/docs/team/leongyatpang.md
new file mode 100644
index 0000000000..21d848c0ed
--- /dev/null
+++ b/docs/team/leongyatpang.md
@@ -0,0 +1,60 @@
+# Leong Yat Pang - Project Portfolio Page
+
+## Overview
+Taste of Mom's is a CLI application for managing, storing and viewing recipes,
+optimized for use via a Command Line Interface (CLI)
+while still having the features of a recipe-managing application.
+
+
+Our app will help users through the process of cooking their favourite dishes stored within the app.
+
+## Summary of Contributions
+
+### Code Contributed
+[RepoSense Link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17&tabOpen=true&tabType=authorship&tabAuthor=YatPang&tabRepo=AY2223S2-CS2113-F13-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+### Enhancements Implemented to Taste of Mom's
+* **Saving Recipe List**
+Implemented the methods and functions to save recipes in recipe list automatically after changes are made to the recipe list.
+The app saves recipes in the `/data` directory as individual `.txt` files.
+
+This is one of the basic features of the application.
+* **Loading Saved Data**
+Implemented the methods and functions to load recipes saved from previous sessions.
+The app scans the `/data` directory for `.txt` files,
+and reads files individually to generate stored recipes.
+
+This is one of the basic features of the application.
+* **Searching Recipe List by Name**
+Implemented the methods and functions behind the command to search the recipe list for items with names that contain the searched term.
+* **Searching Recipe List by Tag**
+Implemented the methods and functions behind the command to search the recipe list for items with tags that contain the searched term.
+* **Adding an Element to Recipe**
+Implemented the command and logic to add step and ingredients to an existing recipe on the recipe list.
+* **Deleting an Element from Recipe**
+Implemented the methods and functions behind the command to delete step and ingredients to an existing recipe on the recipe list.
+
+### Contributions to UG
+Added user documentation to explain how to use the following commands:
+* `findname`
+* `findtag`
+* `addtorecipe`
+* `deletefromrecipe`
+
+### Contributions to DG
+* Added UML diagram and technical documentation to illustrate how recipe data is stored and loaded.
+![image](../PlantUML/StorageComponent.png)
+
+### Issues Resolved
+1. [Issue #66](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/66)
+2. [Issue #69](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/69)
+3. [Issue #70](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/70)
+4. [Issue #82](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/82)
+5. [Issue #83](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/83)
+6. [Issue #88](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/88)
+7. [Issue #90](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/90)
+8. [Issue #92](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/92)
+9. [Issue #102](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/102)
+10. [Issue #103](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/103)
+11. [Issue #120](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/120)
+12. [Issue #121](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/121)
\ No newline at end of file
diff --git a/docs/team/limhongyao.md b/docs/team/limhongyao.md
new file mode 100644
index 0000000000..c53775c633
--- /dev/null
+++ b/docs/team/limhongyao.md
@@ -0,0 +1,70 @@
+# Lim Hong Yao - Project Portfolio Page
+
+## Overview
+Taste of Mom’s (TOM) is a desktop recipe manager application for managing recipes,
+optimized for use via a Command Line Interface (CLI).
+
+This app aims to help people keep track of recipes they have seen online,
+and go through the recipes in a clear step-by-step manner.
+
+## Summary of Contributions
+Mainly responsible for the Step functionality of the app, as well as
+editing of Step and Ingredients in recipes.
+Additionally responsible for implementation of step-by-step viewing of
+recipes
+Contributed code can be viewed in RepoSense at this
+[link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17&tabOpen=true&tabType=authorship&tabAuthor=LimHongYao&tabRepo=AY2223S2-CS2113-F13-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false).
+
+### Function Implementations
+#### Creation of Step functionality
+**Description:** Allow each recipe to include steps for creating the dish
+* Implements basic functionality for recipes
+* Increase usefulness of app, working towards a goal of being a complete
+recipe management system.
+
+### New Features
+#### Editing support for Steps and Ingredients of a recipe
+**Description:** Functionality to change the description of an ingredient or
+a step of a recipe.
+* Increase user-friendliness, allows for editing of mistakes or future
+adjustments of recipe without needing to delete and re-create the recipe.
+* Allows for long-term use of the app and encourages users to continuously
+improve their recipes by giving the ability to edit recipes as better methods
+are devised.
+
+#### Support for viewing the steps of a recipe in a step-by-step manner
+**Description:** Option to choose to view the steps of the recipe in a
+step-by-step manner.
+
+* Prevents information overload especially for complex recipes. Allows users
+to view the steps one at a time as they execute the steps.
+* Makes following recipes easier and increases overall user-friendliness.
+* Clear advantage over simply viewing the recipe from the website or from a book
+### UG Contributions
+Contributed to the user guide for the `editstep`, `editingredient` and `view` step-by-step functions
+
+### DG Contributions
+Contributed initial formatting setup of the DG.
+Also contributed the DG and respective PlantUML diagrams for `editstep`, `editingredient` and `view` functions
+### Resolved Issues
+1. [#7](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/7)
+2. [#47](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/47)
+3. [#48](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/48)
+4. [#49](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/49)
+5. [#64](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/64)
+6. [#67](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/67)
+7. [#73](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/73)
+8. [#74](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/74)
+9. [#77](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/77)
+10. [#79](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/79)
+11. [#81](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/81)
+12. [#84](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/84)
+13. [#86](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/86)
+14. [#88](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/88)
+15. [#89](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/89)
+16. [#91](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/91)
+17. [#104](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/104)
+
+### Contributions Beyond the Project Team
+* PE Dry Run Tester for [AY2223S2-CS2113-T12-2](https://github.com/AY2223S2-CS2113-T12-2/tp/issues/)
+* PE Dry Run Tester for [AY2223S2-CS2113-W15-1](https://github.com/AY2223S2-CS2113-W15-1/tp/issues)
\ No newline at end of file
diff --git a/docs/team/liuziyang020319.md b/docs/team/liuziyang020319.md
new file mode 100644
index 0000000000..fffcbb0859
--- /dev/null
+++ b/docs/team/liuziyang020319.md
@@ -0,0 +1,86 @@
+---
+layout: page
+title: Liu Ziyang's Project Portfolio Page
+---
+
+## Overview
+Taste of Mom's (TOM) is a desktop recipe manager application for managing recipes, optimized for use via a Command Line Interface (CLI).
+## Summary of Contributions
+### Code Contributed
+Code Contributed: [RepoSense Link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=liuziyang020319&breakdown=true)
+### Enhancements Implemented
+Given below are my contributions to the project:
+* **New Feature**: Added the ability to add the steps of recipe to the recipe manager by sum.
+ * What it does: allows the user to add the steps of recipe to the recipe manager by sum.
+* **New Feature**: Wrote partial code for the storage of the recipe manager.
+ * What it does: Wrote two methods. One is to create the directory of the recipe manager. The other is to delete the directory and the files.
+* **New Feature**: Added the ability to edit the recipe by one line command.
+ * What it does: allows the user to edit the recipe (step/ingredient) by one line command.
+* **New Feature**: Added the ability to clear all entries in the recipe manager.
+ * What it does: Delete all the recipes in the recipe manager.
+* **Skeleton Code**: Added the skeleton code of UI class.
+* **Skeleton Code**: Added the skeleton code of VIEW feature.
+* **Code testing**: Wrote the test code for the following features:
+ * UI
+ * Parser for adding a recipe
+ * Storage
+ * Find command
+
+### Contributions to UG
+Wrote the skeleton of the User Guide:
+* Introduction
+* Table of Contents
+* Features
+* FAQ
+* Command Summary
+
+Added user documentation to explain how to use the following commands:
+* Adding a recipe
+* Editing a recipe with one line command
+* Deleting a recipe
+* Listing all recipes
+* Clearing all entries
+* Exiting the program
+* Saving the data
+* Editing the data file
+
+### Contributions to DG
+#### Section
+* Table of Contents
+* Setup & Prerequisites
+* Appendix E.1 & E.2 (Instructions for manual testing)
+In the architecture section:
+* main architecture
+* UI component
+In the feature section:
+* Recipe Manage Feature
+* Recipe Find Feature
+
+#### UML diagrams
+* Added the architecture diagram:
+[Architecture Diagram](https://github.com/AY2223S2-CS2113-F13-1/tp/blob/master/docs/PlantUML/MainArchitecture.png).
+* Added the sequence diagram for showing how the components interact with each other:
+[Interaction Diagram](https://github.com/AY2223S2-CS2113-F13-1/tp/blob/master/docs/PlantUML/ArchitectureInteract.png).
+* Added the sequence diagram for managing the recipe (Wrote the main codes, Phone divided the diagram into two parts):
+[Add&List Diagram](https://github.com/AY2223S2-CS2113-F13-1/tp/blob/master/docs/PlantUML/RecipeManage_Add_List.png)
+[Delete&Clear Diagram](https://github.com/AY2223S2-CS2113-F13-1/tp/blob/master/docs/PlantUML/RecipeManage_Delete_Clear.puml).
+* Added the sequence diagram for finding the recipe:
+[Find Diagram](https://github.com/AY2223S2-CS2113-F13-1/tp/blob/master/docs/PlantUML/FindRecipe.png).
+
+### Contribution to team based task
+* Came up with the user stories.
+* Managed releases `v1.0` on GitHub.
+* Set milestones `v1.0`-`v2.0` on GitHub.
+* Managed issue tracker on GitHub. (e.g. adding labels, assigning issues to team members, closing issues, etc)
+* Solving issues on GitHub:
+ * [#4](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/4)
+ * [#5](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/5)
+ * [#7](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/7)
+ * [#8](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/8)
+ * [#11](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/11)
+ * [#18](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/18)
+ * [#71](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/71)
+ * [#78](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/78)
+ * [#80](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/80)
+ * [#93](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/93)
+ * [#105](https://github.com/AY2223S2-CS2113-F13-1/tp/issues/105)
\ No newline at end of file
diff --git a/docs/team/picture/hongyao.jpg b/docs/team/picture/hongyao.jpg
new file mode 100644
index 0000000000..2e8adc7df5
Binary files /dev/null and b/docs/team/picture/hongyao.jpg differ
diff --git a/docs/team/picture/liu.jpg b/docs/team/picture/liu.jpg
new file mode 100644
index 0000000000..dfe86c59d8
Binary files /dev/null and b/docs/team/picture/liu.jpg differ
diff --git a/docs/team/picture/phone.jpg b/docs/team/picture/phone.jpg
new file mode 100644
index 0000000000..2691a07347
Binary files /dev/null and b/docs/team/picture/phone.jpg differ
diff --git a/docs/team/picture/yatpang.jpg b/docs/team/picture/yatpang.jpg
new file mode 100644
index 0000000000..98edb94003
Binary files /dev/null and b/docs/team/picture/yatpang.jpg differ
diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java
index 5c74e68d59..ccf88e7d72 100644
--- a/src/main/java/seedu/duke/Duke.java
+++ b/src/main/java/seedu/duke/Duke.java
@@ -1,21 +1,71 @@
package seedu.duke;
-import java.util.Scanner;
+import seedu.duke.command.Command;
+import seedu.duke.parser.Parser;
+import seedu.duke.recipe.RecipeList;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.UI;
+import java.io.FileNotFoundException;
+
+/**
+ * Represents the Recipe Manager programme. A Duke object corresponds to
+ * a Recipe Manager.
+ */
public class Duke {
+
+ private final UI ui = new UI();
+
/**
- * Main entry-point for the java.duke.Duke application.
+ * Class constructor specifying filePath for saving data.
+ *
+ * @param filePath a relative file path giving the location of the saved file.
*/
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- System.out.println("What is your name?");
+ public Duke(String filePath) {
+ Storage.setFilePath(filePath);
+ try {
+ Storage.createDirectory();
+ } catch (Exception e) {
+ ui.showLoadingErrorMessage(e);
+ } finally {
+ RecipeList.createRecipeList();
+ ui.showLine();
+ }
+ }
+
+ /**
+ * Starts the Duke programme and continuously take in user inputs until
+ * it is terminated by the user.
+ */
+ public void run() {
+ try {
+ Storage.loadSaveFiles();
+ ui.showLoad();
+ } catch (FileNotFoundException e) {
+ ui.showLoadingErrorMessage(e);
+ }
+ ui.showWelcome();
+ ui.showLine();
+ boolean isExit = false;
+ while (!isExit) {
+ try {
+ String fullCommand = ui.readCommand();
+ Command c = Parser.parseCommands(fullCommand);
+ c.execute(ui);
+ isExit = c.isExit();
+ } catch (Exception e) {
+ ui.showDukeMainError(e);
+ } finally {
+ ui.showLine();
+ }
+ }
+ }
- Scanner in = new Scanner(System.in);
- System.out.println("Hello " + in.nextLine());
+ /**
+ * The main method that runs the entire programme.
+ */
+ public static void main(String[] args) {
+ new Duke("data").run();
+ System.exit(0);
}
}
diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java
new file mode 100644
index 0000000000..588b463da3
--- /dev/null
+++ b/src/main/java/seedu/duke/command/Command.java
@@ -0,0 +1,408 @@
+package seedu.duke.command;
+
+import seedu.duke.exceptions.IncompleteInputException;
+import seedu.duke.exceptions.OutOfIndexException;
+import seedu.duke.exceptions.RecipeListEmptyException;
+import seedu.duke.parser.Parser;
+import seedu.duke.recipe.Step;
+import seedu.duke.recipe.StepList;
+import seedu.duke.recipe.RecipeList;
+import seedu.duke.recipe.Recipe;
+import seedu.duke.recipe.IngredientList;
+import seedu.duke.recipe.Ingredient;
+import seedu.duke.storage.Storage;
+import seedu.duke.ui.IntLib;
+import seedu.duke.ui.StringLib;
+import seedu.duke.ui.UI;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import static seedu.duke.ui.IntLib.RECIPE_NAME_INDEX;
+import static seedu.duke.ui.IntLib.RECIPE_INGREDIENTS_INDEX;
+import static seedu.duke.ui.IntLib.RECIPE_TAG_INDEX;
+import static seedu.duke.ui.IntLib.RECIPE_SUM_OF_STEPS_INDEX;
+
+/**
+ * Represents a particular command to be carried out consisting of the
+ * command type and command description.
+ *
+ * A Command object corresponds to a particular command represented
+ * by type and fullDescription (e.g. DELETE,6)
+ */
+public class Command {
+ private final CommandType type;
+ private final String fullDescription;
+
+ /**
+ * Class constructor specifying the type of command and its follow-up description.
+ *
+ * @param type an Enum that represents a particular command.
+ * @param fullDescription a String that contains the follow-up description for the command.
+ */
+ public Command(CommandType type, String fullDescription) {
+ this.type = type;
+ this.fullDescription = fullDescription;
+ }
+
+ /**
+ * Returns if the command type is CommandType.EXIT
+ * in order to terminate the programme.
+ *
+ * @return if the command is the exit command type.
+ */
+ public boolean isExit() {
+ return this.type == CommandType.EXIT;
+ }
+
+ /**
+ * Based on the type, carries out different tasks assigned
+ * while fully checking for any exceptions that may occur along the way.
+ *
+ * @param ui the instantiated object to handle all user interactions.
+ */
+ public void execute(UI ui) throws IOException {
+
+ int recipeListIndex;
+ int recipeCount = RecipeList.getCurrRecipeNumber();
+ switch (type) {
+ case LIST:
+ ui.showRecipeList(RecipeList.getRecipeList());
+ break;
+ case ADD:
+ try {
+ if (fullDescription.isEmpty()) {
+ throw new IncompleteInputException("The description of " + type + " cannot be empty.\n");
+ }
+ ArrayList parsed = Parser.parseRecipe(fullDescription);
+ String recipeName = parsed.get(RECIPE_NAME_INDEX);
+ IngredientList ingredientLists =
+ Parser.parseIngredients(parsed.get(RECIPE_INGREDIENTS_INDEX));
+ String recipeTag = parsed.get(RECIPE_TAG_INDEX);
+ int sumOfSteps = Integer.parseInt(parsed.get(RECIPE_SUM_OF_STEPS_INDEX));
+ StepList recipeSteps = Parser.parseSteps(ui,sumOfSteps);
+ RecipeList.addNewRecipe(new Recipe(recipeName, recipeTag, ingredientLists, recipeSteps));
+ recipeCount = RecipeList.getCurrRecipeNumber();
+ ui.showRecipeAdded(RecipeList.getNewestRecipe(), recipeCount);
+ Storage.writeSavedFile();
+ } catch (Exception e) {
+ ui.showAddingRecipeErrorMessage(e);
+ }
+ break;
+ case ADDTORECIPE:
+ try {
+ if (fullDescription.isEmpty()) {
+ throw new IncompleteInputException("The description of " + type + " cannot be empty.\n");
+ }
+ if (!Parser.isValidAddToRecipe(fullDescription)) {
+ ui.showInvalidAddToRecipeDescription();
+ break;
+ }
+ String[] parsed = Parser.parseAddToRecipeDescription(fullDescription);
+ if (parsed.length != 3) {
+ ui.showInvalidAddToRecipeDescription();
+ break;
+ }
+ String elementType = parsed[0];
+ String id = parsed[1];
+ String description = parsed[2];
+ if (description.trim().equals(StringLib.EMPTY_STRING) && elementType.equals("s")) {
+ ui.showEmptyStepDescription();
+ break;
+ }
+ if (description.trim().equals(StringLib.EMPTY_STRING) && elementType.equals("i")) {
+ ui.showEmptyIngredientDescription();
+ break;
+ }
+ if (id.trim().equals(StringLib.EMPTY_STRING) && elementType.equals("s")) {
+ ui.showEmptyStepID();
+ break;
+ }
+ if (id.trim().equals(StringLib.EMPTY_STRING) && elementType.equals("i")) {
+ ui.showEmptyIngredientID();
+ break;
+ }
+ Recipe recipeToAddTo = RecipeList.getRecipe(id);
+ int index;
+ switch (elementType) {
+ case "s":
+ StepList stepListToAddTo = recipeToAddTo.getStepList();
+ if (Parser.isDuplicateStep(stepListToAddTo, description)) {
+ ui.showDuplicateStep();
+ break;
+ }
+ stepListToAddTo.showFullStepList();
+ int maxStep = stepListToAddTo.getCurrStepNumber();
+ index = ui.getIndexToAdd(maxStep);
+ if (index == IntLib.ADD_STEP_INDEX_BREAKOUT) {
+ ui.showStepQuitMessage();
+ break;
+ }
+ stepListToAddTo.addStep(new Step(description), index);
+ ui.showStepAdded();
+ Storage.writeSavedFile();
+ break;
+ case "i":
+ IngredientList ingredientListToAddTo = recipeToAddTo.getIngredientList();
+ if (Parser.isDuplicateIngredient(ingredientListToAddTo, description)){
+ ui.showDuplicateIngredient();
+ break;
+ } else {
+ int maxNum = ingredientListToAddTo.getCurrIngredientNumber();
+ ingredientListToAddTo.addIngredient(new Ingredient(description), maxNum);
+ ui.showIngredientAdded();
+ Storage.writeSavedFile();
+ }
+ break;
+ default:
+ }
+ } catch (Exception e) {
+ ui.showAddingRecipeElementErrorMessage(e);
+ }
+ break;
+ case DELETE:
+ try {
+ if (fullDescription.isEmpty()) {
+ throw new IncompleteInputException("The index of " + type + " cannot be empty.\n");
+ }
+ recipeListIndex = Integer.parseInt(fullDescription);
+ if (recipeCount == 0) {
+ System.out.println(StringLib.EMPTY_LIST_MESSAGE);
+ break;
+ }
+ if (recipeListIndex <= 0 || recipeListIndex > recipeCount) {
+ System.out.println(StringLib.POS_INT);
+ System.out.println("Valid range: " + 1 + " to " + recipeCount);
+ break;
+ }
+ Recipe recipeToBeDeleted = RecipeList.getRecipeFromList(recipeListIndex);
+ RecipeList.removeRecipe(recipeListIndex);
+ recipeCount = RecipeList.getCurrRecipeNumber();
+ ui.showRecipeDeleted(recipeToBeDeleted, recipeCount);
+ Storage.writeSavedFile();
+ } catch (Exception e) {
+ ui.showDeletingTaskErrorMessage(e, type);
+ }
+ break;
+ case DELETEFROMRECIPE:
+ try {
+ if (fullDescription.isEmpty()) {
+ throw new IncompleteInputException("The description of " + type + " cannot be empty.\n");
+ }
+ if (!Parser.isValidDeleteFromRecipe(fullDescription)) {
+ ui.showInvalidDeleteFromRecipeDescription();
+ break;
+ }
+ String[] parsed = Parser.parseDeleteFromRecipeDescription(fullDescription);
+ if (parsed.length != 2) {
+ ui.showInvalidDeleteFromRecipeDescription();
+ break;
+ }
+ String elementType = parsed[0];
+ String id = parsed[1];
+ if (id.trim().equals(StringLib.EMPTY_STRING) && elementType.equals("s")) {
+ ui.showEmptyStepID();
+ break;
+ }
+ if (id.trim().equals(StringLib.EMPTY_STRING) && elementType.equals("i")) {
+ ui.showEmptyIngredientID();
+ break;
+ }
+ Recipe recipeToDeleteFrom = RecipeList.getRecipe(id);
+ int index;
+ switch (elementType) {
+ case "s":
+ StepList stepListToDeleteFrom = recipeToDeleteFrom.getStepList();
+ if (stepListToDeleteFrom.isEmpty()) {
+ ui.showEmptyStepList();
+ break;
+ }
+ stepListToDeleteFrom.showFullStepList();
+ int maxStep = stepListToDeleteFrom.getCurrStepNumber();
+ index = ui.getIndexToDelete(maxStep);
+ if (index == IntLib.ADD_STEP_INDEX_BREAKOUT) {
+ ui.showStepQuitMessage();
+ break;
+ }
+ stepListToDeleteFrom.removeStep(index);
+ ui.showStepDeleted();
+ Storage.writeSavedFile();
+ break;
+ case "i":
+ IngredientList ingredientListToDeleteFrom = recipeToDeleteFrom.getIngredientList();
+ if (ingredientListToDeleteFrom.isEmpty()) {
+ ui.showEmptyIngredientList();
+ break;
+ }
+ int maxCount = ingredientListToDeleteFrom.getCurrIngredientNumber();
+ if (maxCount == 1) {
+ ui.showMinimumIngredientError();
+ break;
+ }
+ ingredientListToDeleteFrom.showList();
+
+ index = ui.getIndexToDelete(maxCount);
+ if (index == IntLib.ADD_STEP_INDEX_BREAKOUT) {
+ ui.showIngredientQuitMessage();
+ break;
+ }
+ ingredientListToDeleteFrom.removeIngredient(index);
+ ui.showIngredientDeleted();
+ Storage.writeSavedFile();
+ break;
+ default:
+ ui.showDefaultCaseError();
+ }
+ } catch (Exception e) {
+ ui.showDeletingRecipeElementErrorMessage(e);
+ }
+ break;
+ case CLEAR:
+ RecipeList.clearRecipeList();
+ ui.showRecipeListCleared();
+ Storage.writeSavedFile();
+ break;
+ case VIEW:
+ try {
+ if (fullDescription.isEmpty()) {
+ throw new IncompleteInputException("The "+ type +" command requires an input parameter, " +
+ " / .\nIt cannot be empty!\n");
+ }
+ if (RecipeList.isEmpty()) {
+ throw new RecipeListEmptyException();
+ }
+ Recipe recipeToBeViewed = RecipeList.viewRecipe(fullDescription);
+ ui.showRecipeViewed(recipeToBeViewed, ui);
+ } catch (Exception e) {
+ ui.showViewingRecipeErrorMessage(e);
+ }
+ break;
+ case FINDNAME:
+ RecipeList.searchRecipeList(fullDescription);
+ break;
+ case FINDTAG:
+ RecipeList.searchByTag(fullDescription);
+ break;
+ case EDITSTEP:
+ try {
+ if (fullDescription.isEmpty()) {
+ throw new IncompleteInputException("The index of " + type + " cannot be empty.\n");
+ }
+ int recipeListNum = Integer.parseInt(fullDescription);
+ if (recipeCount == 0) {
+ System.out.println(StringLib.EMPTY_LIST_MESSAGE);
+ break;
+ }
+ if (recipeListNum <= 0 || recipeListNum > recipeCount) {
+ System.out.println(StringLib.POS_INT);
+ System.out.println("Valid range: " + 1 + " to " + recipeCount);
+ break;
+ }
+ Recipe recipeToEdit = RecipeList.getRecipeFromList(recipeListNum);
+ StepList recipeToEditStepList = recipeToEdit.getStepList();
+ int maxSteps = recipeToEditStepList.getCurrStepNumber();
+ if (maxSteps == 0) {
+ assert (maxSteps - 1 == -1);
+ throw new OutOfIndexException(StringLib.NO_STEPS_ERROR);
+
+ }
+
+ recipeToEditStepList.showFullStepList();
+ ui.showEditRecipeStepPrompt();
+ String input = ui.readCommand();
+ if (input.trim().equals(StringLib.STEP_VIEW_QUIT_KEYWORD) ||
+ input.contains(StringLib.STEP_VIEW_QUIT_KEYWORD)) {
+ break;
+ }
+ int stepIndex = Integer.parseInt(input) - 1;
+ if (stepIndex < maxSteps && stepIndex >= 0) {
+ assert (maxSteps - stepIndex > 0);
+ recipeToEditStepList.editStep(stepIndex, ui);
+ } else if (stepIndex <= 0) {
+ System.out.println(StringLib.POS_INT);
+ System.out.println("Valid range: " + 1 + " to " + maxSteps);
+ } else {
+ throw new OutOfIndexException(StringLib.INPUT_STEPS_INDEX_EXCEEDED +
+ "\nValid Range: 1 to " + maxSteps);
+ }
+ Storage.writeSavedFile();
+ } catch (Exception e) {
+ ui.showEditErrorMessage(e);
+ }
+ break;
+
+ case EDITINGREDIENT:
+ try {
+ if (fullDescription.isEmpty()) {
+ throw new IncompleteInputException("The index of " + type + " cannot be empty.\n");
+ }
+ int recipeListNum = Integer.parseInt(fullDescription);
+
+ if (recipeCount == 0) {
+ System.out.println(StringLib.EMPTY_LIST_MESSAGE);
+ break;
+ }
+ if (recipeListNum <= 0 || recipeListNum > recipeCount) {
+ System.out.println(StringLib.POS_INT);
+ System.out.println("Valid range: " + 1 + " to " + recipeCount);
+ break;
+ }
+ Recipe recipeToEdit = RecipeList.getRecipeFromList(recipeListNum);
+ IngredientList recipeToEditIngredientList = recipeToEdit.getIngredientList();
+ int maxSteps = recipeToEditIngredientList.getCurrIngredientNumber();
+ if (maxSteps == 0) {
+ throw new OutOfIndexException(StringLib.NO_INGREDIENTS_ERROR);
+ }
+ recipeToEditIngredientList.showList();
+ ui.showEditRecipeIngredientPrompt();
+ String input = ui.readCommand();
+ if (input.trim().equals(StringLib.STEP_VIEW_QUIT_KEYWORD) ||
+ input.contains(StringLib.STEP_VIEW_QUIT_KEYWORD)) {
+ break;
+ }
+ int ingredientIndex = Integer.parseInt(input) - 1;
+
+ if (recipeToEditIngredientList.isIndexWithinRange(ingredientIndex)) {
+ System.out.println(StringLib.ENTER_INGREDIENT_DESCRIPTION);
+ String newIngredientDescription = ui.readCommand();
+ recipeToEditIngredientList.editIngredient(newIngredientDescription, ingredientIndex);
+ Storage.writeSavedFile();
+ }
+ } catch (Exception e) {
+ ui.showEditErrorMessage(e);
+ }
+ break;
+ case EDIT:
+ try {
+ OperationType editType = Parser.parseEditType(fullDescription);
+ boolean isEditIngredient = editType == OperationType.INGREDIENT;
+ boolean isEditStep = editType == OperationType.STEP;
+ Object[] parsed = Parser.parseEditRecipeIndex(fullDescription.substring(4),editType);
+ int recipeIndex = (int) parsed[0];
+ String editDescription = (String) parsed[1];
+ if(isEditIngredient) {
+ Parser.parseEditIngredient(recipeIndex, editDescription);
+ } else if (isEditStep) {
+ Parser.parseEditStep(recipeIndex, editDescription);
+ } else {
+ ui.showDefaultElseConditionError();
+ }
+ } catch (Exception e) {
+ ui.showErrorMessage(e);
+ }
+ Storage.writeSavedFile();
+ break;
+ case HELP:
+ ui.showHelp();
+ break;
+ case EXIT:
+ ui.showExit();
+ break;
+ case UNKNOWN:
+ ui.showUnrecognizableErrorMessage();
+ break;
+ default:
+ ui.showUnrecognizableCommandErrorMessage();
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/command/CommandType.java b/src/main/java/seedu/duke/command/CommandType.java
new file mode 100644
index 0000000000..15d291e6e5
--- /dev/null
+++ b/src/main/java/seedu/duke/command/CommandType.java
@@ -0,0 +1,83 @@
+package seedu.duke.command;
+
+/**
+ * Commands that can be used.
+ *
+ * {@link #LIST}
+ * {@link #ADD}
+ * {@link #ADDTORECIPE}
+ * {@link #VIEW}
+ * {@link #DELETE}
+ * {@link #DELETEFROMRECIPE}
+ * {@link #HELP}
+ * {@link #CLEAR}
+ * {@link #EDITSTEP}
+ * {@link #EDITINGREDIENT}
+ * {@link #EDIT}
+ * {@link #FINDNAME}
+ * {@link #FINDTAG}
+ * {@link #EXIT}
+ * {@link #UNKNOWN}
+ */
+public enum CommandType {
+ /**
+ * Lists out the current recipe list.
+ */
+ LIST,
+ /**
+ * Adds a recipe to the recipe list.
+ */
+ ADD,
+ /**
+ * Adds an element (step or ingredient) to a recipe.
+ */
+ ADDTORECIPE,
+ /**
+ * Displays a particular recipe in the recipe list.
+ */
+ VIEW,
+ /**
+ * Removes a particular recipe from the recipe list.
+ */
+ DELETE,
+ /**
+ * Deletes an element (step or ingredient) from a recipe.
+ */
+ DELETEFROMRECIPE,
+ /**
+ * Shows the full list of commands.
+ */
+ HELP,
+ /**
+ * Clear the current recipe list.
+ */
+ CLEAR,
+ /**
+ * Terminates the programme and exit without saving.
+ */
+ EXIT,
+ /**
+ * Edit a step in the recipe.
+ */
+ EDITSTEP,
+ /**
+ * Edit a ingredient in the recipe.
+ */
+ EDITINGREDIENT,
+ /**
+ * Edit in one line
+ */
+ EDIT,
+ /**
+ * Search dishes by name.
+ */
+ FINDNAME,
+ /**
+ * Search dishes by tag.
+ */
+ FINDTAG,
+ /**
+ * Command not recognized.
+ */
+ UNKNOWN
+}
diff --git a/src/main/java/seedu/duke/command/OperationType.java b/src/main/java/seedu/duke/command/OperationType.java
new file mode 100644
index 0000000000..721029fdfd
--- /dev/null
+++ b/src/main/java/seedu/duke/command/OperationType.java
@@ -0,0 +1,10 @@
+package seedu.duke.command;
+
+public enum OperationType {
+ /**
+ * Enum for the different types of operations.
+ */
+ INGREDIENT,
+ STEP,
+ OTHER
+}
diff --git a/src/main/java/seedu/duke/exceptions/DuplicateRecipeNameException.java b/src/main/java/seedu/duke/exceptions/DuplicateRecipeNameException.java
new file mode 100644
index 0000000000..be3c1365cc
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/DuplicateRecipeNameException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exceptions;
+
+public class DuplicateRecipeNameException extends Exception{
+ public DuplicateRecipeNameException(String inputText) {
+ super(inputText);
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/EditFormatException.java b/src/main/java/seedu/duke/exceptions/EditFormatException.java
new file mode 100644
index 0000000000..3618d7cc9f
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/EditFormatException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exceptions;
+
+public class EditFormatException extends Exception {
+ public EditFormatException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/FileParseReadingException.java b/src/main/java/seedu/duke/exceptions/FileParseReadingException.java
new file mode 100644
index 0000000000..9aea97860d
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/FileParseReadingException.java
@@ -0,0 +1,17 @@
+package seedu.duke.exceptions;
+
+/**
+ * A Throwable exception that occurs during reading
+ * of saved file to obtain previously saved task list.
+ */
+public class FileParseReadingException extends Exception{
+
+ /**
+ * Class constructor that takes in error message as inputText.
+ *
+ * @param inputText the String representing error message to be displayed.
+ */
+ public FileParseReadingException(String inputText) {
+ super(inputText);
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/FileStorageException.java b/src/main/java/seedu/duke/exceptions/FileStorageException.java
new file mode 100644
index 0000000000..f298680a6c
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/FileStorageException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exceptions;
+
+public class FileStorageException extends Exception{
+ public FileStorageException(String inputText) {
+ super(inputText);
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/IncompleteInputException.java b/src/main/java/seedu/duke/exceptions/IncompleteInputException.java
new file mode 100644
index 0000000000..fb70938392
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/IncompleteInputException.java
@@ -0,0 +1,17 @@
+package seedu.duke.exceptions;
+
+/**
+ * A Throwable exception that occurs when
+ * insufficient parameters for inputs is provided.
+ */
+public class IncompleteInputException extends Exception{
+
+ /**
+ * Class constructor that takes in error message as inputText.
+ *
+ * @param inputText the String representing error message to be displayed.
+ */
+ public IncompleteInputException(String inputText) {
+ super(inputText);
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/InvalidIndexRangeException.java b/src/main/java/seedu/duke/exceptions/InvalidIndexRangeException.java
new file mode 100644
index 0000000000..c8c38698e3
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/InvalidIndexRangeException.java
@@ -0,0 +1,10 @@
+package seedu.duke.exceptions;
+
+import seedu.duke.ui.StringLib;
+
+public class InvalidIndexRangeException extends Exception {
+
+ public InvalidIndexRangeException(int validStartIndex, int validEndIndex) {
+ super(StringLib.INVALID_INPUT_VALID_RANGE_PREFIX + validStartIndex + " to " + validEndIndex);
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/InvalidInputCharactersException.java b/src/main/java/seedu/duke/exceptions/InvalidInputCharactersException.java
new file mode 100644
index 0000000000..c0343ba416
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/InvalidInputCharactersException.java
@@ -0,0 +1,12 @@
+package seedu.duke.exceptions;
+
+public class InvalidInputCharactersException extends Exception{
+ /**
+ * Class constructor that takes in error message as inputText.
+ *
+ * @param inputText the String representing the error message to be displayed.
+ */
+ public InvalidInputCharactersException(String inputText) {
+ super(inputText);
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/ListEmptyException.java b/src/main/java/seedu/duke/exceptions/ListEmptyException.java
new file mode 100644
index 0000000000..4f0da83a5b
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/ListEmptyException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exceptions;
+
+public class ListEmptyException extends Exception{
+ public ListEmptyException() {
+ super("The list is empty!");
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/MissingIngredientInputException.java b/src/main/java/seedu/duke/exceptions/MissingIngredientInputException.java
new file mode 100644
index 0000000000..beadf47f13
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/MissingIngredientInputException.java
@@ -0,0 +1,10 @@
+package seedu.duke.exceptions;
+
+import seedu.duke.ui.StringLib;
+
+public class MissingIngredientInputException extends Exception{
+
+ public MissingIngredientInputException() {
+ super(StringLib.MISSING_INGREDIENT_INPUT);
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/NoMatchingRecipeFound.java b/src/main/java/seedu/duke/exceptions/NoMatchingRecipeFound.java
new file mode 100644
index 0000000000..04826bdc2d
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/NoMatchingRecipeFound.java
@@ -0,0 +1,7 @@
+package seedu.duke.exceptions;
+
+public class NoMatchingRecipeFound extends Exception {
+ public NoMatchingRecipeFound(String inputText) {
+ super(inputText);
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/OutOfIndexException.java b/src/main/java/seedu/duke/exceptions/OutOfIndexException.java
new file mode 100644
index 0000000000..54f45b4ee2
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/OutOfIndexException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exceptions;
+
+public class OutOfIndexException extends Exception {
+ public OutOfIndexException (String inputText) {
+ super(inputText);
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/RecipeListEmptyException.java b/src/main/java/seedu/duke/exceptions/RecipeListEmptyException.java
new file mode 100644
index 0000000000..407831b1fd
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/RecipeListEmptyException.java
@@ -0,0 +1,8 @@
+package seedu.duke.exceptions;
+
+public class RecipeListEmptyException extends Exception{
+ /**
+ * Class constructor.
+ */
+ public RecipeListEmptyException() {}
+}
diff --git a/src/main/java/seedu/duke/parser/Parser.java b/src/main/java/seedu/duke/parser/Parser.java
new file mode 100644
index 0000000000..2e0911434a
--- /dev/null
+++ b/src/main/java/seedu/duke/parser/Parser.java
@@ -0,0 +1,463 @@
+package seedu.duke.parser;
+
+import seedu.duke.command.Command;
+import seedu.duke.command.CommandType;
+import seedu.duke.command.OperationType;
+import seedu.duke.exceptions.EditFormatException;
+import seedu.duke.exceptions.IncompleteInputException;
+import seedu.duke.exceptions.InvalidInputCharactersException;
+import seedu.duke.exceptions.MissingIngredientInputException;
+import seedu.duke.recipe.Step;
+import seedu.duke.recipe.StepList;
+import seedu.duke.recipe.RecipeList;
+import seedu.duke.recipe.IngredientList;
+import seedu.duke.recipe.Ingredient;
+import seedu.duke.ui.StringLib;
+import seedu.duke.ui.UI;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static seedu.duke.ui.IntLib.RECIPE_NAME_INDEX;
+import static seedu.duke.ui.IntLib.RECIPE_INGREDIENTS_INDEX;
+import static seedu.duke.ui.IntLib.RECIPE_TAG_INDEX;
+import static seedu.duke.ui.IntLib.RECIPE_SUM_OF_STEPS_INDEX;
+import static seedu.duke.ui.StringLib.INVALID_CHARACTERS_INGREDIENTS_ERROR;
+import static seedu.duke.ui.StringLib.INVALID_CHARACTERS_NAME_ERROR;
+import static seedu.duke.ui.StringLib.INVALID_CHARACTERS_STEP_ERROR;
+import static seedu.duke.ui.StringLib.INVALID_CHARACTERS_TAG_ERROR;
+
+public class Parser {
+
+ private static final Pattern specialCharacters = Pattern.compile("[^a-z0-9 ]", Pattern.CASE_INSENSITIVE);
+ private static final Pattern specialCharactersIngredients =
+ Pattern.compile("[^a-z0-9, ]", Pattern.CASE_INSENSITIVE);
+ private static final Pattern numericCharacters = Pattern.compile("^[0-9 ]*$");
+ private static final Pattern nonNumericCharacters = Pattern.compile("[^0-9 ]");
+ private static final String RECIPE_WRONG_NAME_INGREDIENTS_TAG_STEP = "\nRecipe is missing the \"NAME\" "
+ + "or \"INGREDIENTS\" or \"TAG\" or \"SUM of the STEPs," +
+ "\nor there is more than one\n" +
+ "\"NAME\" or \"INGREDIENTS\" or \"TAG\" or \"SUM of the STEPs\"!\n";
+ private static final String RECIPE_WRONG_LEADING_STRING = "Recipe contains the leading string!\n";
+ private static final String RECIPE_MISSING_NAME = "Recipe is missing \"NAME\"!\n";
+ private static final String RECIPE_MISSING_INGREDIENTS = "Recipe is missing \"INGREDIENTS\"!\n";
+ private static final String RECIPE_MISSING_TAG = "Recipe is missing \"TAG\"!\n";
+ private static final String RECIPE_MISSING_STEP = "Recipe is missing \"SUM of the STEPs\"!\n";
+
+ /**
+ * Returns a Command type which contains the command to be
+ * executed and its full description to support its execution. It takes
+ * in a string representing the user input.
+ *
+ * @param line the input line by user.
+ * @return a Command containing the command to be executed.
+ */
+ public static Command parseCommands(String line) {
+ line = line.strip();
+ String[] lineSpaced = line.split(" ");
+ String fullDescription = "";
+ if (lineSpaced.length > 1) {
+ for (int i = 2; i < lineSpaced.length; i++) {
+ lineSpaced[1] = lineSpaced[1].concat(" " + lineSpaced[i]);
+ }
+ fullDescription = lineSpaced[1].trim();
+ }
+ CommandType type;
+ switch (lineSpaced[0].toLowerCase()) {
+ case "list":
+ type = CommandType.LIST;
+ break;
+ case "add":
+ type = CommandType.ADD;
+ break;
+ case "addtorecipe":
+ type = CommandType.ADDTORECIPE;
+ break;
+ case "view":
+ type = CommandType.VIEW;
+ break;
+ case "findname":
+ type = CommandType.FINDNAME;
+ break;
+ case "findtag":
+ type = CommandType.FINDTAG;
+ break;
+ case "editstep":
+ type = CommandType.EDITSTEP;
+ break;
+ case "editingredient":
+ type = CommandType.EDITINGREDIENT;
+ break;
+ case "edit":
+ type = CommandType.EDIT;
+ break;
+ case "delete":
+ type = CommandType.DELETE;
+ break;
+ case "deletefromrecipe":
+ type = CommandType.DELETEFROMRECIPE;
+ break;
+ case "help":
+ type = CommandType.HELP;
+ break;
+ case "clear":
+ type = CommandType.CLEAR;
+ break;
+ case "exit":
+ type = CommandType.EXIT;
+ break;
+ default:
+ type = CommandType.UNKNOWN;
+ }
+ return new Command(type, fullDescription);
+ }
+
+ private static boolean matchString(String input, String regex) {
+ String matcher = input;
+ int count = 0;
+ while (matcher.contains(regex)) {
+ matcher = matcher.substring(matcher.indexOf(regex) + 1);
+ ++count;
+ }
+ boolean isMatch = (count == 1);
+ return isMatch;
+ }
+
+ public static int matchCount(String input, String regex) {
+ String matcher = input;
+ int count = 0;
+ while (matcher.contains(regex)){
+ matcher=matcher.substring(matcher.indexOf(regex)+1);
+ ++count;
+ }
+ return count;
+ }
+
+ /**
+ * Returns an Array of Strings containing the parsed full description
+ * of a Recipe into its name, ingredients and tag.
+ *
+ * @param description the full description of the Recipe.
+ * @return parsed ArrayList of Strings containing the recipe's name, ingredients list and tag.
+ * @throws IncompleteInputException if full description input is missing the description or due date or both.
+ */
+ public static ArrayList parseRecipe(String description) throws IncompleteInputException,
+ InvalidInputCharactersException {
+ ArrayList parsed = new ArrayList<>();
+ if (!matchString(description, "n/") || !matchString(description, "i/")
+ || !matchString(description, "t/") || !matchString(description, "s/")) {
+ throw new IncompleteInputException(RECIPE_WRONG_NAME_INGREDIENTS_TAG_STEP);
+ }
+ if (!description.substring(0, 2).equals("n/")) {
+ throw new IncompleteInputException(RECIPE_WRONG_LEADING_STRING);
+ }
+ String[] parsedName = description.split(" i/");
+ String[] parsedIngredientsTag = parsedName[1].split(" t/");
+ String[] parsedTagStep = parsedIngredientsTag[1].split(" s/");
+ parsed.add(parsedName[0].substring(2).trim());
+ parsed.add(parsedIngredientsTag[0].trim());
+ parsed.add(parsedTagStep[0].trim());
+ try {
+ parsed.add(parsedTagStep[1].trim());
+ } catch (Exception e) {
+ throw new IncompleteInputException(RECIPE_MISSING_STEP);
+ }
+
+ if (parsed.size() < 4) {
+ throw new IncompleteInputException(RECIPE_WRONG_NAME_INGREDIENTS_TAG_STEP);
+ }
+ if (parsed.get(RECIPE_NAME_INDEX).isEmpty()) {
+ throw new IncompleteInputException(RECIPE_MISSING_NAME);
+ }
+ if (parsed.get(RECIPE_INGREDIENTS_INDEX).isEmpty()) {
+ throw new IncompleteInputException(RECIPE_MISSING_INGREDIENTS);
+ }
+ if (parsed.get(RECIPE_TAG_INDEX).isEmpty()) {
+ throw new IncompleteInputException(RECIPE_MISSING_TAG);
+ }
+ if (parsed.get(RECIPE_SUM_OF_STEPS_INDEX).isEmpty()) {
+ throw new IncompleteInputException(RECIPE_MISSING_STEP);
+ }
+ checkValidAddParameters(parsed);
+ try {
+ Integer.parseInt(parsed.get(RECIPE_SUM_OF_STEPS_INDEX));
+ if (Integer.parseInt(parsed.get(RECIPE_SUM_OF_STEPS_INDEX)) < 0) {
+ throw new IncompleteInputException(StringLib.MISSING_NUM);
+ }
+ return parsed;
+ } catch (NumberFormatException e) {
+ throw new IncompleteInputException(StringLib.MISSING_NUM);
+ }
+ }
+
+ public static void checkValidAddParameters(ArrayList parsedAddRecipe)
+ throws InvalidInputCharactersException {
+ String NAME = parsedAddRecipe.get(RECIPE_NAME_INDEX);
+ String INGREDIENT = parsedAddRecipe.get(RECIPE_INGREDIENTS_INDEX);
+ String TAG = parsedAddRecipe.get(RECIPE_TAG_INDEX);
+ String STEP = parsedAddRecipe.get(RECIPE_SUM_OF_STEPS_INDEX);
+ Matcher matchSpecialCharactersNAME = specialCharacters.matcher(NAME);
+ Matcher matcherNumericCharactersOnlyNAME = numericCharacters.matcher(NAME);
+ Matcher matchSpecialCharactersINGREDIENT = specialCharactersIngredients.matcher(INGREDIENT);
+ Matcher matcherNumericCharactersOnlyINGREDIENT = numericCharacters.matcher(INGREDIENT);
+ Matcher matchSpecialCharactersTAG = specialCharacters.matcher(TAG);
+ Matcher matcherNumericCharactersOnlyTAG = numericCharacters.matcher(TAG);
+ Matcher matchNonNumericCharactersSTEP = nonNumericCharacters.matcher(STEP);
+ if (matchSpecialCharactersNAME.find() || matcherNumericCharactersOnlyNAME.find()) {
+ throw new InvalidInputCharactersException(INVALID_CHARACTERS_NAME_ERROR);
+ }
+ if (matchSpecialCharactersINGREDIENT.find() || matcherNumericCharactersOnlyINGREDIENT.find()) {
+ throw new InvalidInputCharactersException(INVALID_CHARACTERS_INGREDIENTS_ERROR);
+ }
+ if (matchSpecialCharactersTAG.find() || matcherNumericCharactersOnlyTAG.find()) {
+ throw new InvalidInputCharactersException(INVALID_CHARACTERS_TAG_ERROR);
+ }
+ if (matchNonNumericCharactersSTEP.find()) {
+ throw new InvalidInputCharactersException(INVALID_CHARACTERS_STEP_ERROR);
+ }
+ }
+
+ public static IngredientList parseIngredients(String inputIngredients) throws MissingIngredientInputException {
+ ArrayList parsed = new ArrayList<>();
+ String[] parsedIngredients = inputIngredients.split(",");
+ if (parsedIngredients.length == 0) {
+ throw new MissingIngredientInputException();
+ }
+ for (String ingredient : parsedIngredients) {
+ if (ingredient.trim().isEmpty()) {
+ throw new MissingIngredientInputException();
+ } else {
+ parsed.add(new Ingredient(ingredient.trim()));
+ }
+ }
+ assert (parsed.size() != 0);
+ return new IngredientList(parsed);
+ }
+
+ public static StepList parseSteps(UI ui, int sumOfSteps) {
+ ArrayList parsedStepList = new ArrayList<>();
+ String inputStep;
+ for (int i = 1; i <= sumOfSteps; i++) {
+ ui.showStepInsertMessage(i);
+ inputStep = ui.readCommand();
+ while (inputStep.trim().equals("")) {
+ ui.showInvalidStepMessage();
+ inputStep = ui.readCommand();
+ }
+ parsedStepList.add(new Step(inputStep));
+ }
+ return new StepList(parsedStepList);
+ }
+
+ public static OperationType parseEditType(String description) throws IncompleteInputException {
+ boolean isIngredient = description.startsWith("--i ");
+ boolean isStep = description.startsWith("--s ");
+ if (isIngredient) {
+ return OperationType.INGREDIENT;
+ } else if (isStep) {
+ return OperationType.STEP;
+ } else {
+ if(description.equals("--s")){
+ throw new IncompleteInputException(StringLib.EDIT_STEP_ERROR);
+ }
+ if(description.equals("--i")){
+ throw new IncompleteInputException(StringLib.EDIT_INGREDIENT_ERROR);
+ }
+ throw new IncompleteInputException(StringLib.EDIT_TYPE_ERROR);
+ }
+ }
+
+
+ public static Object[] parseEditRecipeIndex(String description, OperationType type)
+ throws IncompleteInputException {
+ String[] parsedDescription = description.split(" ",2);
+ String errorLog = type.equals(OperationType.INGREDIENT) ?
+ StringLib.EDIT_INGREDIENT_ERROR : StringLib.EDIT_STEP_ERROR;
+ if (parsedDescription.length < 2) {
+ throw new IncompleteInputException(errorLog);
+ }
+ try {
+ int recipeIndex = Integer.parseInt(parsedDescription[0]);
+ return new Object[]{recipeIndex, parsedDescription[1].trim()};
+ } catch (NumberFormatException e) {
+ try {
+ new BigInteger(parsedDescription[0]);
+ } catch (Exception e1) {
+ throw new IncompleteInputException(errorLog);
+ }
+ throw new IncompleteInputException(StringLib.OVERFLOW_NUMBER_ERROR);
+ }
+ }
+
+ public static void parseEditIngredient(Integer recipeIndex, String description) throws Exception {
+ String[] parsedDescription = description.split("i/");
+ if (parsedDescription.length < 2) {
+ throw new IncompleteInputException(StringLib.EDIT_INGREDIENT_ERROR);
+ }
+ if (!matchString(description, "i/")) {
+ throw new IncompleteInputException(StringLib.EDIT_INGREDIENT_ERROR);
+ }
+ try {
+ int ingredientIndex = Integer.parseInt(parsedDescription[0].trim());
+ String newIngredient = parsedDescription[1].trim();
+ if (newIngredient.isEmpty()) {
+ throw new IncompleteInputException(StringLib.EDIT_INGREDIENT_ERROR);
+ }
+ RecipeList.editIngredient(recipeIndex, ingredientIndex, newIngredient);
+ } catch (NumberFormatException e) {
+ try {
+ new BigInteger(parsedDescription[0].trim());
+ } catch (Exception e1) {
+ throw new IncompleteInputException(StringLib.EDIT_INGREDIENT_ERROR);
+ }
+ throw new IncompleteInputException(StringLib.OVERFLOW_NUMBER_ERROR);
+ } catch (EditFormatException e) {
+ throw new Exception("error in edit ingredient:\n" + e.getMessage());
+ }
+ }
+
+ public static void parseEditStep(Integer recipeIndex, String description) throws Exception {
+ String[] parsedDescription = description.split("s/");
+ if (parsedDescription.length < 2) {
+ throw new IncompleteInputException(StringLib.EDIT_STEP_ERROR);
+ }
+ if (!matchString(description, "s/")) {
+ throw new IncompleteInputException(StringLib.EDIT_STEP_ERROR);
+ }
+ try {
+ int stepIndex = Integer.parseInt(parsedDescription[0].trim());
+ String newStep = parsedDescription[1].trim();
+ if (newStep.isEmpty()) {
+ throw new IncompleteInputException(StringLib.EDIT_STEP_ERROR);
+ }
+ RecipeList.editStep(recipeIndex, stepIndex, newStep);
+ } catch (NumberFormatException e) {
+ try {
+ new BigInteger(parsedDescription[0].trim());
+ } catch (Exception e1) {
+ throw new IncompleteInputException(StringLib.EDIT_STEP_ERROR);
+ }
+ throw new IncompleteInputException(StringLib.OVERFLOW_NUMBER_ERROR);
+ } catch (EditFormatException e) {
+ throw new Exception("error in edit step:\n" + e.getMessage());
+ }
+ }
+
+ public static String removeForbiddenChars(String ingredient) {
+ for (String chara : StringLib.FORBIDDEN_CHARS) {
+ ingredient.replaceAll(chara, "");
+ }
+ return ingredient;
+ }
+
+ public static OperationType parseAddToRecipeIndex(String description) throws IncompleteInputException {
+ boolean isIngredient = description.startsWith("--i ");
+ boolean isStep = description.startsWith("--s ");
+ if (isIngredient) {
+ return OperationType.INGREDIENT;
+ } else if (isStep) {
+ return OperationType.STEP;
+ } else {
+ throw new IncompleteInputException(StringLib.INVALID_ADD_TO_RECIPE_DESCRIPTION);
+ }
+ }
+
+ public static String[] parseAddToRecipeDescription(String description) throws IncompleteInputException{
+ OperationType parsedDescription = parseAddToRecipeIndex(description);
+ String[] out = new String[3];
+ out[0] = parsedDescription.equals(OperationType.INGREDIENT) ? "i" : "s";
+ String subDescription = description.substring(4).trim();
+ if (!subDescription.startsWith("id/")) {
+ throw new IncompleteInputException(StringLib.INVALID_ADD_TO_RECIPE_DESCRIPTION);
+ }
+ String[] subDescriptions = subDescription.substring(3).split("desc/",2);
+ if (subDescriptions.length < 2) {
+ throw new IncompleteInputException(StringLib.INVALID_ADD_TO_RECIPE_DESCRIPTION);
+ }
+ out[1] = subDescriptions[0].trim();
+ out[2] = subDescriptions[1].trim();
+ return out;
+ }
+ public static OperationType parseDeleteFromRecipeIndex(String description) throws IncompleteInputException {
+ boolean isIngredient = description.startsWith("--i ");
+ boolean isStep = description.startsWith("--s ");
+ if (isIngredient) {
+ return OperationType.INGREDIENT;
+ } else if (isStep) {
+ return OperationType.STEP;
+ } else {
+ throw new IncompleteInputException(StringLib.INVALID_DELETE_FROM_RECIPE_DESCRIPTION);
+ }
+ }
+ public static String[] parseDeleteFromRecipeDescription(String description) throws IncompleteInputException{
+ OperationType parsedDescription = parseDeleteFromRecipeIndex(description);
+ String[] out = new String[2];
+ out[0] = parsedDescription.equals(OperationType.INGREDIENT) ? "i" : "s";
+ String subDescription = description.substring(4).trim();
+ if (!subDescription.startsWith("id/")) {
+ throw new IncompleteInputException(StringLib.INVALID_DELETE_FROM_RECIPE_DESCRIPTION);
+ }
+ out[1] = subDescription.substring(3).trim();
+ return out;
+ }
+
+ public static boolean isValidAddToRecipe(String description) {
+ String descLowerCase = description.toLowerCase().trim();
+ if ((descLowerCase.contains("--s") && descLowerCase.contains("--i"))){
+ return false;
+ }
+ if (matchCount(descLowerCase,"id/") != 1) {
+ return false;
+ }
+ if (matchCount(descLowerCase,"desc/") != 1){
+ return false;
+ }
+ if (matchCount(descLowerCase, "--i") == 0 && matchCount(descLowerCase, "--s") == 0) {
+ return false;
+ }
+ if (matchCount(descLowerCase, "--i") > 1) {
+ return false;
+ }
+ if (matchCount(descLowerCase, "--s") > 1) {
+ return false;
+ }
+ return true;
+ }
+ public static boolean isValidDeleteFromRecipe(String description) {
+ String descLowerCase = description.toLowerCase().trim();
+ if ((descLowerCase.contains("--s") && descLowerCase.contains("--i"))){
+ return false;
+ }
+ if (matchCount(descLowerCase,"id/") != 1) {
+ return false;
+ }
+ if (matchCount(descLowerCase, "--i") == 0 && matchCount(descLowerCase, "--s") == 0) {
+ return false;
+ }
+ if (matchCount(descLowerCase, "--i") > 1) {
+ return false;
+ }
+ if (matchCount(descLowerCase, "--s") > 1) {
+ return false;
+ }
+ return true;
+ }
+ public static boolean isDuplicateIngredient(IngredientList ingredientList, String newIngredient) {
+ for (Ingredient ingredient : ingredientList.getList()) {
+ if (ingredient.getName().toLowerCase().equals(newIngredient)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ public static boolean isDuplicateStep(StepList stepList, String newStep) {
+ for (Step step : stepList.getList()) {
+ if (step.getStepDescription().toLowerCase().equals(newStep)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/seedu/duke/recipe/Ingredient.java b/src/main/java/seedu/duke/recipe/Ingredient.java
new file mode 100644
index 0000000000..722cd03a3b
--- /dev/null
+++ b/src/main/java/seedu/duke/recipe/Ingredient.java
@@ -0,0 +1,12 @@
+package seedu.duke.recipe;
+
+public class Ingredient {
+ protected String name;
+
+ public Ingredient(String inputName) {
+ name = inputName;
+ }
+ public String getName() {
+ return name;
+ }
+}
diff --git a/src/main/java/seedu/duke/recipe/IngredientList.java b/src/main/java/seedu/duke/recipe/IngredientList.java
new file mode 100644
index 0000000000..15351a945f
--- /dev/null
+++ b/src/main/java/seedu/duke/recipe/IngredientList.java
@@ -0,0 +1,120 @@
+package seedu.duke.recipe;
+
+import seedu.duke.exceptions.InvalidIndexRangeException;
+import seedu.duke.exceptions.ListEmptyException;
+import seedu.duke.ui.IntLib;
+import seedu.duke.ui.StringLib;
+
+import java.util.ArrayList;
+
+public class IngredientList {
+ protected ArrayList list;
+
+ protected int currIngredientNumber;
+
+ /**
+ * Class constructor without arguments.
+ */
+ public IngredientList() {
+ list = new ArrayList<>();
+ currIngredientNumber = 0;
+ }
+
+ /**
+ * Class constructor with argument. An existing array list of ingredients
+ * is used as the argument
+ *
+ * @param ingredientList - list of all ingredients in the recipe.
+ */
+ public IngredientList(ArrayList ingredientList) {
+ list = ingredientList;
+ currIngredientNumber = ingredientList.size();
+ }
+
+ public int getCurrIngredientNumber() {
+ return currIngredientNumber;
+ }
+
+ /**
+ * Ingredient getter method
+ * @param ingredientIndex index of ingredient in ingredientList
+ * @return Ingredient object stored at ingredientIndex
+ * @throws Exception if list does not contain specified index
+ */
+ public Ingredient getIngredient(int ingredientIndex) throws Exception{
+ try {
+ return list.get(ingredientIndex);
+ } catch (IndexOutOfBoundsException e) {
+ if (currIngredientNumber == 0) {
+ throw new ListEmptyException();
+ } else {
+ // add 1 to display in 1-based
+ throw new InvalidIndexRangeException(1,currIngredientNumber+1);
+ }
+ }
+ }
+ /**
+ * Adds a new ingredient to the list.
+ *
+ * @param item - the ingredient to be added to the list.
+ * @param ingredientIndex - position to be added. Current Ingredient at this position is
+ * shifted towards the back.
+ */
+ public void addIngredient(Ingredient item, int ingredientIndex) {
+ list.add(ingredientIndex, item);
+ currIngredientNumber++;
+ assert (currIngredientNumber == list.size());
+ }
+
+ /**
+ * Removes an ingredient from the list.
+ *
+ * @param ingredientIndex - the index of the ingredient to be removed from the list.
+ */
+ public void removeIngredient(int ingredientIndex) {
+ list.remove(ingredientIndex-1);
+ currIngredientNumber--;
+ assert (currIngredientNumber == list.size());
+ }
+ public void showList() {
+ if(currIngredientNumber == 1){
+ System.out.println("There is " + currIngredientNumber + " ingredient in the list:");
+ } else {
+ System.out.println("There are " + currIngredientNumber + " ingredients in the list:");
+ }
+ for (int i = 0; i < currIngredientNumber; i++) {
+ System.out.println((i + 1) + ". " + list.get(i).getName());
+ }
+ }
+ public ArrayList getList() {
+ return list;
+ }
+
+ public boolean isIndexWithinRange(int ingredientIndex) throws Exception{
+ if (currIngredientNumber == 0) {
+ throw new ListEmptyException();
+ } else if (ingredientIndex < 0 || ingredientIndex >= currIngredientNumber) {
+ throw new InvalidIndexRangeException(IntLib.NONEMPTY_START_NUMBER, currIngredientNumber);
+ }
+ return true;
+ }
+ public void editIngredient(int ingredientIndex, String description) throws Exception{
+ Ingredient newIngredient = new Ingredient(description);
+ if (isIndexWithinRange(ingredientIndex)) {
+ list.set(ingredientIndex, newIngredient);
+ System.out.println(StringLib.INGREDIENT_EDIT_SUCCESS);
+ System.out.print((ingredientIndex + 1) + ". ");
+ System.out.println(list.get(ingredientIndex).getName());
+ }
+ }
+ public void editIngredient(String ingredientDescription, int ingredientIndex) {
+ Ingredient newIngredient = new Ingredient(ingredientDescription);
+ list.set(ingredientIndex, newIngredient);
+ System.out.println(StringLib.INGREDIENT_EDIT_SUCCESS);
+ System.out.print((ingredientIndex + 1) + ". ");
+ System.out.println(list.get(ingredientIndex).getName());
+ }
+ public boolean isEmpty() {
+ return (getCurrIngredientNumber() == 0);
+ }
+}
diff --git a/src/main/java/seedu/duke/recipe/Recipe.java b/src/main/java/seedu/duke/recipe/Recipe.java
new file mode 100644
index 0000000000..1686caab46
--- /dev/null
+++ b/src/main/java/seedu/duke/recipe/Recipe.java
@@ -0,0 +1,33 @@
+package seedu.duke.recipe;
+
+public class Recipe {
+ protected IngredientList ingredientList;
+ protected StepList stepList;
+ protected String name;
+ protected String tag;
+
+ public Recipe(String inputName, String inputTag, IngredientList inputList, StepList inputSteps) {
+ name = inputName;
+ tag = inputTag;
+ ingredientList = inputList;
+ stepList = inputSteps;
+ }
+
+ public IngredientList getIngredientList() {
+ return ingredientList;
+ }
+ public StepList getStepList() {
+ return stepList;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getTag() {
+ return tag;
+ }
+ public String toString() {
+ return '[' + tag + "] " + name;
+ }
+}
diff --git a/src/main/java/seedu/duke/recipe/RecipeList.java b/src/main/java/seedu/duke/recipe/RecipeList.java
new file mode 100644
index 0000000000..ed8810da07
--- /dev/null
+++ b/src/main/java/seedu/duke/recipe/RecipeList.java
@@ -0,0 +1,237 @@
+package seedu.duke.recipe;
+
+import seedu.duke.exceptions.DuplicateRecipeNameException;
+import seedu.duke.exceptions.NoMatchingRecipeFound;
+import seedu.duke.exceptions.OutOfIndexException;
+import seedu.duke.exceptions.EditFormatException;
+import seedu.duke.exceptions.RecipeListEmptyException;
+import seedu.duke.exceptions.IncompleteInputException;
+import seedu.duke.ui.StringLib;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+
+import static seedu.duke.ui.StringLib.DUPLICATE_RECIPE_NAMES_ERROR;
+import static seedu.duke.ui.StringLib.INVALID_RANGE;
+import static seedu.duke.ui.StringLib.MISSING_FIND_KEYWORD;
+import static seedu.duke.ui.StringLib.EMPTY_LIST_MESSAGE;
+import static seedu.duke.ui.StringLib.NO_MATCHES;
+import static seedu.duke.ui.StringLib.MATCHING_ITEMS;
+import static seedu.duke.ui.StringLib.NO_MATCHING_RECIPE_ERROR;
+import static seedu.duke.ui.StringLib.INVALID_NUMBER_ERROR;
+import static seedu.duke.ui.StringLib.OVERFLOW_NUMBER_ERROR;
+
+public class RecipeList {
+ protected static ArrayList recipeList;
+ protected static int currRecipeNumber;
+ private static RecipeList instance;
+
+ private RecipeList() {
+ recipeList = new ArrayList<>();
+ currRecipeNumber = 0;
+ assert (recipeList.size() == 0);
+ }
+
+ public static void createRecipeList() {
+ if (instance == null) {
+ instance = new RecipeList();
+ }
+ }
+
+ public static boolean isEmpty() {
+ return recipeList.isEmpty();
+ }
+
+ public static ArrayList getRecipeList() {
+ return recipeList;
+ }
+
+ public static int getCurrRecipeNumber() {
+ assert (currRecipeNumber == recipeList.size());
+ return currRecipeNumber;
+ }
+
+ public static Recipe getRecipeFromList(int itemNum) {
+ return recipeList.get(itemNum - 1);
+ }
+
+ public static Recipe getNewestRecipe() {
+ return recipeList.get(currRecipeNumber - 1);
+ }
+
+ public static void addNewRecipe(Recipe recipe) {
+ recipeList.add(recipe);
+ currRecipeNumber++;
+ assert (currRecipeNumber == recipeList.size());
+ }
+
+ public static void removeRecipe(int index) throws RecipeListEmptyException {
+ if (recipeList.isEmpty()) {
+ throw new RecipeListEmptyException();
+ }
+ recipeList.remove(index - 1);
+ currRecipeNumber--;
+ assert (currRecipeNumber == recipeList.size());
+ }
+
+ public static void clearRecipeList() {
+ recipeList.clear();
+ currRecipeNumber = 0;
+ assert (recipeList.size() == 0);
+ }
+
+ public static void searchRecipeList(String term) {
+ ArrayList matches = new ArrayList<>();
+ term = term.trim().toLowerCase();
+ if (term.equals(StringLib.EMPTY_STRING)) {
+ System.out.println(MISSING_FIND_KEYWORD);
+ return;
+ }
+ if (getCurrRecipeNumber() == 0) {
+ System.out.println(EMPTY_LIST_MESSAGE);
+ return;
+ }
+ for (int i = 1; i <= getCurrRecipeNumber(); i++) {
+ Recipe dish = getRecipeFromList(i);
+ if (dish.getName().trim().toLowerCase().contains(term)) {
+ matches.add(dish + " [Index: " + i + "]");
+ }
+ }
+ if (matches.isEmpty()) {
+ System.out.println(NO_MATCHES);
+ } else {
+ if (matches.size() == 1) {
+ System.out.println(StringLib.MATCHING_ITEM_SINGLE);
+ } else {
+ System.out.println(MATCHING_ITEMS);
+ }
+ for (String match : matches) {
+ System.out.println(" " + match);
+ }
+ }
+ }
+
+ public static void editIngredient(int recipeIndex, int ingredientIndex, String newIngredient)
+ throws EditFormatException {
+ if (recipeIndex > getCurrRecipeNumber() || recipeIndex < 1) {
+ if(getCurrRecipeNumber() > 0) {
+ String range = "\nValid range: " + 1 + " to " + getCurrRecipeNumber();
+ throw new EditFormatException(StringLib.INVALID_RECIPE_INDEX + range);
+ } else {
+ throw new EditFormatException(StringLib.EMPTY_RECIPE_LIST);
+ }
+ }
+ Recipe recipe = getRecipeFromList(recipeIndex);
+ IngredientList ingredientList = recipe.getIngredientList();
+ if (ingredientIndex > ingredientList.getCurrIngredientNumber() || ingredientIndex < 1) {
+ if (ingredientList.getCurrIngredientNumber() > 0) {
+ String range = "\nValid range: " + 1 + " to " + ingredientList.getCurrIngredientNumber();
+ throw new EditFormatException(StringLib.INVALID_INGREDIENT_INDEX + range);
+ } else {
+ throw new EditFormatException(StringLib.EMPTY_INGREDIENT_LIST);
+ }
+ }
+ ingredientList.editIngredient(newIngredient, ingredientIndex - 1);
+ }
+
+ public static void editStep(Integer recipeIndex, int stepIndex, String newStep) throws EditFormatException {
+ if (recipeIndex > getCurrRecipeNumber() || recipeIndex < 1) {
+ if (getCurrRecipeNumber() > 0) {
+ String range = "\nValid range: " + 1 + " to " + getCurrRecipeNumber();
+ throw new EditFormatException(StringLib.INVALID_RECIPE_INDEX + range);
+ } else {
+ throw new EditFormatException(StringLib.EMPTY_RECIPE_LIST);
+ }
+ }
+ Recipe recipe = getRecipeFromList(recipeIndex);
+ StepList stepList = recipe.getStepList();
+ if (stepIndex > stepList.getCurrStepNumber() || stepIndex < 1) {
+ if (stepList.getCurrStepNumber() > 0) {
+ String range = "\nValid range: " + 1 + " to " + stepList.getCurrStepNumber();
+ throw new EditFormatException(StringLib.INVALID_STEP_INDEX + range);
+ } else {
+ throw new EditFormatException(StringLib.EMPTY_STEP_LIST);
+ }
+ }
+ stepList.editStep(stepIndex - 1, newStep);
+ }
+
+ public static void searchByTag(String tag) {
+ ArrayList matches = new ArrayList<>();
+ tag = tag.trim().toLowerCase();
+ if (tag.equals(StringLib.EMPTY_STRING)) {
+ System.out.println(MISSING_FIND_KEYWORD);
+ } else if (getCurrRecipeNumber() == 0) {
+ System.out.println(EMPTY_LIST_MESSAGE);
+ } else {
+ for (int i = 1; i <= getCurrRecipeNumber(); i++) {
+ Recipe dish = getRecipeFromList(i);
+ if (dish.getTag().trim().toLowerCase().contains(tag)) {
+ matches.add(dish + " [Index: " + i + "]");
+ }
+ }
+ if (matches.isEmpty()) {
+ System.out.println(NO_MATCHES);
+ } else {
+ if (matches.size() == 1) {
+ System.out.println(StringLib.MATCHING_ITEM_SINGLE);
+ } else {
+ System.out.println(MATCHING_ITEMS);
+ }
+ for (String match : matches) {
+ System.out.println(" " + match);
+ }
+ }
+ }
+ }
+
+ public static Recipe viewRecipe(String term)
+ throws DuplicateRecipeNameException, NoMatchingRecipeFound, OutOfIndexException {
+ Recipe recipeToBeViewed;
+ try {
+ int recipeListIndex = Integer.parseInt(term);
+ if (recipeListIndex <= 0 || recipeListIndex > getCurrRecipeNumber()) {
+ throw new OutOfIndexException(INVALID_RANGE + "1 to " + getCurrRecipeNumber() + '\n');
+ }
+ recipeToBeViewed = recipeList.get(recipeListIndex - 1);
+ } catch (NumberFormatException e) {
+ ArrayList viewRecipeResults = new ArrayList<>();
+ for (Recipe recipe : recipeList) {
+ if (recipe.getName().equalsIgnoreCase(term)) {
+ viewRecipeResults.add(recipe);
+ }
+ }
+ if (viewRecipeResults.isEmpty()) {
+ throw new NoMatchingRecipeFound(NO_MATCHING_RECIPE_ERROR);
+ } else if (viewRecipeResults.size() == 1) {
+ recipeToBeViewed = viewRecipeResults.get(0);
+ } else {
+ throw new DuplicateRecipeNameException(DUPLICATE_RECIPE_NAMES_ERROR);
+ }
+ }
+ return recipeToBeViewed;
+ }
+ public static Recipe getRecipe(String term)
+ throws Exception {
+ Recipe targetRecipe;
+ try{
+ Integer.parseInt(term);
+ } catch (NumberFormatException e) {
+ try {
+ new BigInteger(term);
+ } catch (Exception e1) {
+ throw new IncompleteInputException(INVALID_NUMBER_ERROR);
+ }
+ throw new IncompleteInputException(OVERFLOW_NUMBER_ERROR);
+ }
+ int recipeListIndex = Integer.parseInt(term);
+ if (getCurrRecipeNumber() == 0) {
+ throw new RecipeListEmptyException();
+ }
+ if (recipeListIndex <= 0 || recipeListIndex > getCurrRecipeNumber()) {
+ throw new OutOfIndexException(INVALID_RANGE + "1 to " + getCurrRecipeNumber() + '\n');
+ }
+ targetRecipe = recipeList.get(recipeListIndex - 1);
+ return targetRecipe;
+ }
+}
diff --git a/src/main/java/seedu/duke/recipe/Step.java b/src/main/java/seedu/duke/recipe/Step.java
new file mode 100644
index 0000000000..f144b6550c
--- /dev/null
+++ b/src/main/java/seedu/duke/recipe/Step.java
@@ -0,0 +1,15 @@
+package seedu.duke.recipe;
+
+public class Step {
+ protected String description;
+ public Step(String inputDescription) {
+ description = inputDescription;
+ }
+ public String getStepDescription() {
+ return description;
+ }
+ @Override
+ public String toString() {
+ return description;
+ }
+}
diff --git a/src/main/java/seedu/duke/recipe/StepList.java b/src/main/java/seedu/duke/recipe/StepList.java
new file mode 100644
index 0000000000..77af9c951f
--- /dev/null
+++ b/src/main/java/seedu/duke/recipe/StepList.java
@@ -0,0 +1,142 @@
+package seedu.duke.recipe;
+
+import java.util.ArrayList;
+
+import seedu.duke.exceptions.InvalidIndexRangeException;
+import seedu.duke.exceptions.ListEmptyException;
+import seedu.duke.ui.IntLib;
+import seedu.duke.ui.UI;
+import seedu.duke.ui.StringLib;
+
+public class StepList {
+ protected ArrayList stepList;
+ protected int currStepNumber;
+
+ /**
+ * Class constructor without arguments.
+ */
+ public StepList() {
+ stepList = new ArrayList<>();
+ currStepNumber = 0;
+ }
+ /**
+ * Class constructor with argument. An existing array list of steps
+ * is used as the argument
+ *
+ * @param stepList - list of all steps in the recipe.
+ *
+ */
+ public StepList(ArrayList stepList) {
+ this.stepList = stepList;
+ currStepNumber = stepList.size();
+ }
+
+
+ /**
+ * Adds a new step to the list.
+ *
+ * @param step - the step to be added to the list.
+ * @param index - position to be added. Current step object at this position is
+ * shifted towards the back.
+ */
+ public void addStep(Step step, int index) {
+ stepList.add(index, step);
+ currStepNumber++;
+ assert (currStepNumber == stepList.size());
+ }
+ /**
+ * Removes a step from the list.
+ *
+ * @param stepIndex - the index of the step to be removed from the list.
+ */
+ public void removeStep(int stepIndex) {
+ stepList.remove(stepIndex - 1);
+ currStepNumber--;
+ assert (currStepNumber == stepList.size());
+ }
+
+ public void checkIndexWithinRange(int stepIndex) throws Exception{
+ if (currStepNumber == 0) {
+ throw new ListEmptyException();
+ } else if (stepIndex < 0 || stepIndex >= currStepNumber) {
+ throw new InvalidIndexRangeException(IntLib.NONEMPTY_START_NUMBER,currStepNumber);
+ }
+ }
+ /**
+ * Replaces a specified step in the list with a new step object
+ * @param stepIndex index of the step to be replaced
+ * @param ui scanner class
+ */
+ public void editStep(int stepIndex, UI ui) {
+ System.out.println(StringLib.ENTER_STEP_DESCRIPTION);
+ String description = ui.readCommand();
+ Step newStep = new Step(description);
+ stepList.set(stepIndex, newStep);
+ System.out.println(StringLib.STEP_EDIT_SUCCESS);
+ System.out.print((stepIndex + 1) + ". ");
+ System.out.println(stepList.get(stepIndex).toString());
+ }
+
+ public void editStep(int stepIndex, String description) {
+ Step newStep = new Step(description);
+ stepList.set(stepIndex, newStep);
+ System.out.println(StringLib.STEP_EDIT_SUCCESS);
+ System.out.print((stepIndex + 1) + ". ");
+ System.out.println(stepList.get(stepIndex).toString());
+ }
+ public void showFullStepList() {
+ assert (!stepList.isEmpty());
+ if (stepList.size() == 1) {
+ System.out.println("There is " + currStepNumber + " step in the list");
+ } else {
+ System.out.println("There are " + currStepNumber + " steps in the list");
+ }
+ for (int i = 0; i < currStepNumber; i++) {
+ System.out.println((i + 1) + ". " + stepList.get(i).getStepDescription());
+ }
+ }
+ public void showStepByStep(UI ui) {
+ for (int i = 0; i < currStepNumber; i++) {
+ System.out.println((i + 1) + ". " + stepList.get(i).getStepDescription());
+ if (i == currStepNumber - 1) {
+ System.out.println(StringLib.END_OF_RECIPE_STEPS);
+ return;
+ }
+ if (ui.readCommand().equals(StringLib.STEP_VIEW_QUIT_KEYWORD)) {
+ return;
+ }
+ }
+ }
+ public void showStepList(UI ui) {
+ if (currStepNumber == 0) {
+ System.out.println(StringLib.RECIPE_NO_STEPS);
+ return;
+ }
+ assert (!stepList.isEmpty());
+ if (stepList.size() == 1) {
+ System.out.println("There is " + currStepNumber + " step in the list");
+ } else {
+ System.out.println("There are " + currStepNumber + " steps in the list");
+ }
+ System.out.println(StringLib.STEPBYSTEP_PROMPT);
+ String input = ui.readCommand();
+ if (input.equalsIgnoreCase("yes")) {
+ System.out.println(StringLib.STEPBYSTEP_EARLY_TERMINATION_PROMPT);
+ showStepByStep(ui);
+ } else {
+ showFullStepList();
+ }
+ }
+ public Step getStep(int stepIndex) {
+ return stepList.get(stepIndex);
+ }
+ public int getCurrStepNumber() {
+ return currStepNumber;
+ }
+ public ArrayList getList() {
+ return stepList;
+ }
+ public boolean isEmpty() {
+ return (getCurrStepNumber() == 0);
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/Storage.java b/src/main/java/seedu/duke/storage/Storage.java
new file mode 100644
index 0000000000..c42a83ed6a
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/Storage.java
@@ -0,0 +1,161 @@
+package seedu.duke.storage;
+
+import seedu.duke.exceptions.FileStorageException;
+import seedu.duke.recipe.Ingredient;
+import seedu.duke.recipe.IngredientList;
+import seedu.duke.recipe.Recipe;
+import seedu.duke.recipe.RecipeList;
+import seedu.duke.recipe.Step;
+import seedu.duke.recipe.StepList;
+
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+
+/**
+ * The Storage class contains various methods which involves
+ * handling of the saved file. For instance, file reading, file writing and saving.
+ */
+public class Storage {
+
+ private static final String DIRECTORY_CREATED = "\nDirectory for file saving created.";
+ private static final String DIRECTORY_EXISTS = "\nDirectory for file saving already exists.";
+ private static String filePath = "data";
+
+ /**
+ * Sets the filePath to a specific value.
+ *
+ * @param filePath the String containing the location of the file path to be created.
+ */
+ public static void setFilePath(String filePath) {
+ Storage.filePath = filePath;
+ }
+
+ /**
+ * Creates a new directory to store
+ * save file, if it does not already exist.
+ *
+ * @throws FileStorageException if an I/O error has occurred.
+ */
+ public static void createDirectory() throws FileStorageException {
+ try {
+ File directory = new File(filePath);
+ if (directory.mkdir()) {
+ System.out.println(DIRECTORY_CREATED + "\n");
+ } else {
+ System.out.println(DIRECTORY_EXISTS + "\n");
+ }
+ } catch (Exception e) {
+ throw new FileStorageException(e.getMessage());
+ }
+ }
+
+ public static void deleteFile(File file) throws FileStorageException {
+ try {
+ if (file.isFile()) {
+ file.delete();
+ } else if (file.isDirectory()) {
+ File[] files = file.listFiles();
+ for (File item : files) {
+ deleteFile(item);
+ }
+ }
+ } catch (Exception e) {
+ throw new FileStorageException("Error in file clear:" + e.getMessage());
+ }
+ }
+
+ /**
+ * Writes save file in data folder.
+ * Format:
+ * DishName
+ * Tag
+ * "Ingredient list"
+ * list ingredients
+ * "Step list"
+ * list steps
+ */
+ public static void writeSavedFile() {
+ File folder = new File(filePath);
+ try {
+ deleteFile(folder);
+ } catch (FileStorageException e) {
+ System.out.println(e.getMessage());
+ return;
+ }
+ try {
+ FileWriter saveWriter;
+ int dishIndex = 1;
+ for (Recipe dish : RecipeList.getRecipeList()) {
+ saveWriter = new FileWriter("data/" + dishIndex + ".txt");
+ saveWriter.write(dish.getName() + "\n");
+ saveWriter.write(dish.getTag() + "\n");
+ saveWriter.write(dish.getIngredientList().getCurrIngredientNumber() + "\n");
+ for (Ingredient ingredient : dish.getIngredientList().getList()) {
+ saveWriter.write( ingredient.getName() + "\n");
+ }
+ saveWriter.write(dish.getStepList().getCurrStepNumber() + "\n");
+ for (Step step : dish.getStepList().getList()) {
+ saveWriter.write(step.getStepDescription() + "\n");
+ }
+ saveWriter.close();
+ dishIndex++;
+ }
+ } catch (IOException e) {
+ System.out.println("Error in file writing:" + e.getMessage());
+ }
+ }
+ public static ArrayList findValidSaveFiles() {
+ File searchArea = new File("data");
+ File[] saveList = searchArea.listFiles();
+ ArrayList validSaveFiles = new ArrayList<>();
+ for (File item : saveList) {
+ if (item.getName().contains(".txt")) {
+ validSaveFiles.add(item);
+ }
+ }
+ return validSaveFiles;
+ }
+
+ /**
+ * Loads all saved recipes into recipe list.
+ *
+ * @throws FileNotFoundException when file is not present or corrupted.
+ */
+ public static void loadSaveFiles() throws FileNotFoundException {
+ ArrayList validSaves = findValidSaveFiles();
+ ArrayList recipeList = new ArrayList<>();
+ for (File saveFile : validSaves) {
+ Scanner reader = new Scanner(saveFile);
+ String name = reader.nextLine();
+ String tag = reader.nextLine();
+ ArrayList ingredientList = new ArrayList<>();
+ ArrayList stepList = new ArrayList<>();
+ int numOfIngredients;
+ int numOfSteps;
+ numOfIngredients = Integer.parseInt(reader.nextLine());
+ for (int i = 0; i < numOfIngredients; i++) {
+ String ingredient = reader.nextLine();
+ ingredientList.add(new Ingredient(ingredient));
+ }
+ numOfSteps = Integer.parseInt(reader.nextLine());
+ for (int i = 0; i < numOfSteps; i++) {
+ String step = reader.nextLine();
+ stepList.add(new Step(step));
+ }
+ recipeList.add(new Recipe(
+ name,
+ tag,
+ new IngredientList(ingredientList),
+ new StepList(stepList)));
+ reader.close();
+ }
+ for (Recipe item : recipeList) {
+ RecipeList.addNewRecipe(item);
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/ui/IntLib.java b/src/main/java/seedu/duke/ui/IntLib.java
new file mode 100644
index 0000000000..2e313935f5
--- /dev/null
+++ b/src/main/java/seedu/duke/ui/IntLib.java
@@ -0,0 +1,11 @@
+package seedu.duke.ui;
+
+public interface IntLib {
+ int RECIPE_NAME_INDEX = 0;
+ int RECIPE_INGREDIENTS_INDEX = 1;
+ int RECIPE_TAG_INDEX = 2;
+ int RECIPE_SUM_OF_STEPS_INDEX = 3;
+ int NONEMPTY_START_NUMBER = 1;
+ int NONEMPTY_START_INDEX = 0;
+ int ADD_STEP_INDEX_BREAKOUT = -23;
+}
diff --git a/src/main/java/seedu/duke/ui/StringLib.java b/src/main/java/seedu/duke/ui/StringLib.java
new file mode 100644
index 0000000000..c936dbb182
--- /dev/null
+++ b/src/main/java/seedu/duke/ui/StringLib.java
@@ -0,0 +1,240 @@
+package seedu.duke.ui;
+
+public interface StringLib {
+ String LOGO = "_____ _ _____ __ ___ ___ _ _______ "
+ + "________ ____\n"
+ + "|_ _| | | | _ |/ _| | \\/ | ( ) / /_ _| _ | \\/ \\ \\\n"
+ + " | | __ _ ___| |_ ___ | | | | |_ | . . | ___ _ __ ___ |/ ___ | | | | | | | | . . || |\n"
+ + " | |/ _` / __| __/ _ \\ | | | | _| | |\\/| |/ _ \\| '_ ` _ \\ / __| | | | | | | | | |\\/| || |\n"
+ + " | | (_| \\__ \\ || __/ \\ \\_/ / | | | | | (_) | | | | | | \\__ \\ | | | | \\ \\_/ / | | || |\n"
+ + " \\_/\\__,_|___/\\__\\___| \\___/|_| \\_| |_/\\___/|_| |_| |_| |___/ | | \\_/ \\___/\\_| |_/| |"
+ + "\n"
+ + " \\_\\ /_/\n";
+ String WELCOME_MESSAGE = "\nHELLO there! I am\n "
+ + LOGO + '\n'
+ + "Your personal recipes assistant!\n"
+ + "What can I do for you today?\n\n"
+ + "You can start by adding recipes to a recipe list that I can generate,"
+ + " simply follow the format below:\n\n"
+ + "Add recipe : \"add n/ i/ "
+ + "t/ s/\"\n\n"
+ + "If you wish to view the full list of commands, simply type \"help\"!\n";
+ String HELP = "\nHelp is here! You may find the list of commands below useful. \n \n \n"
+ + "COMMANDS LIST: \n \n \n"
+ + "## Help ## \n"
+ + "Description : Shows a message explaining how to access the help page. \n"
+ + "Format : \"help\" \n"
+ + "Example use : \"help\" \n \n"
+ + "## Add ## \n"
+ + "Description : Adds a new recipe to the list. \n"
+ + "Format : \"add n/ i/ "
+ + "t/ s/\"\n"
+ + "Example use : \"add n/Hotpot i/Beef, Potatoes, Carrots t/Chinese s/4\" \n \n"
+ + "## Add Step/Ingredient ## \n"
+ + "Description : Adds a step or ingredient to a particular recipe in the recipe list.\n"
+ + " As shown in the format, the flag for \"--\" can be used interchangeably,\n"
+ + " 's' to add a step and 'i' to add an ingredient. \n"
+ + "Format : \"addtorecipe --