diff --git a/.gitignore b/.gitignore index 71c9194e8bd..20c9b6e45b1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,11 +9,15 @@ src/main/resources/docs/ /*.iml # Storage/log files -/data/ /config.json /preferences.json /*.log.* +# Data files +/data/ +/order/ +/profile/ + # Test sandbox files src/test/data/sandbox/ diff --git a/README.md b/README.md index 13f5c77403f..aba0caef058 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,28 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) + + +# Supper Strikers + +[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/AY2021S1-CS2103-T16-1/tp/actions) ![Ui](docs/images/Ui.png) -* This is **a sample project for Software Engineering (SE) students**.
- Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. - * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. - * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. -* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. + + +**Supper Strikers is a desktop application for managing your supper orders.** It is targeted at students living in NUS for ordering delivery from supper stretch. While it has a GUI (Graphical User Interface), most of the user interactions happen using a CLI (Command Line Interface). + +- If you are interested in using Supper Strikers, head over to the [**User Guide**](docs/UserGuide.md). +- If you are interested about developing Supper Strikers, the [**Developer Guide**](docs/DeveloperGuide.md) is a good place to start. + + + +## Libraries Used + +- JavaFX - Used for creating the GUI +- Jackson - Used for processing JSON files +- JUnit5 - Used for unit testing + + + +## Acknowledgments + +- Adapted from AddressBook Level 3 (AB3), part of the **se-education.org initiative.** If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info . diff --git a/build.gradle b/build.gradle index be2d2905dde..85e77299608 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,10 @@ mainClassName = 'seedu.address.Main' sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 +run { + enableAssertions = true +} + repositories { mavenCentral() maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } @@ -42,7 +46,7 @@ task coverage(type: JacocoReport) { dependencies { String jUnitVersion = '5.4.0' - String javaFxVersion = '11' + String javaFxVersion = '11.0.1' implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win' implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac' @@ -66,7 +70,7 @@ dependencies { } shadowJar { - archiveName = 'addressbook.jar' + archiveName = 'supperstrikers.jar' } defaultTasks 'clean', 'test' diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..b245fd7ba93 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,59 +1,53 @@ ---- -layout: page -title: About Us ---- - We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg). -You can reach us at the email `seer[at]comp.nus.edu.sg` +You can contact us at the email `seer[at]comp.nus.edu.sg` ## Project team -### John Doe +### Lim Wei Quan, Ernest - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/ernestlim8)] +[[portfolio](http://ernestlim8.netlify.app/)] -* Role: Project Advisor +* Role: Team Lead -### Jane Doe +### Ambrose Liew Cheng Yuan - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/MorningLit)] +[[portfolio](https://ambroseliew.netlify.app/)] -* Role: Team Lead -* Responsibilities: UI +* Role: Developer +* Responsibilities: Checkstyle, Documentation, Intellij expert, Arts Director -### Johnny Doe +### Anikesh Bhuvaneshwaram - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](https://github.com/Ebolaeater)] +[[portfolio](https://anikeshb.netlify.app)] * Role: Developer -* Responsibilities: Data +* Responsibilities: Integration, Scheduling and Tracking, Data Handler -### Jean Doe +### Tan Wei Xin - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](github.com/duckmoon99/)] +[[portfolio](https://www.linkedin.com/in/tanweixin/)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: Code Quality -### James Doe +### Chan Yong Soon, Kendrew - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/KendrewChan)] [[portfolio]([https://kendrewc.netlify.app](https://kendrewc.netlify.app/))] * Role: Developer -* Responsibilities: UI +* Responsibilities: Testing, Handle Code Skeletons, Deliverables and deadlines diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 4829fe43011..04071692ab3 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -23,11 +23,11 @@ The ***Architecture Diagram*** given above explains the high-level design of the
-:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/se-edu/addressbook-level3/tree/master/docs/diagrams/) folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams. +:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/AY2021S1-CS2103-T16-1/tp/tree/master/docs/diagrams) folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
-**`Main`** has two classes called [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java). It is responsible for, +**`Main`** has two classes called [`Main`](https://github.com/AY2021S1-CS2103-T16-1/tp/blob/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/AY2021S1-CS2103-T16-1/tp/blob/master/src/main/java/seedu/address/MainApp.java). It is responsible for, * At app launch: Initializes the 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. @@ -47,11 +47,11 @@ Each of the four components, For example, the `Logic` component (see the class diagram given below) defines its API in the `Logic.java` interface and exposes its functionality using the `LogicManager.java` class which implements the `Logic` interface. -![Class Diagram of the Logic Component](images/LogicClassDiagram.png) +![Class Diagram of the Logic Component](images/LogicClassDiagram2.png) **How the architecture components interact with each other** -The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`. +The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `remove 1`. @@ -62,66 +62,78 @@ The sections below give more details of each component. ![Structure of the UI Component](images/UiClassDiagram.png) **API** : -[`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java) +[`Ui.java`](https://github.com/AY2021S1-CS2103-T16-1/tp/blob/master/src/main/java/seedu/address/ui/Ui.java) -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class. +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `FoodListPanel`, `OrderListPanel`, `VendorListPanel` `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class. -The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml) +The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/AY2021S1-CS2103-T16-1/tp/blob/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/AY2021S1-CS2103-T16-1/tp/blob/master/src/main/resources/view/MainWindow.fxml) The `UI` component, * Executes user commands using the `Logic` component. * Listens for changes to `Model` data so that the UI can be updated with the modified data. +The `UI` component has two modes, the Vendor mode and the Menu mode. + +- If `Model#getVendorIndex()` is -1 (default value), the `UI` will be in Vendor mode, otherwise, the `UI` will be in Menu mode. +- The mode can be updated using `MainWindow#updateMode()`. +- In Vendor mode, the `VendorListPanel` will be displayed to the user and the `FoodListPanel` will be hidden. +- In Menu mode, the `FoodListPanel` will be displayed to the user and the `VendorListPanel` will be hidden. + + + ### Logic component -![Structure of the Logic Component](images/LogicClassDiagram.png) +![Structure of the Logic Component](images/LogicClassDiagram2.png) **API** : -[`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java) +[`Logic.java`](https://github.com/AY2021S1-CS2103-T16-1/tp/blob/master/src/main/java/seedu/address/logic/Logic.java) -1. `Logic` uses the `AddressBookParser` class to parse the user command. +1. `Logic` uses the `SupperStrikersParser` class to parse the user command. 1. This results in a `Command` object which is executed by the `LogicManager`. -1. The command execution can affect the `Model` (e.g. adding a person). +1. The command execution can affect the `Model` (e.g. adding an order item). +1. The command execution can affect the `Storage` (e.g saving a preset). 1. The result of the command execution is encapsulated as a `CommandResult` object which is passed back to the `Ui`. -1. In addition, the `CommandResult` object can also instruct the `Ui` to perform certain actions, such as displaying help to the user. +1. In addition, the `CommandResult` object can also instruct the `UI` to perform certain actions, such as displaying help to the user. +1. The `CommandResult` object can also instruct the `UI` to re-render the menu when some commands such as `SortCommand` and `FindCommand`. -Given below is the Sequence Diagram for interactions within the `Logic` component for the `execute("delete 1")` API call. +Given below is the Sequence Diagram for interactions within the `Logic` component for the `execute("remove 1")` API call. -![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) +![Interactions Inside the Logic Component for the `remove 1` Command](images/DeleteSequenceDiagram2.png) -
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. +
:information_source: **Note:** The lifeline for `RemoveCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
### Model component -![Structure of the Model Component](images/ModelClassDiagram.png) +![Structure of the Model Component](images/ModelClassDiagram2.png) -**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) +**API** : [`Model.java`](https://github.com/AY2021S1-CS2103-T16-1/tp/blob/master/src/main/java/seedu/address/model/Model.java) The `Model`, * stores a `UserPref` object that represents the user’s preferences. -* stores the address book data. -* exposes an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. -* does not depend on any of the other three components. - +* contains the `VendorManager` and `MenuManager`, `OrderManager` components. + * `VendorManager` stores the data for vendors. + * `MenuManager` stores the data for food items of the vendor's menu. + * `OrderManager` stores the data for order items. -
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique `Tag`, instead of each `Person` needing their own `Tag` object.
-![BetterModelClassDiagram](images/BetterModelClassDiagram.png) +* Each of these components exposes an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. +* does not depend on any of the other three components. -
### Storage component -![Structure of the Storage Component](images/StorageClassDiagram.png) +![Structure of the Storage Component](images/StorageClassDiagram2.png) -**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) +**API** : [`Storage.java`](https://github.com/AY2021S1-CS2103-T16-1/tp/blob/master/src/main/java/seedu/address/storage/Storage.java) The `Storage` component, * can save `UserPref` objects in json format and read it back. -* can save the address book data in json format and read it back. +* can save the vendor book data in json format and read it back. +* can save the `Preset` objects in json format and read it back. +* can save the `Profile` object in json format and read it back. ### Common classes @@ -133,90 +145,425 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa This section describes some noteworthy details on how certain features are implemented. -### \[Proposed\] Undo/redo feature -#### Proposed Implementation +### Menu Commands -The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations: +Menu commands represent the operations with which the users can interact with the menu. -* `VersionedAddressBook#commit()` — Saves the current address book state in its history. -* `VersionedAddressBook#undo()` — Restores the previous address book state from its history. -* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history. -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +#### Find Command -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +- The Find Command allows the user to find items in the menu of their selected vendor `Model#updateFilteredMenuItemList()`. +- If a vendor has not been selected, a `CommandException will be thrown`. -Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state. -![UndoRedoState0](images/UndoRedoState0.png) +The following diagram summarises the sequence when the MenuCommand is executed. -Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state. +![FindCommandSequenceDiagram](images/FindCommandSequenceDiagram.png) -![UndoRedoState1](images/UndoRedoState1.png) -Step 3. The user executes `add n/David …​` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`. +Given below is an example usage scenario and how the FindCommand behaves at each step. -![UndoRedoState2](images/UndoRedoState2.png) +Step 1: The user has selected a vendor with vendor i. -
:information_source: **Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`. +Step 2: The user enters the command `find Milo` which finds and displays all the items in the selected menu with Milo in their names. -
+Step 3: `Model#updateFilteredMenuItemList()` is executed to filter the current menu to only contain items that have the string "Milo" in their names. -Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state. -![UndoRedoState3](images/UndoRedoState3.png) +#### Menu Command -
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather -than attempting to perform the undo. +- The Menu Command allows the user to view the default menu of their selected vendor `Model#showDefaultMenu`. +- If a vendor has not been selected, a `CommandException will be thrown`. -
-The following sequence diagram shows how the undo operation works: +The following diagram summarises the sequence when the MenuCommand is executed. -![UndoSequenceDiagram](images/UndoSequenceDiagram.png) +![MenuCommandSequenceDiagram](images/MenuCommandSequenceDiagram.png) -
:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. -
+Given below is an example usage scenario and how the FindCommand behaves at each step. -The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state. +Step 1: The user has selected a vendor with vendor i. -
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo. +Step 2: The user enters the command `find Milo` which finds and displays all the items in the selected menu with Milo in their names. -
+Step 3: The user enters the command `menu` to display the selected menu with all the items. + +Step 4: `Model#showDefaultMenu()` is executed to retrieve and display the selected menu. + + +#### Sort Command + +- The Sort Command allows the user to sort the selected menu based on price or name in ascending or descending order. +- If a vendor has not been selected, a `CommandException will be thrown`. +- If the parameters inputted are invalid, a `ParseException will be thrown`. + + +The following diagram summarizes the sequence when the Sort Command is executed. + +![SortCommandSequenceDiagram](images/SortCommandSequenceDiagram.png) + + +Given below is an example usage scenario and how the SortCommand behaves at each step. + +Step 1: The user has selected a vendor with vendor i. + +Step 2: The user enters the command `sort n a` which sorts the menu in ascending alphabetical order based on their names. + +Step 3: SortCommand checks whether the parameters - ascending and name, inputted are valid. The parameters are valid. + +Step 4: `Model#sortMenuItemBy()` is executed to sort the currently displayed menu in alphabetical ascending order of the menu item names. + + + +### Order Commands + +Order commands represents the operations to which users interact with. + +#### Add Command + +- The Add Command allows the user to add an order item from the selected menu `Model#getFilteredMenuItemList()`. +- If the index provided is greater than the size of the menu or less than equals to zero, a `CommandException will be thrown`. +- If the quantity provided is less than equals to zero, a `CommandException will be thrown`. +- If the resultant supper order's quantity added exceeds 100, a `CommandException will be thrown`. + + +The following diagram summarises the sequence when the AddCommand is executed. + +![AddCommandDiagram](images/AddCommandDiagram.png) + + + +Given below is an example usage scenario and how the AddCommand behaves at each step. + +Step 1: The user launches the application for the first time, by default, no vendor is selected. + +Step 2: The user selects a vendor with the VendorCommand `vendor i`, the corresponding menu will be loaded. + +Step 3: The user enters the command `add 2 3` which adds item 2 from the menu with a quantity of 3. + +Step 4: `Model#getFilteredMenuItemList()` is executed to retrieve the list of menu items from the current vendor. + +Step 5: `SupperStrikers` creates an AddCommandParser and calls `AddCommandParser#parse()` with argument "2 3". + +Step 6: AddCommandParser checks whether the index and quantity inputted is valid. Index and Quantity is valid, then an AddCommand is created. + +Step 7: An OrderItem object is created from the retrieved menu item and input quantity. + +Step 8: `Model#addOrderItem()` is executed to add the OrderItem into the `Model`. + + + +#### Remove Command + +- The RemoveCommand allows the user to remove an order from his order list `Model#getObservableOrderItemList()`. +- If the index provided is greater than the size of his order list or less than or equal to zero, a `CommandException will be thrown`. +- If the quantity provided is less than equals to zero or greater than the current quantity, a `CommandException will be thrown`. + + + +The following diagram summarises the sequence when the RemoveCommand is executed. + +![RemoveCommandDiagram](images/RemoveCommandDiagram.png) + + +Given below is an example usage scenario and how the RemoveCommand behaves at each step. + +Step 1: The user has selected a vendor with `vendor i`. + +Step 2: The user has added items with `add i qty`. + +Step 3: The user enters the command `remove 1 1` which removes 1 quantity of the item at the 1st index in the order. + +Step 4: `Model#getObservableOrderItemList()` is executed to retrieve the list of OrderItems from the current order. + +Step 5: RemoveCommand checks whether the index and quantity inputted is valid. Index and Quantity is valid. + +Step 6: A new OrderItem object is created from the retrieved OrderItem and input quantity. + +Step 7: `Model#removeOrderItem()` is executed to remove the related OrderItem. + + + +#### Clear Command + +- The ClearCommand allows the user to clear all orders in the current order. +- If the current Order has no items, a `CommandException will be thrown`. + + + +The following diagram summarises the sequence when the ClearCommand is executed. + +![ClearCommandDiagram](images/ClearCommandDiagram.png) + + + +Given below is an example usage scenario and how the ClearCommand behaves at each step. + +Step 1: The user has selected a vendor with `vendor i`. + +Step 2: The user has added items with `add i qty`. + +Step 3: The user enters the command `clear`. + +Step 4: ClearCommand checks whether the order has OrderItems with `Model#getOrderSize()`. The order has OrderItems. + +Step 5: `Model#clearOrder()` is executed to clear all OrderItems from the order. + + + +#### Tag Command + + - The TagCommand allows the user to specify his preferences to an order item in the current order. + - If the index provided is greater than the size of the user's order list or less than equals to zero, a `CommandException will be thrown`. + - If the tag provided already exists, a `CommandException will be thrown`. + + +The following diagram summarizes the sequence when the TagCommand is executed. +![TagCommandActivityDiagram](images/TagCommandActivityDiagram.png) + +Given below is an example usage scenario and how the TagCommand behaves at each step. + +Step 1: The user has selected a vendor with `vendor i`. + +Step 2: The user has added items with `add i qty`. + +Step 3: The user enters the command `tag 1 1 no ice` which adds the tag of "1 no ice" to the item at the first index in the order. + +Step 4: `Model#getObservableOrderItemList()` is executed to retrieve the list of OrderItems from the current order. + +Step 5: TagCommand checks whether the index and tag inputted is valid. Index and tag is valid. + +Step 6: `Model#tagOrderItem()` is executed to tag the given OrderItem. + + +#### Untag Command + +- The UntagCommand allows users to remove all tags from an order item in the current order. +- If the index provided is greater than the size of the user's order list or less than equals to zero, a `CommandException will be thrown`. + +The following diagram summarizes the sequence when the TagCommand is executed. +![UntagCommandActivityDiagram](images/UntagCommandActivityDiagram.png) + +Given below is an example usage scenario and how the UnTagCommand behaves at each step. + +Step 1: The user has selected a vendor with `vendor i`. + +Step 2: The user has added items with `add i qty`. + +Step 3: The user tagged the item at index 1 with `tag 1 all no ice`. + +Step 4: The user enters the command `untag 1` to remove the tag of "all no ice" from the OrderItem at index 1. + +Step 5: `Model#getObservableOrderItemList()` is executed to retrieve the list of OrderItems from the current order. + +Step 6: UntagCommand checks whether the index inputted is valid. Index is valid. + +Step 7: `Model#untagOrderItem()` is executed to remove all the tags that the given OrderItem has. + + + +#### Preset Commands + +- There are three types of preset commands, `preset save`, `preset load` `preset delete`. +- Preset save stores the current order in a file, with the given preset name under the specific vendor. +- Preset load retrieves the preset with the name provided from the user, and loads it into the current order. +- Preset delete deletes the preset with the name provided from the user. + +- If the name given is invalid for preset commands, a `CommandException will be thrown`. +- If the name already exists for preset save, the previous file will be overwritten with the current one. +- Preset load retrieves the preset with the name provided from the file, and loads it into the current order. + +The following sequence diagram summarises the sequence when the LoadPresetCommand is executed. + + + +![LoadPresetSequenceDiagram](images/LoadPresetCommandSequenceDiagram.png) -Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged. +Given below is an example usage scenario and how the LoadPresetCommand behaves at each step. -![UndoRedoState4](images/UndoRedoState4.png) +Step 1: The user has selected a vendor with `vendor i`. -Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …​` command. This is the behavior that most modern desktop applications follow. +Step 2: The user enters the command `load preset sample` to load the preset named `sample`. -![UndoRedoState5](images/UndoRedoState5.png) +Step 3: `Storage#readPresetManager()` is executed to retrieve the list of all presets, `allLists`,from the presets json file. -The following activity diagram summarizes what happens when a user executes a new command: +Step 4: The preset with the name `sample` for vendor `i` exists and is valid. -![CommitActivityDiagram](images/CommitActivityDiagram.png) +Step 5: The preset is obtained and the ArrayList of `OrderItem` is obtained. -#### Design consideration: +Step 6: `Model#setOrder()` is executed to set the current order to the obtained ArrayList of `OrderItem`. -##### Aspect: How undo & redo executes -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. -* **Alternative 2:** Individual command knows how to undo/redo by - itself. - * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted). - * Cons: We must ensure that the implementation of each individual command are correct. +Given below is the activity diagram for the LoadPresetCommand. -_{more aspects and alternatives to be added}_ +![LoadPresetCommandActivityDiagram](images/LoadPresetCommandActivityDiagram.png) -### \[Proposed\] Data archiving -_{Explain here how the data archiving feature will be implemented}_ +The following diagram sequence summarises the sequence when the SavePresetCommand is executed. + +![SavePresetSequenceDiagram](images/SavePresetCommandSequenceDiagram.png) + +Given below is an example usage scenario and how the SavePresetCommand behaves at each step. + +Step 1: The user has selected a vendor with `vendor i`. + +Step 2: The user has added items with `add i qty`. + +Step 3: The user enters the command `save preset sample` to save the preset with the name `sample`. + +Step 4: `Storage#readPresetManager()` is executed to retrieve the list of all presets, `allLists`, from the presets json file. + +Step 5: The preset with the name `sample` for vendor `i` does not exist. + +Step 6: The current order item list, `orderItemList`, is retrieved by executing `Model#getObservableOrderItemList()`. + +Step 7: `orderItemList` is converted to a preset named `sample`. + +Step 8: `sample` is added to the vendor index `i` position of `allLists`. + +Step 9: `Storage#savePresetManager()` is executed to save the modified `allLists` into the `presets` json file. + + + +Given below is the activity diagram for SavePresetCommand. + +![SavePresetCommandActivityDiagram](images/SavePresetCommandActivityDiagram.png) + + + +The following diagram sequence summarises the sequence when the DeletePresetCommand is executed. + +![DeletePresetSequenceDiagram](images/DeletePresetCommandSequenceDiagram.png) + +Given below is an example usage scenario and how the DeletePresetCommand behaves at each step. + +Step 1: The user has selected a vendor with `vendor i`. + +Step 2: The user enters the command `delete preset sample` to delete the preset named 'sample' stored in the user's presets json file. + +Step 3: `Storage#readPresetManager()` is executed to retrieve the list of all presets, from the presets json file. + +Step 4: The preset with the name `sample` for vendor `i` exists. + +Step 5: `sample` is removed from the vendor index `i` position of `allLists`. + +Step 6: `Storage#savePresetManager()` is executed to save the modified `allLists` into the `presets` json file. + + + +Given below is the activity diagram for SavePresetCommand. + +![SavePresetCommandActivityDiagram](images/SavePresetCommandActivityDiagram.png) + + + +### Undo feature + +Changes made to the Order can be undone by using the `undo` command. + +The OrderManager starts out with a Stack of Order `orderHistory` and a main Order `order`, which +represents the past versions of the order, and the most current order respectively. Any changes (ie. `add`, `remove`, +`clear` etc.) should be done to `order`. After the changes are done, the method `OrderManager#saveChanges()` should be +called, which saves a copy (see `Order#makeCopy()`) of `order` to the `orderHistory`. If the user requests an undo, +the head of `orderHistory` will be popped, and `order` is now a copy of the head of the popped stack. + +`OrderManager#saveChanges()` works based on the assumption that the head of `orderHistory` is always equal to `order`. +If not and a change is made to the order followed by calling `OrderManager#saveChanges()`, then the `order` right before +the change is not saved. Therefore, the method `orderHistory#clear()` should not be called unless the developer +understands the effect of doing so. This also means if a method changes the order and it should be able to be undone, +`saveChanges()` must be called at the end of the method. + +### Friendly Syntax + +The friendly syntax allows users to type in just the prefix of a command to execute it. + +The following diagram summarizes the sequence when the RemoveCommand is executed from the user input of `r 1`. + +![Friendly_Syntax_Architecture_Diagram](images/friendly_syntax_architecture_diagram.png) + +Given below is an example usage scenario and how the friendly syntax behaves at the parse stage. + +Step 1: The user launches the application for the first time and enters the vendor command `vendor 1`. + +Step 2: SupperStrikers loads the menu of the 1st vendor into the GUI. + +Step 3: The user enters the add command `add 1 3` + +Step 4: SupperStrikers adds 3 of the 1st item into the order. + +Step 5: The user enters the command `r 1 1`. + +Step 6: The `LogicManager#execute()` is executed to call the `SupperStrikersParser#parseCommand()` method. + +Step 7: `SupperStrikersParser#parseCommand()` checks if the inputted command word is a prefix of one and only one of the valid commands by filtering the list of valid commands based on whether they start with the user inputted prefix. The `r` in this case maps to the `remove` keyword. + +Step 8: The `RemoveCommand` is executed and one quantity of the first item in the order is removed. + +- If there is no command with the given prefix, an `ParseException` will be thrown. +- If the inputted prefix exists for more than 1 command, a `ParseException` will be thrown. +- If there is a command which is a prefix for another command, it can no longer be executed. + +### Vendor Commands + +- There are two VendorCommand classes in SupperStrikers. +- `SwitchVendorCommand` allows the user to select a vendor from the `VendorManager` to order from. +- `VendorCommand`, deselects the vendor to the default unintialized value. + +- If the vendor does not exist, a `CommandException will be thrown`. +- If the vendor selected is different from the current vendor, the model will clear the current order. + + +#### Switch Vendor Command +The following activity diagram summarises the process when the SwitchVendorCommand is executed. +SwitchVendorCommandActivityDiagram + + + + + +Given below is an example usage scenario and how the SwitchVendorCommand behaves at each step. + +Step 1: The user launches the application for the first time, by default, no vendor is selected. + +Step 2: The user enters the vendor command `vendor i`. + +Step 3: `Model#getObservableVendorList()` is executed to retrieve the list of vendors. + +Step 4: SwitchVendorCommand checks whether ith index is valid. + +Step 5: If the ith index is valid, `Model#selectVendor(i)` is executed to select the vendor. + +Step 6: Supper Strikers loads the menu of the ith vendor into the UI by calling `MainWindow#handleVendor()`. + +Step 7: The UI component showing the vendor list is hidden and the UI showing the menu is displayed to the user by calling +`MainWindow#displayMenu()`. + +Step 8: `Model#resetOrder()` creates a new empty order for the ith vendor. + + + +The following diagram summarises the sequence when the SwitchVendorCommmand is executed. + +VendorSequenceDiagram + +#### Vendor Command + +Given below is an example usage scenario and how VendorCommand behaves at each step. + +Step 1: The user has selected a vendor with index `i`. + +Step 2: The user enters the vendor command `vendor`. + +Step 3: `Model#selectVendor(-1)` is executed to set the vendor to the default uninitialized value. + +Step 4: The UI component showing the menu is hidden and the UI component showing the vendor list is displayed to the +user by calling `MainWindow#displayMenu()`. + +Step 5: `Model#resetOrder()` sets the order to a new empty order. -------------------------------------------------------------------------------------------------------------------- @@ -236,13 +583,14 @@ _{Explain here how the data archiving feature will be implemented}_ **Target user profile**: -* has a need to manage a significant number of contacts +* has a need to order supper frequently +* stays on campus * prefer desktop apps over other types * can type fast * prefers typing to mouse interactions * is reasonably comfortable using CLI apps -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +**Value proposition**: manage ordering supper faster than a typical mouse/GUI driven app ### User stories @@ -252,55 +600,470 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli | Priority | As a …​ | I want to …​ | So that I can…​ | | -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- | | `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App | -| `* * *` | user | add a new person | | -| `* * *` | user | delete a person | remove entries that I no longer need | -| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list | -| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident | -| `*` | user with many persons in the address book | sort persons by name | locate a person easily | +| `* * *` | NUS resident | add a food item to my supper order | | +| `* * *` | NUS resident | remove a food item from my supper order | | +| `* * *` | NUS resident | see the menu | view all the items currently ordered by me | +| `* * *` | NUS resident | see the vendor list and select vendor | confirm which vendor to order from | +| `* * ` | NUS resident | see the total price of my current order | decide whether I want to order more | +| `*` | NUS resident | confirm order | finalize my supper selection | +| `* * *` | NUS resident | undo my commands | fix any mistakes made while ordering | +| `* * *` | NUS resident | submit my order | | +| `* *` | NUS resident | clear the current order | start a new order | +| `* *` | NUS resident | filter the menu | find the food item that I want to order easily | +| `* *` | NUS resident | save my current order as a preset | load up the preset for fast supper ordering | +| `* *` | NUS resident | tag an item in my supper order | mention my preferences for that item | +| `* *` | NUS resident | remove tags from an item in my supper order| fix any mistakes made while tagging that item | -*{More to be added}* ### Use cases -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +(For all use cases below, the **System** is the `SupperStrikers` and the **Actor** is the `user`, unless specified otherwise) + +**Use case: Display user instructions** + +**MSS** + +1. User requests to see the user instructions. +2. SupperStrikers displays the link to the user guide. + + Use case ends. + +**Use case: Exit the program** + +**MSS** + +1. User requests to exit the program. +2. SupperStrikers terminates and closes. + + Use case ends. + +**Use case: Showing and selecting a particular vendor** + +**MSS** + +1. SupperStrikers displays the list of vendors. +2. User requests to choose a specified vendor. +3. SupperStrikers displays the menu of the selected vendor. +4. SupperStrikers creates a new empty order of the selected vendor. + + Use case ends. + +**Extensions** + +- 2a. The given index is invalid. + + - 2a1. SupperStrikers displays an error message. + + Use case resumes at step 2. + +- 2b. The user has already selected a different vendor. + + - 2b1. SupperStrikers clears the order of the current vendor. + + Use case resumes at step 3. + + +**Use case: Showing list of all vendors** + +Precondition: User has already selected a particular vendor + +**MSS** + +1. SupperStrikers displays a specific vendor. +2. User requests to view all vendors. +3. SupperStrikers displays the details of all vendors. +4. SupperStrikers resets the current order to a new empty order. + + Use case ends. + +**Use case: Display current menu** + +Precondition: User has already selected a particular vendor + +**MSS** + +1. User requests to see the current default menu for his selected vendor. +2. SupperStrikers displays the default menu to the user. + + Use case ends. + +**Use case: Find a keyword** + +Precondition: User has already selected a particular vendor + +**MSS** + +1. User requests to search for specified keyword(s) in the menu. +2. SupperStrikers filter out the current menu using the specified keyword(s). +3. SupperStrikers displays the matching menu items that contains the keyword(s) to the user. + + Use case ends. + +**Use case: Filter menu item by price** + +Precondition: User has already selected a particular vendor + +**MSS** + +1. User requests to see menu items that satisfy a specified price range. +2. SupperStrikers filter out the current menu using the specified price range. +3. SupperStrikers displays the matching menu items that satisfy the price range to the user. + + Use case ends. + +**Use case: Sort menu items** + +Precondition: User has already selected a particular vendor + +**MSS** + +1. User requests to sort the menu items by price or name. +2. SupperStrikers sorts the current menu. + + Use case ends. + +**Use case: Viewing total** -**Use case: Delete a person** +Precondition: User has already selected a particular vendor **MSS** -1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person +1. User requests to see the total price of the current order. +2. SupperStrikers displays the total price of the current order to the user. + + Use case ends. +**Extensions** + +- 1a. The order list is empty. + + - 1a1. SupperStrikers displays an error message. + Use case ends. + +**Use case: Clearing current order** + +Precondition: User has already selected a particular vendor + +**MSS** + +1. User requests to clear the current order. +2. SupperStrikers clears the current order. +3. SupperStrikers creates a new empty order of the selected order. + + Use case ends. + **Extensions** -* 2a. The list is empty. +- 1a. The order list is empty. + + - 1a1. SupperStrikers displays an error message. + + Use case ends. + + +**Use case: Set a profile** + +**MSS** + +1. User requests to set his profile which includes his phone number and address. +2. SupperStrikers creates a new profile that has the user's inputted details. +3. SupperStrikers saves the created profile into storage. Use case ends. -* 3a. The given index is invalid. +**Extensions** + +- 1a. The user's details are invalid. - * 3a1. AddressBook shows an error message. + - 1a1. SupperStrikers displays an error message. + + Use case resumes at step 1. - Use case resumes at step 2. +- 3a. The user has an existing profile. + + - 3a1. SupperStrikers overwrite the existing profile with the newly created profile. + + Use case ends. + +**Use case: Submit order** + +Precondition: User has already selected a particular vendor and User has created a profile + +**MSS** + +1. User requests to submit the current order. +2. SupperStrikers displays a copy of the order in a submittable format to the user and at the same time copies that text + to the user's clipboard. + + Use case ends. + +**Extensions** + +- 1a. The order list is empty. + + - 1a1. SupperStrikers displays an error message. + + Use case ends. + +**Use case: Add an item** + +Precondition: User has already selected a particular vendor + +**MSS** + +1. User requests to add a specified quantity of an item listed in the vendor menu. +2. SupperStrikers adds the item along with the quantity specified into the current order. +3. SupperStrikers displays the updated order with the newly added item. + + Use case ends. + +**Extensions** + +- 1a. The given index is invalid. + + - 1a1. SupperStrikers shows an error message. + + Use case resumes at step 1. + +- 1b. The given quantity is invalid. + + - 1b1. SupperStrikers shows an error message. + + Use case resumes at step 1. + +- 1c. The quantity is not specified. + + - 1c1. SupperStrikers sets the quantity of the item to be added as 1. + + Use case resumes at step 2. + +- 2a. The resultant quantity of the item exceeds 100. + + - 2a1. SupperStrikers shows an error message. + + Use case resumes at step 1. + +**Use case: Remove an item** + +Precondition: User has already selected a particular vendor + +**MSS** + +1. User requests to remove a quantity of a specific item in the current order. +2. SupperStrikers decreases the quantity of the item by the quantity provided. + + Use case ends. + +**Extensions** + +- 1a. The given index is invalid. + + - 1a1. SupperStrikers shows an error message. + + Use case resumes at step 1. + +- 1b. The given quantity is invalid. + + - 1b1. SupperStrikers shows an error message. + + Use case resumes at step 1. + +- 1c. The given quantity is larger than the quantity to the order item. + + - 1c1. SupperStrikers shows an error message. + + Use case resumes at step 1. + +- 1d. The quantity is not specified. + + - 1d1. SupperStrikers removes the order item at the specified index along with all of its quantity. + + Use case ends. + + +**Use case: Add a tag to an item** + +Precondition: User has already selected a particular vendor + +**MSS** + +1. User requests to tag a specific item in the current order. +2. SupperStrikers adds tag to the item. + + Use case ends. + +**Extensions** + +- 1a. The given index is invalid. + + - 1a1. SupperStrikers shows an error message. + + Use case resumes at step 1. + +- 1b. The tag is not specified. + + - 1b1. SupperStrikers shows an error message. + + Use case resumes at step 1. + +- 1c. The tag already exists at the specified order item. + + - 1c1. SupperStrikers shows an error message. + + Use case resumes at step 1. + +**Use case: Remove tags from an item** + +Precondition: User has already selected a particular vendor + +**MSS** + +1. User requests to remove all tags from a specific item in the current order. +2. SupperStrikers removes all tags from the item. + + Use case ends. + +**Extensions** + +- 1a. The given index is invalid. + + - 1a1. SupperStrikers shows an error message. + + Use case resumes at step 1. + + +**Use case: Load a Preset** + +Precondition: User has already selected a particular vendor + +**MSS** + +1. User requests to load preset of a specific name. +2. SupperStrikers loads the saved presets from storage. +3. SupperStrikers finds the preset with the same name under the current vendor. +4. SupperStrikers loads the preset into the current order. + + Use case ends. + +**Extensions** + +- 1a. The name given is invalid. + + - 1a1. SupperStrikers shows an error message. + + Use case resumes at step 1. + +- 1b. The name is not provided. + + - 1b1. SupperStrikers instead displays the name of all saved presets for the current vendor. + + Use case ends. + +- 3a. There is no preset with the same name. + + - 3a1. SupperStrikers shows an error message. + + Use case resumes at step 1. + +- 4a. The current order already contains order items. + + - 4a1. The current order is replaced by the preset. + + Use case ends. + +**Use case: Save a Preset** + +Precondition: User has already selected a particular vendor + +**MSS** + +1. User requests to save preset with a specific name. +2. SupperStrikers loads the saved presets from storage. +3. SupperStrikers creates a new preset with the current order items. +4. SupperStrikers adds the new preset into the current list of saved presets for the current vendor. +5. SupperStrikers saves the modified presets into storage. + + Use case ends. + +**Extensions** + +- 1a. The name given is invalid. + + - 1a1. SupperStrikers shows an error message. + + Use case resumes at step 1. + +- 1b. The name is not provided. + + - 1b1. SupperStrikers gives the preset a default name of 'MyPreset'. + + Use case resumes at step 2. + +- 4a. The preset that is trying to be saved has an identical name to an existing saved preset. + + - 4a1. The preset that is trying to be saved replaces the old preset with the same name. + + Use case resumes at step 5. + +**Use case: Delete a Preset** + +Precondition: User has already selected a particular vendor + +**MSS** + +1. User requests to delete a preset with a specific name. +2. SupperStrikers loads the saved presets from storage. +3. SupperStrikers removes the preset with the specified name from the list. +4. SupperStrikers saves the modified presets into storage. + + Use case ends. + +**Extensions** + +- 1a. The name given is invalid or is not provided. + + - 1a1. SupperStrikers shows an error message. + + Use case resumes at step 1. + +- 2a. The specified name is not found in the list of saved presets. + + - 2a1. SupperStrikers shows an error message. + + Use case resumes at step 1. + +**Use case: Undo changes to an order** + +Precondition: User has already selected a particular vendor + +**MSS** + +1. User requests to undo a change from his current order. +2. SupperStrikers reverts his order back to an older history of his order. + + Use case ends. + +**Extensions** + +- 1a. The user is at the oldest history of his order. + + - 1a1. SupperStrikers shows an error message. + + Use case ends. -*{More to be added}* ### Non-Functional Requirements 1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. -2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. +2. Should be able to hold up to 1000 items without a noticeable sluggishness in performance for typical usage. 3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. -*{More to be added}* ### Glossary * **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others -------------------------------------------------------------------------------------------------------------------- @@ -310,7 +1073,6 @@ Given below are instructions to test the app manually.
:information_source: **Note:** These instructions only provide a starting point for testers to work on; testers are expected to do more *exploratory* testing. -
### Launch and shutdown @@ -319,38 +1081,85 @@ testers are expected to do more *exploratory* testing. 1. Download the jar file and copy into an empty folder - 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. + 2. Open a command window. Run the java -version command to ensure you are using Java 11. If not, please install Java 11 to ensure you are able to safely launch the jar file. + + 3. Launch the jar file using the java -jar supperstrikers.jar command (do not use double-clicking).
+ Expected: Shows the GUI with a set of sample vendors. The window size may not be optimum. 1. Saving window preferences 1. Resize the window to an optimum size. Move the window to a different location. Close the window. - 1. Re-launch the app by double-clicking the jar file.
+ 2. Re-launch the app by using the java -jar supperstrikers.jar command.
Expected: The most recent window size and location is retained. -1. _{ more test cases …​ }_ -### Deleting a person +### Selecting a vendor + +1. Selecting a vendor while all the vendors are being shown -1. Deleting a person while all persons are being shown + 1. Prerequisites: List all vendors using the `vendor` command. - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. + 2. Test case: `vendor 1`
+ Expected: First vendor is selected. The menu from the selected vendor is displayed. - 1. Test case: `delete 1`
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. + 3. Test case: `vendor`
+ Expected: No vendor is selected. The list of vendors is displayed again. - 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. + 4. Test case: `vendor 0`
+ Expected: No vendor is selected. Error details shown in status message. Status bar remains the same. - 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
+ 5. Other incorrect delete commands to try: `vendor x`, `...` (where x is larger than the list size)
Expected: Similar to previous. -1. _{ more test cases …​ }_ + +### Adding an item + +1. Adding an item while an order is currently active + + 1. Prerequisites: Has a vendor selected using the `vendor` command. + + 2. Test case: `add 1 1`
+ Expected: 1 order of the first item from the menu is added into the order. Details of the added order item shown in the status message. + + 3. Test case: `add 1 0`
+ Expected: No item is added. Error details shown in the status message. + + 4. Test case: `add 0 1`
+ Expected: No item is added. Error details shown in the status message. + + 5. Other incorrect add commands to try: `add`, `add -1 -1`, `add x y`, `...` (where x is larger than the menu size)
+ Expected: Similar to previous. + + +### Removing an item + +1. Removing an item while an order is currently active + + 1. Prerequisites: Has a vendor selected using the `vendor` command. There is at least one item in the order. + + 2. Test case: `remove 1`
+ Expected: All the quantity of the first item is removed from the order. Details of the removed order shown in the status message. + + 3. Test case: `remove 1 1`
+ Expected: 1 order of the first item from the user's order is removed from the order. Details of the removed order shown in the status message. + + 4. Test case: `remove 0`
+ Expected: No item is deleted. Error details shown in the status message. + + 5. Test case: `remove 1 0`
+ Expected: No item is deleted. Error details shown in the status message. + + 6. Other incorrect delete commands to try: `remove`, `remove -1 -1`,`remove x y`, `...` (where x is larger than the list size or y is larger than the user's order quantity amount)
+ Expected: Similar to previous. ### Saving data 1. Dealing with missing/corrupted data files - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ + 1. Delete the data folder, preferences and config json file. + + 2. Make sure you have the latest copy of the SupperStrikers.jar which can be obtained [here.](https://github.com/AY2021S1-CS2103-T16-1/tp/releases) + + 3. Rerun the SupperStrikers.jar in an empty folder. -1. _{ more test cases …​ }_ diff --git a/docs/UserGuide.md b/docs/UserGuide.md index b91c3bab04d..4dd91f0cce1 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -2,38 +2,33 @@ layout: page title: User Guide --- - -AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps. +**Supper Strikers is a desktop application for managing your supper orders.** It is targeted at students living in NUS for ordering delivery from supper stretch. While it has a GUI (Graphical User Interface), most of the user interactions happen using a CLI (Command Line Interface). * Table of Contents {:toc} -------------------------------------------------------------------------------------------------------------------- -## Quick start - -1. Ensure you have Java `11` or above installed in your Computer. +1. Download the latest `supperstrikers.jar` from [here](https://github.com/AY2021S1-CS2103-T16-1/tp/releases). -1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +1. Copy the jar to the folder you want to use as the _home folder_ for your SupperStrikers. -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +1. Open a command window. Run the java -version command to ensure you are using Java 11. If not, please install Java 11 to ensure you are able to safely launch the jar file. -1. Double-click the file to start the app. The GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
+1. Launch the jar file using the java -jar supperstrikers.jar command (do not use double-clicking). The GUI similar to the one below should appear in a few seconds.
![Ui](images/Ui.png) 1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
Some example commands you can try: - * **`list`** : Lists all contacts. - - * **`add`**`n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book. - - * **`delete`**`3` : Deletes the 3rd contact shown in the current list. - - * **`clear`** : Deletes all contacts. - + * **`vendor`** : List all the vendors available. + * **`vendor 1`** : Select the first vendor to make a supper order for. + * **`add 1 2`** : Adds two quantity of the item at the 1st index from the vendor's menu to your supper order. + * **`remove 1 1`** : Removes 1 quantity of the item at the 1st index from your order. + * **`preset save My First Preset`** : Saves your current order items locally as a preset with the name "My First Preset", which you can call it back easily by: + * **`preset load My First Preset`** : Loads the preset with the name "My First Preset" to your supper strikers app! * **`exit`** : Exits the app. - + 1. Refer to the [Features](#features) below for details of each command. -------------------------------------------------------------------------------------------------------------------- @@ -41,138 +36,332 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo ## Features
- -**:information_source: Notes about the command format:**
+**Notes about the command format:**
* Words in `UPPER_CASE` are the parameters to be supplied by the user.
- e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`. - + E.g. in `find KEYWORD`, `KEYWORD` is a parameter which can be used as `find spicy`. + * Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. - -* Items with `…`​ after them can be used multiple times including zero times.
- e.g. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc. + E.g `add INDEX [QUANTITY]` can be used as `add 3 2` or as `add 3`. + +* **Friendly syntax** is supported! For any command, typing the prefix of the command will already be recognized, unless there is any ambiguity. -* Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. + * E.g. `help` only requires the user to type `h` to be recognized, while `sort` will require user to at least type `so` since typing `s` by itself will conflict with another command `submit` (The commands + will be explained below. To minimize confusion, the whole command will be shown instead of the prefix.) + +* Extra whitespaces in between arguments will be ignored.
-### Viewing help : `help` +The application is divided into two modes, vendor mode and menu mode. Vendor mode is when a vendor is not yet selected, +as seen by the section on the left displaying a list of vendors. In vendor mode, only vendor +related commands can be executed. In menu mode, vendor, menu and order commands can be executed. -Shows a message explaning how to access the help page. +## General -![help message](images/helpMessage.png) +### Getting Started: `help` + +Shows the user instructions on how to use the application. Format: `help` +- Note that anything written after the `help` command will be ignored. -### Adding a person: `add` +### Exit application: `exit` -Adds a person to the address book. +See you next time! -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +Note that any arguments written after the `exit` command will be ignored. -
:bulb: **Tip:** -A person can have any number of tags (including 0) -
+- `exit` and `exit 1` is treated as the same command +- This also applies to all other commands with no arguments + +Format: `exit` + +- This command closes the jar file. + +## Vendor related commands + +### View/select vendor: `vendor` + +Displays the list of all vendors or selects the specified vendor. + +Format: `vendor [INDEX]` + +* If no `INDEX` is specified, it displays the list of all vendors and returns to vendor mode. +* If an `INDEX` is specified, the corresponding vendor is selected, and its menu will be shown. +* If there is an existing supper order, it will be deleted. +* `INDEX` must be a positive integer and must not exceed the size of the vendor list. Examples: -* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` -* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal` +* `vendor`: Changes back to vendor mode. +* `vendor 2`: Selects the 2nd vendor in the list. + +## Menu related commands + +### Displaying supper menu: `menu` + +Displays the default menu from the selected vendor. + +- Can be used to display the menu after a `sort` / `find` / `price` command. +- Note that any arguments written after the `menu` command will be ignored, and menu will be listed as intended. + +Format: `menu` -### Listing all persons : `list` +Example: -Shows a list of all persons in the address book. +- `menu`: Displays the full menu of the selected vendor. -Format: `list` -### Editing a person : `edit` +### Sorting the menu: `sort` -Edits an existing person in the address book. +Sorts the menu by either price or name. -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +Format: `sort MODE [DIRECTION]` -* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ -* At least one of the optional fields must be provided. -* Existing values will be updated to the input values. -* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative. -* You can remove all the person’s tags by typing `t/` without - specifying any tags after it. +* `MODE` dictates which mode it will sort by, with format: + * `n`: sorts by name + * `p`: sorts by price + +* `DIRECTION` dictates which direction it will sort by, with format: + * `a`: sort in ascending order + * `d`: sort in descending order + * `t`: toggles the direction, if previous sort was ascending, new direction is descending. If no direction was previously specified, sort in ascending order. +* If `DIRECTION` is not specified, it will be treated as a toggle, and ascending direction will be sorted as descending order and vice versa. Examples: -* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively. -* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags. +* `sort n a`: sorts the menu by name in ascending direction. +* `sort p`: sorts the menu by price in opposite direction as last sorted. -### Locating persons by name: `find` -Finds persons whose names contain any of the given keywords. +### Find menu item: `find` -Format: `find KEYWORD [MORE_KEYWORDS]` +Finds and lists all menu items containing any of the specified keywords in their name. -* The search is case-insensitive. e.g `hans` will match `Hans` -* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans` -* Only the name is searched. -* Only full words will be matched e.g. `Han` will not match `Hans` -* Persons matching at least one keyword will be returned (i.e. `OR` search). - e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` +Format: `find KEYWORD [MORE_KEYWORDS]...` + +- `KEYWORD` are NOT **case-sensitive**. +- Tags will also be detected. +- Note that keywords with spaces will be are treated as separate keywords (see example below). This is to avoid user mistake. + +Examples: +* `find milo`: lists all menu items containing the word 'milo' in their name. +* `find milo dinosaur`: lists all menu items containing the word 'milo' or 'dinosaur' in their name. + + +### Filter menu item by price: `price` + +Filters all menu item within a specified price range. + +Format: `price INEQUALITY PRICE` + +* `INEQUALITY` is an inequality sign, of the below formats: + * `<`: Strictly less than + * `<=`: Less than or Equal to + * `>`: Strictly greater than + * `>=`: Greater than or Equal to +* `PRICE` must be a non-negative real number. Only up to 2 decimal places will be considered, anything from third decimal places onward will be ignored. + +Examples: +* `price < 3`: lists all menu items with price less than $3. +* `price <= 2`: lists all menu items with price less than or equal to $2. +* `price > 4.123`: lists all menu items with price greater than $4.12 + + +## Order related commands + +### Adding an order item: `add` + +Adds an order item for the user according to the index from the menu to the user's supper order. + +Format: `add INDEX [QUANTITY]` + +* The `INDEX` refers to the index number shown on the displayed menu list. +* `INDEX` must be a positive integer and must not exceed the size of the menu list. +* `QUANTITY` can be specified to indicate the number of item to be added. Otherwise, it adds one quantity of the item at the specified index. + +Examples: +* `add 1 1`: add item at INDEX 1, of QUANTITY 1. +* `add 2 3`: add item at INDEX 2, of QUANTITY 3. +* `add 1`: add item at INDEX 1, of default QUANTITY 1. + + +### Removing an order item : `remove` + +Removes the specified item from the supper order. + +Format: `remove INDEX [QUANTITY]` + +* `INDEX` refers to the index number shown in the displayed supper order list. +* `INDEX` must be a positive integer and must not exceed the size of the supper order list. +* `QUANTITY` can be specified to indicate the number of item to be deleted (has to be less than or equal to the current quantity). +* If `QUANTITY` is not specified, it removes all items of the specified index. Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +* `remove 2`: remove all items at INDEX 2. +* `remove 1 2`: remove item at INDEX 1, of quantity 2. + -### Deleting a person : `delete` +### Tag an order item: `tag` -Deletes the specified person from the address book. +Tags an order item with remark. -Format: `delete INDEX` +Format: `tag INDEX REMARK` -* Deletes the person at the specified `INDEX`. -* The index refers to the index number shown in the displayed person list. -* The index **must be a positive integer** 1, 2, 3, …​ +- The `INDEX` refers to the index number of the order item. +- `INDEX` must be positive number and must not exceed the size of the order list. +- `REMARK` is any non-empty string. +- If the `REMARK` being added to the order item already exists, it would not be added. +- Unlimited tags can be added, and is left up to the user's discretion. Examples: -* `list` followed by `delete 2` deletes the 2nd person in the address book. -* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command. +* `tag 5 2 no egg`: tags the order item at INDEX 5 with the REMARK '2 no egg'. -### Clearing all entries : `clear` -Clears all entries from the address book. +### Untag an order item: `untag` + +Clears all tags of the specified order item. + +Format: `untag INDEX` + +- The `INDEX` refers to the index number of the order item. +- `INDEX` must be positive number and must not exceed the size of the order list. + +Examples: + +- `untag 1`: clears all tags for the order item at INDEX 1. + + +### Clearing the order: `clear` + +Removes everything from the order. Format: `clear` -### Exiting the program : `exit` +Example: -Exits the program. +- `clear`: clears all items on current order. + + +### Undo changes to order: `undo` + +Undoes last change to the order. Note that it does not affect commands unrelated to order. + +Format: `undo` + +- Note that any arguments written after the `undo` command will be ignored. +- Note that an error message will be returned to the user if there are no changes left to undo. + +Example: + +- `undo`: undoes previous command and returns order back to its previous state. + + +### Calculate total: `total` + +Displays the total cost of the order currently. + +Format: `total` + +- Note that any arguments written after the `total` command will be ignored. +- Note that an error message will be returned to the user if the order is empty. + +Example: + +- `total`: returns the total cost of all items from current order. -Format: `exit` -### Saving the data -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +### Profile Details: `profile` + +Add delivery address and phone number for submission. + +Format: `profile PHONE ADDRESS` + +- `PHONE` represents your contact number and must be a valid phone number (First digit must start with a 6/8/9 and must be exactly 8 digits long). + +- `ADDRESS` represents your delivery address. + +Examples: + +- `profile 92030888 25 Lower Kent Ridge Rd, Singapore 119081`: Saves your PHONE number as '92030888' and your ADDRESS as '25 Lower Kent Ridge Rd, Singapore 119081'. + + +### Generate order text: `submit` + +Displays a copy-paste-able text of profile with order. A profile must be set up before using `submit`. + +Format: `submit` + +- Text obtained will be copied to clipboard if possible. +- Note that an error message will be returned to the user if the order is empty. +- Note that any arguments written after the `submit` command will be ignored. + +Example: + +- `submit`: returns a text form of the order + + +### Preset supper orders: `preset` + +Saves or Loads a preset of the user's supper order. + +Format: `preset MODE [NAME]` + +* `MODE` dictates what the system will perform for the user's supper orders, represented by the formats: + * `save`: Used to save a preset. (If used without a `NAME`, will save with a default preset name of 'MyPreset') + * `load`: Used to load a preset. (If used without a `NAME`, will list all saved presets for the current vendor) + * `delete`: Used to delete a preset. (`NAME` must be specified) +* `NAME` is the preset name which the system will save the preset as, or tries to load the given preset by the given name. + * if `NAME` already exists and preset is in save mode, the new preset will overwrite the existing preset. + * `NAME` is **case-sensitive** and supports space characters. + * if `NAME` does not exist and preset is in delete mode, an error message will be returned to the user. +* Presets are split by vendors, therefore running `preset save PresetName` for two different vendors will not affect one +another. Similar for `preset load`. + +Examples: +* `preset save`: saves the user's supper order with the default preset name. +* `preset load MyPreset`: loads the current default preset if it exists. +* `preset save vegan`: save the user's supper order with a preset NAME of 'vegan'. +* `preset load vegan`: loads the preset supper order with the preset NAME 'vegan'. +* `preset delete vegan`: deletes the preset supper order with the preset NAME "vegan". -### Archiving data files `[coming in v2.0]` -_{explain the feature here}_ -------------------------------------------------------------------------------------------------------------------- ## FAQ -**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder. +**Q**: How do I transfer my data to another Computer? +**A**: Overwrite your current data file with your old data file. + +**Q**: Why are some pictures missing on the menu? +**A**: These are intended as an example of what is shown if vendors lack a picture for a menu item. + +**Q**: Why am I not allowed to add vendors or menu items? +**A**: We realised that allowing users to add vendors or menu items from the command line is unfeasible, as each vendor will have many menu items, and it would be extremely time-consuming for the user. However, the user is able to manually add vendors or menu items in the json file, but the user must ensure that all details are included correctly. + -------------------------------------------------------------------------------------------------------------------- ## Command summary -Action | Format, Examples +Action | Format --------|------------------ -**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague` +**Help** | `help` +**Exit** | `exit` +**Vendor** | `vendor [INDEX]` +**Menu** | `menu` +**Sort** | `sort MODE [DIRECTION]` +**Find** | `find KEYWORD [MORE_KEYWORDS]...` +**Price** | `price INEQUALITY PRICE` +**Add** | `add INDEX [QUANTITY]` +**Remove** | `remove INDEX [QUANTITY]` +**Tag** | `tag INDEX REMARK` +**Untag** | `untag INDEX` **Clear** | `clear` -**Delete** | `delete INDEX`
e.g., `delete 3` -**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…​`
e.g.,`edit 2 n/James Lee e/jameslee@example.com` -**Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` -**List** | `list` -**Help** | `help` +**Undo** | `undo` +**Total** | `total` +**Profile** | `profile PHONE ADDRESS` +**Submit** | `submit` +**Preset** | `preset MODE [NAME]` diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..7359dcae976 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "Supper Strikers" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2021S1-CS2103-T16-1/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/diagrams/AddCommand.xml b/docs/diagrams/AddCommand.xml new file mode 100644 index 00000000000..1d5dae891e0 --- /dev/null +++ b/docs/diagrams/AddCommand.xml @@ -0,0 +1,2 @@ + +7VzbcqM4EP0aV+0+hBIS4vJoJ5nd2ZqpmdpUzV5ethSj2FQw8mI8cebrVxhhQDLhYm6eWT+koIFG6T5Sn+6WPUO3m8MvIdmuPzKX+jMI3MMM3c0gtADkf2PBayLQTWQlklXouUKWCR68b1QIgZDuPZfuCjdGjPmRty0KlywI6DIqyEgYspfibU/ML751S1ZUETwsia9K//DcaJ1IbWhl8l+pt1qnb9ZNJ7myIenN4j/ZrYnLXnIidD9DtyFjUXK0OdxSPzZeapfkuXclV08DC2kQ1Xlg/uJ+ef+w2M1D+Pfdb58efv/n+euN0PKV+HvxD4vBRq+pBdbRxudH+gwttswLoqNB8WKG72IJDb0NjWjIhSyM1mzFAuJ/zqQL8QIaRvRQOnL9ZA8OJMr4s+ErvyV9AAkTChDd2KngJXNJCph1zhsQCyERKFiddGeG4gfCVg3sBlW7ofkHtvKWH0nAMRG+ZcYOTGLLFrHOWASeM0lfFjGqkUQDdx5PSX4WsIALFy7ZrakrzJKzED140Z/8GGgYmOL8r/hcHN8d8ievuZMc9I6yZBDUVWa5ZGk+ULYPl7RqrqgeyVkcnzF4KgupTyLva3EY57wg3vA5nmv5OYAlj0OnqCMZv3gsvw6omjRu1KIyeaJEJFzRSFHG3Udec7eJBUHGzsk67eGEO4VTwAcj8JSexnDSNQCMVJBh6nj2mj9rj6rEkFeDKowkINRHlVHUZBkSPksg1RVgrEaAefTZ8vnoXfed5/tnIMN9+yAezQLbfSZdEN9bBfyqT5+4Uxbxcu5x3jAX4ohtGwCldKFPI/SFji6NoM39XKWpZz/bip/pgS73Ef1pBs1/9zGbWhDX5bfEt6FM+LOKB+6WD+SRM1a0COnO+0Yej5dAEQoVjn5kUcQ2J18LIipUzU4BtyLcl2O6N2ikt7Cnpx3txVdO9Zwcn11CE0mI1sVSVsEujb6oVPqyIrucuy4H6W7B2PNnEu56p5h8qkt2GZ1j6jXSlSsnmc6YdACatuTytiSTa9IAdrKPzF4NR3Pyn1oxZDj6qZ9J8Drhn4Zt5hko0Bz7dMdIDHRakGvNQKFpFTUNzUDT9XJiFPQEvhugAWgU0QeANU30dUt/dUNmrWmMb46yKk2N166yF5XQkgy1icpuMayWcbYx2bhlmw0J3O+QYyeztjsAXkZwapQ9xmfMyJHnAKhZj+2NGJqK3UhCmQVw37JiFxYxZH6jn7PIGZOkI+/eJGqi/N0ymGTajEVhkExuLRnndYMLcqQimumgYSlMjYx9DAojMjVdAyjN3BLsoQnmat1SF2hJPMByDA22ZC/qOpU2aCeTecEaOX4OYaVMIQdTtqWBvLbxy6LZbA/CZy9eY6SQa9lt0yRLLtTLmnpeY1J/XquDS5jAxXmwPM2tlnNcN8wKTX07WM2DiRpFJpQXVNRBgTz1ZHt2t6ugRhN9gvTfqrkdQ5dLO90Zrq928QTJbgqSSnpilLSPRmLFWO6XtGfF+rCsGKrZ5VvoGqWwBwobG46SsXc2JEDtv7Snl6xGjXEmt7YVTR2V9kqH3GtpD6o7JNLO+RWW7ZI52T+4pBZCf71yWKNeM37sx7o8R2C92N9bsxyeKVmgudiIXG6/Dmxh2HIyjVVbmEOWQdP16jvujyfTZCxSg3WlftKyQc41acAEp48UEWw47f44arYT40fodfYdiwwlD21bZsYgBpekTHc0C16IsjKoAw3K1Q04AONBaqWJ/z8cgNy51H3HmPvB20XXyH5Qx4i7LOrU2PcwWkGv80IdBtI2E7NtJVaZ0IqmnvNadBXdZiyXm0yjHuXsrdqE1HoA45zzU+jS8H1EN28ZsQuDYImEmKZqEHtI3mk0452Dtw0LLUNTx40JbFYRLFIKXEEo6q5SdYivKIJVll3QIJmxYceEKveRmg4mBpoOFX7bmKpgXTOdc6ovpCpdr6XGGEHwBEu9CEsHoonW/i4Nvkos0NsGX/kblIqmnoOv0azX0zdgxt7B0wtclE6VCVrCRfnCraKpb7g042pXTrKV/bpYCjewnRvlCn+F2r59eo5HTjkXraqHVi2p3RHOGt/23K3JNj7cb/w7uotCxoe1eFl7EX3YkiPVegnJtmjF+Lbn0w89oI765bJdcFo4zvF1NGQCY/xAe0On1wXvZCFzDM0uLx+bAGqGmVWP7WHXtWa7SKcLr8m0GVrvncA6x4lUex26zISbVQf+h4Oa9RiaY8mIcDSz5eIR61NA0Vn6w0+zHzVKbs9+Ggrd/wc= diff --git a/docs/diagrams/AddCommandSequenceDiagram.puml b/docs/diagrams/AddCommandSequenceDiagram.puml new file mode 100644 index 00000000000..94473566fdf --- /dev/null +++ b/docs/diagrams/AddCommandSequenceDiagram.puml @@ -0,0 +1,68 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SupperStrikersParser" as SupperStrikersParser LOGIC_COLOR +participant ":AddCommandParser" as AddCommandParser LOGIC_COLOR +participant "s:AddCommand" as AddCommand LOGIC_COLOR +end box + + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +participant "o:OrderItem" as OrderItem MODEL_COLOR +end box + +[-> LogicManager : execute("add 2 3") +activate LogicManager + +LogicManager -> SupperStrikersParser : parseCommand("add 2 3") +activate SupperStrikersParser + +create AddCommandParser +SupperStrikersParser -> AddCommandParser +activate AddCommandParser + +AddCommandParser --> SupperStrikersParser +deactivate AddCommandParser + +SupperStrikersParser -> AddCommandParser : parse("2 3") +activate AddCommandParser + +create AddCommand +AddCommandParser -> AddCommand +activate AddCommand +AddCommand --> AddCommandParser : s +deactivate AddCommand + +AddCommandParser --> SupperStrikersParser : s +deactivate AddCommandParser +AddCommandParser -[hidden]-> SupperStrikersParser +destroy AddCommandParser + +SupperStrikersParser --> LogicManager : s +deactivate SupperStrikersParser + +LogicManager -> AddCommand : execute() +activate AddCommand + +AddCommand -> Model : getFilteredMenuItemList() + +create OrderItem +AddCommand -> OrderItem +activate OrderItem + +OrderItem --> AddCommand +deactivate OrderItem + +AddCommand -> Model: addOrderItem(o) + +AddCommand -> LogicManager +deactivate AddCommand + +[<-- LogicManager +deactivate LogicManager + + +@enduml diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index ef81d18c337..553baf0cfae 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -19,7 +19,7 @@ activate model MODEL_COLOR model -[MODEL_COLOR]-> logic deactivate model -logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook) +logic -[LOGIC_COLOR]> storage : saveAddressBook(vendorManager) activate storage STORAGE_COLOR storage -[STORAGE_COLOR]> storage : Save to file diff --git a/docs/diagrams/ClearCommandSequenceDiagram.puml b/docs/diagrams/ClearCommandSequenceDiagram.puml new file mode 100644 index 00000000000..cf659a7760d --- /dev/null +++ b/docs/diagrams/ClearCommandSequenceDiagram.puml @@ -0,0 +1,49 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SupperStrikersParser" as SupperStrikersParser LOGIC_COLOR +participant "s:ClearCommand" as ClearCommand LOGIC_COLOR +end box + + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("clear") +activate LogicManager + + +LogicManager -> SupperStrikersParser : parseCommand("clear") +activate SupperStrikersParser + +create ClearCommand +SupperStrikersParser -> ClearCommand +activate ClearCommand + + +ClearCommand --> SupperStrikersParser +deactivate ClearCommand + +SupperStrikersParser --> LogicManager +deactivate SupperStrikersParser + +LogicManager -> ClearCommand: execute() +activate ClearCommand + +ClearCommand -> Model: getOrderSize() + +ClearCommand -> Model: clearOrder() + +ClearCommand --> LogicManager +deactivate ClearCommand + + +[<-- LogicManager +deactivate LogicManager + +destroy ClearCommand + +@enduml diff --git a/docs/diagrams/DeletePresetSequenceDiagram.puml b/docs/diagrams/DeletePresetSequenceDiagram.puml new file mode 100644 index 00000000000..8bf14593b54 --- /dev/null +++ b/docs/diagrams/DeletePresetSequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SupperStrikersParser" as SupperStrikersParser LOGIC_COLOR +participant ":PresetCommandParser" as PresetCommandParser LOGIC_COLOR +participant "d:DeletePresetCommand" as DeletePresetCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + + +box Storage STORAGE_COLOR_T1 +participant ":Storage" as Storage STORAGE_COLOR +end box + +[-> LogicManager : execute("preset delete sample") +activate LogicManager + +LogicManager -> SupperStrikersParser : parseCommand("preset delete sample") +activate SupperStrikersParser + +create PresetCommandParser +SupperStrikersParser -> PresetCommandParser +activate PresetCommandParser + +PresetCommandParser --> SupperStrikersParser +deactivate PresetCommandParser + +SupperStrikersParser -> PresetCommandParser : parse("delete sample") +activate PresetCommandParser + +create DeletePresetCommand +PresetCommandParser -> DeletePresetCommand +activate DeletePresetCommand + +DeletePresetCommand --> PresetCommandParser : d +deactivate DeletePresetCommand + +PresetCommandParser --> SupperStrikersParser : d +deactivate PresetCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +PresetCommandParser -[hidden]-> SupperStrikersParser +destroy PresetCommandParser + +SupperStrikersParser --> LogicManager : d +deactivate SupperStrikersParser + +LogicManager -> DeletePresetCommand : execute() +activate DeletePresetCommand + +DeletePresetCommand -> Storage : readPresetManager() +activate Storage +Storage --> DeletePresetCommand: allLists +deactivate Storage + + +DeletePresetCommand -> Storage : savePresetManager(allLists) +activate Storage +Storage --> DeletePresetCommand +deactivate Storage +||| +create CommandResult +DeletePresetCommand -> CommandResult +activate CommandResult + +CommandResult --> DeletePresetCommand : r +deactivate CommandResult + +DeletePresetCommand --> LogicManager : r +deactivate DeletePresetCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 1dc2311b245..0c834ed8b1f 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -3,66 +3,66 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR -participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR -participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR -participant ":CommandResult" as CommandResult LOGIC_COLOR +participant ":SupperStrikersParser" as SupperStrikersParser LOGIC_COLOR +participant ":RemoveCommandParser" as RemoveCommandParser LOGIC_COLOR +participant "d:RemoveCommand" as RemoveCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR end box box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR end box -[-> LogicManager : execute("delete 1") +[-> LogicManager : execute("remove 1") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") -activate AddressBookParser +LogicManager -> SupperStrikersParser : parseCommand("remove 1") +activate SupperStrikersParser -create DeleteCommandParser -AddressBookParser -> DeleteCommandParser -activate DeleteCommandParser +create RemoveCommandParser +SupperStrikersParser -> RemoveCommandParser +activate RemoveCommandParser -DeleteCommandParser --> AddressBookParser -deactivate DeleteCommandParser +RemoveCommandParser --> SupperStrikersParser +deactivate RemoveCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") -activate DeleteCommandParser +SupperStrikersParser -> RemoveCommandParser : parse("1") +activate RemoveCommandParser -create DeleteCommand -DeleteCommandParser -> DeleteCommand -activate DeleteCommand +create RemoveCommand +RemoveCommandParser -> RemoveCommand +activate RemoveCommand -DeleteCommand --> DeleteCommandParser : d -deactivate DeleteCommand +RemoveCommand --> RemoveCommandParser : d +deactivate RemoveCommand -DeleteCommandParser --> AddressBookParser : d -deactivate DeleteCommandParser +RemoveCommandParser --> SupperStrikersParser : d +deactivate RemoveCommandParser 'Hidden arrow to position the destroy marker below the end of the activation bar. -DeleteCommandParser -[hidden]-> AddressBookParser -destroy DeleteCommandParser +RemoveCommandParser -[hidden]-> SupperStrikersParser +destroy RemoveCommandParser -AddressBookParser --> LogicManager : d -deactivate AddressBookParser +SupperStrikersParser --> LogicManager : d +deactivate SupperStrikersParser -LogicManager -> DeleteCommand : execute() -activate DeleteCommand +LogicManager -> RemoveCommand : execute() +activate RemoveCommand -DeleteCommand -> Model : deletePerson(1) +RemoveCommand -> Model : removeOrderItem(1) activate Model -Model --> DeleteCommand +Model --> RemoveCommand deactivate Model create CommandResult -DeleteCommand -> CommandResult +RemoveCommand -> CommandResult activate CommandResult -CommandResult --> DeleteCommand +CommandResult --> RemoveCommand : r deactivate CommandResult -DeleteCommand --> LogicManager : result -deactivate DeleteCommand +RemoveCommand --> LogicManager : r +deactivate RemoveCommand [<--LogicManager deactivate LogicManager diff --git a/docs/diagrams/FindCommandSequenceDiagram.puml b/docs/diagrams/FindCommandSequenceDiagram.puml new file mode 100644 index 00000000000..03d6cbdcf76 --- /dev/null +++ b/docs/diagrams/FindCommandSequenceDiagram.puml @@ -0,0 +1,66 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SupperStrikersParser" as SupperStrikersParser LOGIC_COLOR +participant ":FindCommandParser" as FindCommandParser LOGIC_COLOR +participant "f:FindCommand" as FindCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("find Milo") +activate LogicManager + +LogicManager -> SupperStrikersParser : parseCommand("find Milo") +activate SupperStrikersParser + +create FindCommandParser +SupperStrikersParser -> FindCommandParser +activate FindCommandParser + +FindCommandParser --> SupperStrikersParser +deactivate FindCommandParser + +SupperStrikersParser -> FindCommandParser : parse("Milo") +activate FindCommandParser + +create FindCommand +FindCommandParser -> FindCommand +activate FindCommand +FindCommand --> FindCommandParser : f +deactivate FindCommand + +FindCommandParser --> SupperStrikersParser : f +deactivate FindCommandParser +FindCommandParser -[hidden]-> SupperStrikersParser +destroy FindCommandParser + +SupperStrikersParser --> LogicManager : f +deactivate SupperStrikersParser + +LogicManager -> FindCommand : execute() +activate FindCommand + +FindCommand -> Model : updateFilteredMenuItemList() +activate Model +Model --> FindCommand +deactivate Model + +create CommandResult +FindCommand -> CommandResult +activate CommandResult + +CommandResult --> FindCommand : r +deactivate CommandResult + +FindCommand --> LogicManager: r +deactivate FindCommand +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/LoadPresetCommandActivityDiagram.puml b/docs/diagrams/LoadPresetCommandActivityDiagram.puml new file mode 100644 index 00000000000..fa25598e017 --- /dev/null +++ b/docs/diagrams/LoadPresetCommandActivityDiagram.puml @@ -0,0 +1,25 @@ +@startuml +start +:User executes LoadPresetCommand; + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +if () then ([Vendor has been selected]) +:Current presets are read from storage; +if () then([preset name exists]) +:Preset with the same name is +loaded into the current order; +else ([else]) +:Supper Strikers throws a CommandException +informing user that the preset does not exist; +:Error message is displayed to user; +endif + +else ([else]) +:Supper Strikers throws a CommandException +informing user that no vendor has been selected; +:Error message is displayed to user; +endif +stop +@enduml diff --git a/docs/diagrams/LoadPresetSequenceDiagram.puml b/docs/diagrams/LoadPresetSequenceDiagram.puml new file mode 100644 index 00000000000..a2c5e6d2053 --- /dev/null +++ b/docs/diagrams/LoadPresetSequenceDiagram.puml @@ -0,0 +1,79 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SupperStrikersParser" as SupperStrikersParser LOGIC_COLOR +participant ":PresetCommandParser" as PresetCommandParser LOGIC_COLOR +participant "d:LoadPresetCommand" as LoadPresetCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + + +box Storage STORAGE_COLOR_T1 +participant ":Storage" as Storage STORAGE_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("preset load sample") +activate LogicManager + +LogicManager -> SupperStrikersParser : parseCommand("preset load sample") +activate SupperStrikersParser + +create PresetCommandParser +SupperStrikersParser -> PresetCommandParser +activate PresetCommandParser + +PresetCommandParser --> SupperStrikersParser +deactivate PresetCommandParser + +SupperStrikersParser -> PresetCommandParser : parse("load sample") +activate PresetCommandParser + +create LoadPresetCommand +PresetCommandParser -> LoadPresetCommand +activate LoadPresetCommand + +LoadPresetCommand --> PresetCommandParser : d +deactivate LoadPresetCommand + +PresetCommandParser --> SupperStrikersParser : d +deactivate PresetCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +PresetCommandParser -[hidden]-> SupperStrikersParser +destroy PresetCommandParser + +SupperStrikersParser --> LogicManager : d +deactivate SupperStrikersParser + +LogicManager -> LoadPresetCommand : execute() +activate LoadPresetCommand + +LoadPresetCommand -> Storage : readPresetManager() +activate Storage +Storage --> LoadPresetCommand: allLists +deactivate Storage +||| + +LoadPresetCommand -> Model: setOrder(orderItems) +activate Model +Model --> LoadPresetCommand +deactivate Model +||| +create CommandResult +LoadPresetCommand -> CommandResult +activate CommandResult + +CommandResult --> LoadPresetCommand +deactivate CommandResult + +LoadPresetCommand --> LogicManager : result +deactivate LoadPresetCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index 016ef33e2e2..0dbc4b12267 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -8,13 +8,9 @@ package Logic { package Parser { Interface Parser <> -Class AddressBookParser +Class SupperStrikersParser Class XYZCommandParser -Class CliSyntax -Class ParserUtil -Class ArgumentMultimap -Class ArgumentTokenizer -Class Prefix + } package Command { @@ -31,29 +27,27 @@ package Model{ Class HiddenModel #FFFFFF } +package Storage { +Class HiddenStorage #FFFFFF +} + Class HiddenOutside #FFFFFF HiddenOutside ..> Logic LogicManager .up.|> Logic -LogicManager -->"1" AddressBookParser -AddressBookParser .left.> XYZCommandParser: creates > +LogicManager -->"1" SupperStrikersParser +SupperStrikersParser ..> XYZCommandParser: creates > +SupperStrikersParser ..> XYZCommand: creates > XYZCommandParser ..> XYZCommand : creates > XYZCommandParser ..|> Parser -XYZCommandParser ..> ArgumentMultimap -XYZCommandParser ..> ArgumentTokenizer -ArgumentTokenizer .left.> ArgumentMultimap -XYZCommandParser ..> CliSyntax -CliSyntax ..> Prefix -XYZCommandParser ..> ParserUtil -ParserUtil .down.> Prefix -ArgumentTokenizer .down.> Prefix XYZCommand -up-|> Command LogicManager .left.> Command : executes > LogicManager --> Model -Command .right.> Model -note right of XYZCommand: XYZCommand = AddCommand, \nFindCommand, etc +Command .up.> Model +Command .right.> Storage +note right of XYZCommand: XYZCommand = AddCommand, \nSortCommand, etc Logic ..> CommandResult LogicManager .down.> CommandResult diff --git a/docs/diagrams/MenuCommandSequenceDiagram.puml b/docs/diagrams/MenuCommandSequenceDiagram.puml new file mode 100644 index 00000000000..b942dce9e50 --- /dev/null +++ b/docs/diagrams/MenuCommandSequenceDiagram.puml @@ -0,0 +1,52 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SupperStrikersParser" as SupperStrikersParser LOGIC_COLOR +participant "m:MenuCommand" as MenuCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("menu") +activate LogicManager + +LogicManager -> SupperStrikersParser : parseCommand("menu") +activate SupperStrikersParser + +create MenuCommand +SupperStrikersParser -> MenuCommand +activate MenuCommand + +MenuCommand --> SupperStrikersParser : m +deactivate MenuCommand + +SupperStrikersParser --> LogicManager : m +deactivate SupperStrikersParser + +LogicManager -> MenuCommand : execute() +activate MenuCommand + +MenuCommand -> Model : showDefaultMenu() +activate Model +Model --> MenuCommand +deactivate Model + +create CommandResult +MenuCommand -> CommandResult +activate CommandResult + +CommandResult --> MenuCommand +deactivate CommandResult + +MenuCommand --> LogicManager: result +deactivate MenuCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index e85a00d4107..006163994fa 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -5,52 +5,83 @@ skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR Package Model <>{ -Interface ReadOnlyAddressBook <> +Interface ReadOnlyVendorManager <> Interface Model <> Interface ObservableList <> -Class AddressBook -Class ReadOnlyAddressBook +Class VendorManager +Class ReadOnlyVendorManager <> Class Model Class ModelManager +Class MenuManager +Class ReadOnlyMenuManager <> +Class ReadOnlyOrderManager <> +Class OrderManager Class UserPrefs -Class ReadOnlyUserPrefs +Class ReadOnlyUserPrefs <> -Package Person { -Class Person +Package Vendor { +Class Vendor Class Address Class Email Class Name Class Phone -Class UniquePersonList +Class UniqueVendorList } Package Tag { Class Tag } + +Package Food { +Abstract Class Food +Class Order +Class Menu +Class MenuItem +Class OrderItem +} + } Class HiddenOutside #FFFFFF HiddenOutside ..> Model -AddressBook .up.|> ReadOnlyAddressBook +VendorManager .up.|> ReadOnlyVendorManager ModelManager .up.|> Model -Model .right.> ObservableList -ModelManager o--> "1" AddressBook +ModelManager o--> "1" VendorManager +ModelManager o--> "1" OrderManager +ModelManager o--> "*" MenuManager ModelManager o-left-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs -AddressBook *--> "1" UniquePersonList -UniquePersonList o--> "*" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag +VendorManager *--> "1" UniqueVendorList +UniqueVendorList o--> "*" Vendor +UniqueVendorList .up.|> ObservableList + +MenuManager .up.|> ReadOnlyMenuManager +MenuManager *--> "1" Menu +Menu .up.|> ObservableList +Menu o--> "*" MenuItem +MenuItem -up-> Food +MenuItem *--> "*" Tag + +OrderManager *--> Order +OrderManager .up.|> ReadOnlyOrderManager +Order .up.|> ObservableList +Order o--> "*" OrderItem +OrderItem -up-> Food +OrderItem *--> "*" Tag + +Vendor *--> Name +Vendor *--> Phone +Vendor *--> Email +Vendor *--> Address +Vendor *--> "*" Tag +OrderManager -[hidden]left-> ReadOnlyMenuManager +Order -[hidden]right-> MenuItem Name -[hidden]right-> Phone Phone -[hidden]right-> Address Address -[hidden]right-> Email -ModelManager -->"1" Person : filtered list @enduml diff --git a/docs/diagrams/RemoveCommandSequenceDiagram.puml b/docs/diagrams/RemoveCommandSequenceDiagram.puml new file mode 100644 index 00000000000..f753765c18c --- /dev/null +++ b/docs/diagrams/RemoveCommandSequenceDiagram.puml @@ -0,0 +1,68 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SupperStrikersParser" as SupperStrikersParser LOGIC_COLOR +participant ":RemoveCommandParser" as RemoveCommandParser LOGIC_COLOR +participant "s:RemoveCommand" as RemoveCommand LOGIC_COLOR +end box + + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +participant "o:OrderItem" as OrderItem MODEL_COLOR +end box + +[-> LogicManager : execute("remove 1 1") +activate LogicManager + +LogicManager -> SupperStrikersParser : parseCommand("remove 1 1") +activate SupperStrikersParser + +create RemoveCommandParser +SupperStrikersParser -> RemoveCommandParser +activate RemoveCommandParser + +RemoveCommandParser --> SupperStrikersParser +deactivate RemoveCommandParser + +SupperStrikersParser -> RemoveCommandParser : parse("1 1") +activate RemoveCommandParser + +create RemoveCommand +RemoveCommandParser -> RemoveCommand +activate RemoveCommand +RemoveCommand --> RemoveCommandParser : s +deactivate RemoveCommand + +RemoveCommandParser --> SupperStrikersParser : s +deactivate RemoveCommandParser +RemoveCommandParser -[hidden]-> SupperStrikersParser +destroy RemoveCommandParser + +SupperStrikersParser --> LogicManager : s +deactivate SupperStrikersParser + +LogicManager -> RemoveCommand : execute() +activate RemoveCommand + +RemoveCommand -> Model : getFilteredOrderItemList() + +create OrderItem +RemoveCommand -> OrderItem +activate OrderItem + +OrderItem --> RemoveCommand +deactivate OrderItem + +RemoveCommand -> Model: removeOrderItem(o) + +RemoveCommand -> LogicManager +deactivate RemoveCommand + +[<-- LogicManager +deactivate LogicManager + + +@enduml diff --git a/docs/diagrams/SavePresetCommandActivityDiagram.puml b/docs/diagrams/SavePresetCommandActivityDiagram.puml new file mode 100644 index 00000000000..b47ff462a4f --- /dev/null +++ b/docs/diagrams/SavePresetCommandActivityDiagram.puml @@ -0,0 +1,32 @@ +@startuml +start +:User executes SavePresetCommand; + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +if () then ([Vendor has been selected]) + +if () then([Order is not empty]) +:Current presets are read from storage; +:New Preset is created from current order; +if () then([Preset Name already exists]) +:Current preset with the same name +is overwritten by the new preset; +else ([else]) +:New preset is added to the list of current presets; +endif +:List of presets is saved to storage; +else ([else]) +:Supper Strikers throws a CommandException +informing user that the order must not be empty; +:Error message is displayed to user; +endif + +else ([else]) +:Supper Strikers throws a CommandException +informing user that no vendor has been selected; +:Error message is displayed to user; +endif +stop +@enduml diff --git a/docs/diagrams/SavePresetSequenceDiagram.puml b/docs/diagrams/SavePresetSequenceDiagram.puml new file mode 100644 index 00000000000..21848116f3f --- /dev/null +++ b/docs/diagrams/SavePresetSequenceDiagram.puml @@ -0,0 +1,84 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SupperStrikersParser" as SupperStrikersParser LOGIC_COLOR +participant ":PresetCommandParser" as PresetCommandParser LOGIC_COLOR +participant "d:SavePresetCommand" as SavePresetCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + + +box Storage STORAGE_COLOR_T1 +participant ":Storage" as Storage STORAGE_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("preset Save sample") +activate LogicManager + +LogicManager -> SupperStrikersParser : parseCommand("preset Save sample") +activate SupperStrikersParser + +create PresetCommandParser +SupperStrikersParser -> PresetCommandParser +activate PresetCommandParser + +PresetCommandParser --> SupperStrikersParser +deactivate PresetCommandParser + +SupperStrikersParser -> PresetCommandParser : parse("Save sample") +activate PresetCommandParser + +create SavePresetCommand +PresetCommandParser -> SavePresetCommand +activate SavePresetCommand + +SavePresetCommand --> PresetCommandParser : d +deactivate SavePresetCommand + +PresetCommandParser --> SupperStrikersParser : d +deactivate PresetCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +PresetCommandParser -[hidden]-> SupperStrikersParser +destroy PresetCommandParser + +SupperStrikersParser --> LogicManager : d +deactivate SupperStrikersParser + +LogicManager -> SavePresetCommand : execute() +activate SavePresetCommand + +SavePresetCommand -> Storage : readPresetManager() +activate Storage +Storage --> SavePresetCommand: allLists +deactivate Storage + +SavePresetCommand -> Model : getObservableOrderItemList() +activate Model +Model --> SavePresetCommand: orderItemList +deactivate Model + + +SavePresetCommand -> Storage : savePresetManager(allLists) +activate Storage +Storage --> SavePresetCommand +deactivate Storage +||| +create CommandResult +SavePresetCommand -> CommandResult +activate CommandResult + +CommandResult --> SavePresetCommand +deactivate CommandResult + +SavePresetCommand --> LogicManager : result +deactivate SavePresetCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/SortCommandSequenceDiagram.puml b/docs/diagrams/SortCommandSequenceDiagram.puml new file mode 100644 index 00000000000..05e73789eff --- /dev/null +++ b/docs/diagrams/SortCommandSequenceDiagram.puml @@ -0,0 +1,66 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SupperStrikersParser" as SupperStrikersParser LOGIC_COLOR +participant ":SortCommandParser" as SortCommandParser LOGIC_COLOR +participant "s:SortCommand" as SortCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("sort n a") +activate LogicManager + +LogicManager -> SupperStrikersParser : parseCommand("sort n a") +activate SupperStrikersParser + +create SortCommandParser +SupperStrikersParser -> SortCommandParser +activate SortCommandParser + +SortCommandParser --> SupperStrikersParser +deactivate SortCommandParser + +SupperStrikersParser -> SortCommandParser : parse("n a") +activate SortCommandParser + +create SortCommand +SortCommandParser -> SortCommand +activate SortCommand +SortCommand --> SortCommandParser : s +deactivate SortCommand + +SortCommandParser --> SupperStrikersParser : s +deactivate SortCommandParser +SortCommandParser -[hidden]-> SupperStrikersParser +destroy SortCommandParser + +SupperStrikersParser --> LogicManager : s +deactivate SupperStrikersParser + +LogicManager -> SortCommand : execute() +activate SortCommand + +SortCommand -> Model : sortMenuItemBy() +activate Model +Model --> SortCommand +deactivate Model + +create CommandResult +SortCommand -> CommandResult +activate CommandResult + +CommandResult --> SortCommand : r +deactivate CommandResult + +SortCommand --> LogicManager: r +deactivate SortCommand +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 6adb2e156bf..4575bd7c60a 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -6,19 +6,33 @@ skinparam classBackgroundColor STORAGE_COLOR Interface Storage <> Interface UserPrefsStorage <> -Interface AddressBookStorage <> +Interface VendorManagerStorage <> +Interface PresetManagerStorage <> +Interface ProfileManagerStorage <> Class StorageManager Class JsonUserPrefsStorage -Class JsonAddressBookStorage +Class JsonVendorManagerStorage +Class JsonPresetManagerStorage +class JsonProfileManagerStorage StorageManager .left.|> Storage StorageManager o-right-> UserPrefsStorage -StorageManager o--> AddressBookStorage +StorageManager o--> VendorManagerStorage +StorageManager o--> PresetManagerStorage +StorageManager o--> ProfileManagerStorage JsonUserPrefsStorage .left.|> UserPrefsStorage -JsonAddressBookStorage .left.|> AddressBookStorage -JsonAddressBookStorage .down.> JsonSerializableAddressBookStorage -JsonSerializableAddressBookStorage .right.> JsonSerializablePerson -JsonSerializablePerson .right.> JsonAdaptedTag +JsonVendorManagerStorage .left.|> VendorManagerStorage +JsonPresetManagerStorage .up.|> PresetManagerStorage +JsonProfileManagerStorage .up.|> ProfileManagerStorage +JsonVendorManagerStorage .down.> JsonSerializableVendorManager +JsonSerializableVendorManager .down.> JsonAdaptedVendor +JsonAdaptedVendor .down.> JsonAdaptedTag +JsonPresetManagerStorage .down.> JsonSerializablePresetManager +JsonPresetManagerStorage .down.> JsonAdaptedPreset +JsonAdaptedPreset .down.> JsonAdaptedOrderItem +JsonAdaptedOrderItem .right.> JsonAdaptedTag +JsonProfileManagerStorage .down.> JsonAdaptedProfile +JsonProfileManagerStorage .down.> JsonSerializableProfileManager @enduml diff --git a/docs/diagrams/SwitchVendorCommandActivityDiagram.puml b/docs/diagrams/SwitchVendorCommandActivityDiagram.puml new file mode 100644 index 00000000000..b45111a8b65 --- /dev/null +++ b/docs/diagrams/SwitchVendorCommandActivityDiagram.puml @@ -0,0 +1,24 @@ +@startuml +start +:User executes SwitchVendorCommand; + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +if () then ([Vendor Index is valid]) + :Supper Strikers selects vendor + corresponding to the index; + :Vendor window will be hidden + and replaced with corresponding + Menu window; + if() then([index is different from current index]) + :Current order is cleared; + else ([else]) + endif +else ([else]) +:Supper Strikers throws a CommandException +informing user that index provided is invalid; +:Error message is displayed to user; +endif +stop +@enduml diff --git a/docs/diagrams/TagCommandActivityDiagram.puml b/docs/diagrams/TagCommandActivityDiagram.puml new file mode 100644 index 00000000000..1d6d2ad05f7 --- /dev/null +++ b/docs/diagrams/TagCommandActivityDiagram.puml @@ -0,0 +1,69 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SupperStrikersParser" as SupperStrikersParser LOGIC_COLOR +participant ":TagCommandParser" as TagCommandParser LOGIC_COLOR +participant "t:TagCommand" as TagCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("tag 1 all no ice") +activate LogicManager + +LogicManager -> SupperStrikersParser : parseCommand("tag 1 all no ice") +activate SupperStrikersParser + +create TagCommandParser +SupperStrikersParser -> TagCommandParser +activate TagCommandParser + +TagCommandParser --> SupperStrikersParser +deactivate TagCommandParser + +SupperStrikersParser -> TagCommandParser : parse("1", "all no ice") +activate TagCommandParser + +create TagCommand +TagCommandParser -> TagCommand +activate TagCommand + +TagCommand --> TagCommandParser : t +deactivate TagCommand + +TagCommandParser --> SupperStrikersParser : t +deactivate TagCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +TagCommandParser -[hidden]-> SupperStrikersParser +destroy TagCommandParser + +SupperStrikersParser --> LogicManager : t +deactivate SupperStrikersParser + +LogicManager -> TagCommand : execute() +activate TagCommand + +TagCommand -> Model : TagOrderItem(1, "all no ice") +activate Model + +Model --> TagCommand +deactivate Model + +create CommandResult +TagCommand -> CommandResult +activate CommandResult + +CommandResult --> TagCommand +deactivate CommandResult + +TagCommand --> LogicManager : result +deactivate TagCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 92746f9fcf7..9e426430a84 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -11,9 +11,12 @@ Class UiManager Class MainWindow Class HelpWindow Class ResultDisplay -Class PersonListPanel -Class PersonCard -Class StatusBarFooter +Class VendorListPanel +Class VendorCard +Class FoodListPanel +Class FoodCard +Class OrderListPanel +Class OrderCard Class CommandBox } @@ -33,28 +36,33 @@ UiManager -down-> MainWindow MainWindow --> HelpWindow MainWindow *-down-> CommandBox MainWindow *-down-> ResultDisplay -MainWindow *-down-> PersonListPanel -MainWindow *-down-> StatusBarFooter +MainWindow *-down-> VendorListPanel +MainWindow *-down-> FoodListPanel +MainWindow *-down-> OrderListPanel -PersonListPanel -down-> PersonCard +VendorListPanel -down-> VendorCard +FoodListPanel -down-> FoodCard +OrderListPanel -down-> OrderCard MainWindow -left-|> UiPart ResultDisplay --|> UiPart CommandBox --|> UiPart -PersonListPanel --|> UiPart -PersonCard --|> UiPart -StatusBarFooter --|> UiPart +VendorListPanel --|> UiPart +VendorCard --|> UiPart +FoodListPanel --|> UiPart +FoodCard --|> UiPart HelpWindow -down-|> UiPart -PersonCard ..> Model +VendorCard ..> Model +FoodCard .right.> Model +OrderCard .up.> Model UiManager -right-> Logic MainWindow -left-> Logic -PersonListPanel -[hidden]left- HelpWindow +VendorListPanel -[hidden]left- HelpWindow HelpWindow -[hidden]left- CommandBox CommandBox -[hidden]left- ResultDisplay -ResultDisplay -[hidden]left- StatusBarFooter MainWindow -[hidden]-|> UiPart @enduml diff --git a/docs/diagrams/UntagCommandActivityDiagram.puml b/docs/diagrams/UntagCommandActivityDiagram.puml new file mode 100644 index 00000000000..9570a63ef0a --- /dev/null +++ b/docs/diagrams/UntagCommandActivityDiagram.puml @@ -0,0 +1,69 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SupperStrikersParser" as SupperStrikersParser LOGIC_COLOR +participant ":UntagCommandParser" as UntagCommandParser LOGIC_COLOR +participant "u:UntagCommand" as UntagCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("untag 1") +activate LogicManager + +LogicManager -> SupperStrikersParser : parseCommand("untag 1") +activate SupperStrikersParser + +create UntagCommandParser +SupperStrikersParser -> UntagCommandParser +activate UntagCommandParser + +UntagCommandParser --> SupperStrikersParser +deactivate UntagCommandParser + +SupperStrikersParser -> UntagCommandParser : parse("1") +activate UntagCommandParser + +create UntagCommand +UntagCommandParser -> UntagCommand +activate UntagCommand + +UntagCommand --> UntagCommandParser : u +deactivate UntagCommand + +UntagCommandParser --> SupperStrikersParser : u +deactivate UntagCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +UntagCommandParser -[hidden]-> SupperStrikersParser +destroy UntagCommandParser + +SupperStrikersParser --> LogicManager : u +deactivate SupperStrikersParser + +LogicManager -> UntagCommand : execute() +activate UntagCommand + +UntagCommand -> Model : untagOrderItem(1) +activate Model + +Model --> UntagCommand +deactivate Model + +create CommandResult +UntagCommand -> CommandResult +activate CommandResult + +CommandResult --> UntagCommand +deactivate CommandResult + +UntagCommand --> LogicManager : result +deactivate UntagCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/images/AddCommandDiagram.png b/docs/images/AddCommandDiagram.png new file mode 100644 index 00000000000..20bc57c0bf6 Binary files /dev/null and b/docs/images/AddCommandDiagram.png differ diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 2f1346869d0..f4b4434db6c 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/ClearCommandDiagram.png b/docs/images/ClearCommandDiagram.png new file mode 100644 index 00000000000..7c19f76e756 Binary files /dev/null and b/docs/images/ClearCommandDiagram.png differ diff --git a/docs/images/DeletePresetCommandSequenceDiagram.png b/docs/images/DeletePresetCommandSequenceDiagram.png new file mode 100644 index 00000000000..a7fafc57734 Binary files /dev/null and b/docs/images/DeletePresetCommandSequenceDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index fa327b39618..75e8c1f97cc 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram2.png b/docs/images/DeleteSequenceDiagram2.png new file mode 100644 index 00000000000..75e8c1f97cc Binary files /dev/null and b/docs/images/DeleteSequenceDiagram2.png differ diff --git a/docs/images/FindCommandSequenceDiagram.png b/docs/images/FindCommandSequenceDiagram.png new file mode 100644 index 00000000000..2e2ebc3ca4f Binary files /dev/null and b/docs/images/FindCommandSequenceDiagram.png differ diff --git a/docs/images/LoadPresetCommandActivityDiagram.png b/docs/images/LoadPresetCommandActivityDiagram.png new file mode 100644 index 00000000000..0d557c6e716 Binary files /dev/null and b/docs/images/LoadPresetCommandActivityDiagram.png differ diff --git a/docs/images/LoadPresetCommandSequenceDiagram.png b/docs/images/LoadPresetCommandSequenceDiagram.png new file mode 100644 index 00000000000..53f276073ba Binary files /dev/null and b/docs/images/LoadPresetCommandSequenceDiagram.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index b9e853cef12..7c5a1093993 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/LogicClassDiagram2.png b/docs/images/LogicClassDiagram2.png new file mode 100644 index 00000000000..74cbf0e0b3c Binary files /dev/null and b/docs/images/LogicClassDiagram2.png differ diff --git a/docs/images/MenuCommandSequenceDiagram.png b/docs/images/MenuCommandSequenceDiagram.png new file mode 100644 index 00000000000..0c76b67a038 Binary files /dev/null and b/docs/images/MenuCommandSequenceDiagram.png differ diff --git a/docs/images/ModelClassDiagram2.png b/docs/images/ModelClassDiagram2.png new file mode 100644 index 00000000000..513e75e2795 Binary files /dev/null and b/docs/images/ModelClassDiagram2.png differ diff --git a/docs/images/RemoveCommandDiagram.png b/docs/images/RemoveCommandDiagram.png new file mode 100644 index 00000000000..c653d3bab8e Binary files /dev/null and b/docs/images/RemoveCommandDiagram.png differ diff --git a/docs/images/SavePresetCommandActivityDiagram.png b/docs/images/SavePresetCommandActivityDiagram.png new file mode 100644 index 00000000000..a219ef5e4a4 Binary files /dev/null and b/docs/images/SavePresetCommandActivityDiagram.png differ diff --git a/docs/images/SavePresetCommandSequenceDiagram.png b/docs/images/SavePresetCommandSequenceDiagram.png new file mode 100644 index 00000000000..d9bda464138 Binary files /dev/null and b/docs/images/SavePresetCommandSequenceDiagram.png differ diff --git a/docs/images/SortCommandSequenceDiagram.png b/docs/images/SortCommandSequenceDiagram.png new file mode 100644 index 00000000000..45e4d0872ac Binary files /dev/null and b/docs/images/SortCommandSequenceDiagram.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index d87c1216820..b612c187c3d 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/StorageClassDiagram2.png b/docs/images/StorageClassDiagram2.png new file mode 100644 index 00000000000..8f2ef59b47a Binary files /dev/null and b/docs/images/StorageClassDiagram2.png differ diff --git a/docs/images/SwitchVendorCommandActivityDiagram.png b/docs/images/SwitchVendorCommandActivityDiagram.png new file mode 100644 index 00000000000..eb7deeaeb4a Binary files /dev/null and b/docs/images/SwitchVendorCommandActivityDiagram.png differ diff --git a/docs/images/TagCommandActivityDiagram.png b/docs/images/TagCommandActivityDiagram.png new file mode 100644 index 00000000000..9d08568294d Binary files /dev/null and b/docs/images/TagCommandActivityDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5bd77847aa2..10e8903dd9b 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png index 7b4b3dbea45..02fd4cc9a05 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/UntagCommandActivityDiagram.png b/docs/images/UntagCommandActivityDiagram.png new file mode 100644 index 00000000000..f79c9406220 Binary files /dev/null and b/docs/images/UntagCommandActivityDiagram.png differ diff --git a/docs/images/VendorCommandSequenceDiagram.png b/docs/images/VendorCommandSequenceDiagram.png new file mode 100644 index 00000000000..14675cc0edd Binary files /dev/null and b/docs/images/VendorCommandSequenceDiagram.png differ diff --git a/docs/images/duckmoon99.png b/docs/images/duckmoon99.png new file mode 100644 index 00000000000..d1b0502a899 Binary files /dev/null and b/docs/images/duckmoon99.png differ diff --git a/docs/images/ebolaeater.png b/docs/images/ebolaeater.png new file mode 100644 index 00000000000..97f4519491e Binary files /dev/null and b/docs/images/ebolaeater.png differ diff --git a/docs/images/ernestlim8.png b/docs/images/ernestlim8.png new file mode 100644 index 00000000000..e9d14761a35 Binary files /dev/null and b/docs/images/ernestlim8.png differ diff --git a/docs/images/friendly_syntax_architecture_diagram.png b/docs/images/friendly_syntax_architecture_diagram.png new file mode 100644 index 00000000000..4a356e81e4f Binary files /dev/null and b/docs/images/friendly_syntax_architecture_diagram.png differ diff --git a/docs/images/kendrewchan.png b/docs/images/kendrewchan.png new file mode 100644 index 00000000000..1f9df57aeb1 Binary files /dev/null and b/docs/images/kendrewchan.png differ diff --git a/docs/images/morninglit.png b/docs/images/morninglit.png new file mode 100644 index 00000000000..c512b4077af Binary files /dev/null and b/docs/images/morninglit.png differ diff --git a/docs/images/tset.png b/docs/images/tset.png new file mode 100644 index 00000000000..cfb69068dee Binary files /dev/null and b/docs/images/tset.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..09e1d10b728 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,17 @@ --- layout: page -title: AddressBook Level-3 +title: Supper Strikers --- -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) -[![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3) +![Java CI](https://github.com/AY2021S1-CS2103-T16-1/tp/workflows/Java%20CI/badge.svg) +[![codecov](https://codecov.io/gh/AY2021S1-CS2103-T16-1/tp/branch/master/graph/badge.svg?token=CJKQa8CATW)](https://codecov.io/gh/AY2021S1-CS2103-T16-1/tp) ![Ui](images/Ui.png) -**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). +**Supper Strikers is a desktop application for managing your supper orders.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). -* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). -* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. +* If you are interested in using Supper Strikers, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). +* If you are interested about developing Supper Strikers, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** diff --git a/docs/team/duckmoon99.md b/docs/team/duckmoon99.md new file mode 100644 index 00000000000..f7d3b055acc --- /dev/null +++ b/docs/team/duckmoon99.md @@ -0,0 +1,38 @@ +--- +layout: page +title: Wei Xin's Project Portfolio Page +--- + +## Project: SupperStrikers + +SupperStriker is a desktop meal ordering application. The user interacts with it using a CLI, and it has a GUI created +with JavaFX. It is written in Java. + +Given below are my contributions to the project. + +* **New Feature**: Added OrderItem, Order class + * What it does: Used to represent order items. +* **New Feature**: Added Friendly Syntax + * What it does: Users can type prefix of a command to use it, without typing the full command. For example, users can + type `r` instead of `remove` to use the `remove` command. If the prefix is ambiguous (match multiple commands) the + user will be notified. + * Justification: To reduce the number of characters needed to be typed by the user, increasing speed. +* **New Feature**: Add Undo Command + * What it does: Add ability for user to undo last few changes to the order. + * Justification: To allow user to recover from minor mistakes, without going through the trouble of edit or remove. +* **New Feature**: Add Price Filter + * What it does: Users are able to list out all food item within a certain price range. For example, `price < 5` lists + all food item with price < $5. + * Justification: Allows price conscious users to find food within their budget. +* **New Feature**: Add Tag/Untag feature + * What it does: Add ability for user to tag specific order item with remarks. + * Justification: Allows users to specify special request for their food if needed, ie. "Less sugar", "No nuts" +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=duckmoon99) +* **Enhancements made**: + * Wrote additional tests to increase code coverage [#228](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/228) +* **Documentation**: + * User Guide: Update and finalised User Guide [#213](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/213) + * Developer Guide: Explained how the Undo feature is implemented [#111](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/111) +* **Team contribution**: + * Hosted meetings and suggested directions for the project. + * Reorganise bug reports into more coherent problems to be fixed. diff --git a/docs/team/ebolaeater.md b/docs/team/ebolaeater.md new file mode 100644 index 00000000000..f685195af34 --- /dev/null +++ b/docs/team/ebolaeater.md @@ -0,0 +1,53 @@ +--- +layout: page +title: Anikesh's Project Portfolio Page +--- + +## Project: SupperStrikers + +SupperStrikers is a food ordering application. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. + +* **New Feature**: Added MenuItem and Vendor Class. + + * What it does: Used to represent menu items as items the vendor has in the menu + +* **New Feature**: Added Clear Command + + * What it does: Allows users to clear their current supper order. + * Justification: Helps the user to quickly delete all the items in the order and start afresh. + +* **New Feature**: Helped add Vendor Command + + * What it does: Allows users to choose a vendor to order from. + * Justification: Needed by the user to choose a vendor to order from. + + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=&sort=groupTitle&sortWithin=title&since=2020-08-14&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other&tabOpen=true&tabType=authorship&tabAuthor=Ebolaeater&tabRepo=AY2021S1-CS2103-T16-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&reverseAuthorshipOrder=true) + + +* **Enhancements to existing features**: + + * Helped update GUI to enable the displaying of images for menu items (Pull requests [#196](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/194)) + * Refactored AddCommand (Pull requests [#59](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/59)) + * Add clipboard functionality for submit command (Pull requests [#187](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/187)) + * Wrote tests to increase code coverage (Pull requests [#204](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/204), [\#220](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/220), [\#121](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/121), [\#126](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/126), [\#202](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/202), [\#198](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/198)) + * Add actual vendors into SupperStrikers (Pull requests [#232](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/232)) + +* **Documentation**: + + * User Guide: + * Added documentation for `total` and `submit` command: [#69](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/69) + + * Developer Guide: + * Added use case details for `vendor` and `add` command : [#13](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/13) + * Added use case details for `clear` and `submit` command : [#94](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/94) + * Added use case details for `total` command : [#20](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/20/files) + * Added implementation details of `tag` and `untag`: [#223](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/223) + * Added implementation details of features like friendly-syntax : [#121](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/121) + +* **Community**: + + * PRs reviewed (with non-trivial review comments):[#222](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/222) + * Reported bugs for other teams: (examples: [#5](https://github.com/Ebolaeater/ped/issues/5), [#1](https://github.com/Ebolaeater/ped/issues/1)) diff --git a/docs/team/ernestlim8.md b/docs/team/ernestlim8.md new file mode 100644 index 00000000000..9ffff8f123d --- /dev/null +++ b/docs/team/ernestlim8.md @@ -0,0 +1,74 @@ +--- +layout: page +title: Ernest Lim's Project Portfolio Page +--- + +## Project: SupperStrikers + +SupperStrikers is a food ordering application. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. + +* **New Feature**: Added the ability to display and switch between vendors. (Pull requests [#78](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/78), [#86](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/86)) + + * What it does: Allows the user to select a vendor to order from. When a vendor has been successfully selected, the GUI portion will display the menu of the selected vendor. + * Justification: This feature is required to allow users to select a vendor so that they are able to view the menu corresponding to that vendor. + * Highlights: This enhancement creates a new vendor mode for SupperStrikers, where the menu based commands will return an error message in vendor mode. Corresponding commands such as VendorCommand and SwitchVendorCommand are implemented to switch between vendor mode and menu mode. + +* **New Feature**: Added the ability to display the menu of the selected vendor in menu mode. (Pull requests [#78](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/78), [#86](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/86)) + + * What it does: Allows the user to view the menu of a selected vendor in the GUI. + + * Justification: This feature reduces clutter in the screen as the vendor list is not important to the user after the user has + + selected a vendor. + +* **New Feature**: Added the ability for users to save and load their order items in their presets. (Pull request [#142](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/142)) + + * What it does: Allows the user to load their presets from a json file. Users are able to create a preset for that vendor and give the preset a name. The user is able to load the preset using its name and they can modify the order items after the preset is loaded before submitting. + + * Highlights: If the preset is saved with an already existing name, it will be overwritten. Implemented execute portion of save and load preset commands. + + + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=ernestlim8&sort=groupTitle&sortWithin=title&since=2020-08-14&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + +* **Project management**: + +* Set up team repository for SupperStrikers + + * Managed release `v1.2` by creating issues on GitHub + + * Adapted AddressBook to create MenuManager, OrderManager and its related classes ([#43](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/43)) + + * Refactor Command classes (Pull Request [#124](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/124)) + + * Refactor AddressBook to VendorManager (Pull Request [#214](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/214)) + + + +* **Enhancements to existing features**: + + * Updated the GUI to display the list of vendors in vendor mode and display the menu in menu mode (Pull requests [#43](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/43)) + * Updated the GUI to support images for the menu items (Pull requests [#196](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/196), [#205](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/205)) + * Wrote additional tests for existing features to increase test coverage (Pull requests [#52](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/52), [#119](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/119), [#124](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/124), [#191](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/191),[#218](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/218) ) + * Change Vendors in SupperStrikers to reflect actual vendors (Pull Request [#195](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/195)) + +* **Documentation**: + + * User Guide: + * Added documentation for the features `VendorCommand` (Pull Request [#135](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/135)) + + * Developer Guide: + + * Added implementation details of the `VendorCommand` feature. (Pull Request [#92](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/92), [#95](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/95)) + * Added implementation details of the `SwitchVendorCommand` feature. (Pull Request [#116](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/116)) + * Added implementation details and use cases of the `LoadPresetCommand` and `SavePresetCommand`. (Pull Request [#226](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/226)) + * Improved use cases portion of some commands (Pull Request [#122](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/122)) + * Improved UI, Storage and Logic sections (Pull Request [#208](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/208)) + +* **Community**: + + * PRs reviewed (with non-trivial review comments [#13](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/13)): + * Reported bugs and suggestions for other teams in the class (examples: [6](https://github.com/ernestlim8/ped/issues/6), [10](https://github.com/ernestlim8/ped/issues/10), [15](https://github.com/ernestlim8/ped/issues/15)) + diff --git a/docs/team/kendrewchan.md b/docs/team/kendrewchan.md new file mode 100644 index 00000000000..52ad1276a15 --- /dev/null +++ b/docs/team/kendrewchan.md @@ -0,0 +1,31 @@ +--- +layout: page +title: Kendrew's Project Portfolio Page +--- + +## Project: SupperStrikers + +SupperStrikers is a desktop food ordering application. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. + +* **New Feature**: Added OrderItem, Order Class. + * What it does: Used to represent food items as items the user takes as orders. +* **New Feature**: Added Sort Command + * What it does: Allows users to sort the menu items by name or price. + * Justification: Helps to improve user experience and make it easier to find particular food items. +* **New Feature**: Added a profile command to allow user to create their profile + * What it does: Adds an address and phone number tagged to the user, which will be shown when user submits their order. + * Justification: Address and phone number required for shop owners to contact the user. +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=kendrewchan) +* **Enhancements to existing features**: + * Refactored DeleteCommand (Pull requests [\#55](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/55)) + * Wrote tests for Order and OrderManagerStorage (Pull requests [\#77](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/77)) +* **Documentation**: + * User Guide: + * Standardised UserGuide with Messages shown on GUI and added target audience: [\#190](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/190) + * Added UserGuide for `profile` command: [\#202](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/202) + * Developer Guide: + * Added implementation details of order commands such as `add`, `remove` and `clear`: [\#110](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/110) +* **Community**: + * PRs reviewed (with non-trivial review comments): [\#199](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/199) diff --git a/docs/team/morninglit.md b/docs/team/morninglit.md new file mode 100644 index 00000000000..388f4670c66 --- /dev/null +++ b/docs/team/morninglit.md @@ -0,0 +1,36 @@ +--- +layout: page +title: Ambrose Liew's Project Portfolio Page +--- + +## Project: SupperStrikers + +SupperStrikers is a desktop supper ordering application used for ordering supper from supper stretch easily. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +Given below are my contributions to the project. + +* **New Feature**: Added preset delete command. (Pull request [#189](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/189)) + * What it does: Used to delete a user's existing preset saved in their presets.json file. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=morninglit) + +* **Enhancements to existing features**: + * Updated the GUI color scheme (Pull request [#201](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/201)) + * Updated the functionality of JsonSerializablePresetManager (Pull request [#142](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/142)) + * Finished the functionality of preset load command (Pull request [#142](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/142)) + * Updated the Phone class to accept only valid phone numbers (Pull request [#210](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/210)) + * Updated the functionality of sort command to be able to accept commands with no direction (toggle feature) (Pull request [#125](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/125)) + * Updated the find command to work for our menu items and tags (Pull requests [#107](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/107), [#120](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/120)) + * Wrote test cases for all preset commands (Pull requests [#212](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/212), [#217](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/217), [#219](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/219)) + * Fixed saving storage bugs (Pull requests [#93](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/93), [#147](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/147)) + +* **Documentation**: + * User Guide: + * Added documentation for preset commands (Pull request [#139](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/139)) + * Helped standardise the entire document: (Pull requests [#16](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/16), [#138](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/138), [#189](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/189)) + * Developer Guide: + * Standardise and fixed grammatical errors (Pull requests [#90](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/90), [#230](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/230)) + * Added last few remaining use cases (Pull request [#239](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/239)) + +* **Community**: + * PRs reviewed: (Pull requests [#144](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/144), [#234](https://github.com/AY2021S1-CS2103-T16-1/tp/pull/234)) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 44e7c4d1d7b..f520ff4bcf0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Tue Oct 27 10:19:07 SGT 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java index e5cfb161b73..e2034a7769b 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/address/MainApp.java @@ -2,6 +2,8 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import java.util.logging.Logger; @@ -12,22 +14,31 @@ import seedu.address.commons.core.Version; import seedu.address.commons.exceptions.DataConversionException; import seedu.address.commons.util.ConfigUtil; +import seedu.address.commons.util.FileUtil; import seedu.address.commons.util.StringUtil; import seedu.address.logic.Logic; import seedu.address.logic.LogicManager; -import seedu.address.model.AddressBook; import seedu.address.model.Model; import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.ReadOnlyUserPrefs; import seedu.address.model.UserPrefs; +import seedu.address.model.menu.MenuManager; +import seedu.address.model.menu.ReadOnlyMenuManager; +import seedu.address.model.order.OrderManager; import seedu.address.model.util.SampleDataUtil; -import seedu.address.storage.AddressBookStorage; -import seedu.address.storage.JsonAddressBookStorage; +import seedu.address.model.vendor.ReadOnlyVendorManager; +import seedu.address.model.vendor.VendorManager; +import seedu.address.storage.JsonPresetManagerStorage; +import seedu.address.storage.JsonProfileManagerStorage; import seedu.address.storage.JsonUserPrefsStorage; +import seedu.address.storage.JsonVendorManagerStorage; +import seedu.address.storage.MenuItemStorage; +import seedu.address.storage.PresetManagerStorage; +import seedu.address.storage.ProfileManagerStorage; import seedu.address.storage.Storage; import seedu.address.storage.StorageManager; import seedu.address.storage.UserPrefsStorage; +import seedu.address.storage.VendorManagerStorage; import seedu.address.ui.Ui; import seedu.address.ui.UiManager; @@ -36,7 +47,7 @@ */ public class MainApp extends Application { - public static final Version VERSION = new Version(0, 6, 0, true); + public static final Version VERSION = new Version(1, 3, 0, true); private static final Logger logger = LogsCenter.getLogger(MainApp.class); @@ -48,7 +59,7 @@ public class MainApp extends Application { @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing VendorManager ]==========================="); super.init(); AppParameters appParameters = AppParameters.parse(getParameters()); @@ -56,9 +67,13 @@ public void init() throws Exception { UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); UserPrefs userPrefs = initPrefs(userPrefsStorage); - AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath()); - storage = new StorageManager(addressBookStorage, userPrefsStorage); - + VendorManagerStorage vendorManagerStorage = new JsonVendorManagerStorage(userPrefs.getVendorManagerFilePath()); + PresetManagerStorage presetManagerStorage = new JsonPresetManagerStorage(userPrefs.getOrderManagerFilePath()); + ProfileManagerStorage profileManagerStorage = new JsonProfileManagerStorage( + userPrefs.getProfileManagerFilePath() + ); + storage = new StorageManager(vendorManagerStorage, userPrefsStorage, presetManagerStorage, + profileManagerStorage); initLogging(config); model = initModelManager(storage, userPrefs); @@ -69,28 +84,53 @@ public void init() throws Exception { } /** - * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book is not found, - * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book. + * Returns a {@code ModelManager} with the data from {@code storage}'s vendor book and {@code userPrefs}.
+ * The data from the sample vendor book will be used instead if {@code storage}'s vendor book is not found, + * or an empty vendor book will be used instead if errors occur when reading {@code storage}'s vendor book. */ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional vendorManagerOptional; + List> menuManagersOptional; + ReadOnlyVendorManager initialData; + List initialMenuManagers = new ArrayList<>(); + OrderManager initialOrderManager = new OrderManager(); try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Data file not found. Will be starting with a sample AddressBook"); + vendorManagerOptional = storage.readVendorManager(); + if (vendorManagerOptional.isEmpty()) { + logger.info("Data file not found. Will be starting with a sample VendorManager"); } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + initialData = vendorManagerOptional.orElseGet(SampleDataUtil::getSampleVendorManager); + menuManagersOptional = new MenuItemStorage().readMenuManagers(initialData.getVendorList()); + menuManagersOptional.forEach(x -> x.ifPresentOrElse(y -> + initialMenuManagers.add(new MenuManager(y)), () -> { + logger.info("Data file not found. Will be starting with an empty menu"); + initialMenuManagers.add(new MenuManager()); + } + )); } catch (DataConversionException e) { - logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Data file not in the correct format. Will be starting with an empty VendorManager"); + initialData = new VendorManager(); + } catch (IOException e) { + logger.warning("Problem while reading from the file. Will be starting with an empty VendorManager"); + initialData = new VendorManager(); + assert model != null; + } + try { + storage.saveVendorManager(initialData); + } catch (IOException e) { + logger.warning("Something unexpected occurred!"); + assert false; + } + try { + if (!FileUtil.isFileExists(storage.getPresetManagerFilePath())) { + storage.savePresetManager(new ArrayList<>(new ArrayList<>())); + } } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Something unexpected occurred!"); + assert false; } - return new ModelManager(initialData, userPrefs); + return new ModelManager(initialData, userPrefs, initialMenuManagers, initialOrderManager); } private void initLogging(Config config) { @@ -151,7 +191,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { + "Using default user prefs"); initializedPrefs = new UserPrefs(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); + logger.warning("Problem while reading from the file. Will be starting with an empty VendorManager"); initializedPrefs = new UserPrefs(); } @@ -167,13 +207,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting VendorManager " + MainApp.VERSION); ui.start(primaryStage); } @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("============================ [ Stopping Supper Strikers ] ============================="); try { storage.saveUserPrefs(model.getUserPrefs()); } catch (IOException e) { diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java index 1deb3a1e469..0998ae9b7cd 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/seedu/address/commons/core/Messages.java @@ -4,10 +4,46 @@ * Container for user visible messages. */ public class Messages { - public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; - public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; + public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format!\n%1$s"; + public static final String MESSAGE_INVALID_INDEX = "%s is not a non-zero unsigned integer."; + public static final String MESSAGE_INVALID_QUANTITY = "Quantity given is invalid."; + public static final String MESSAGE_INVALID_VENDOR_DISPLAYED_INDEX = "The vendor index provided is invalid"; + public static final String MESSAGE_INVALID_ORDERITEM_DISPLAYED_INDEX = "The order item index provided is invalid"; + public static final String MESSAGE_INVALID_ORDERITEM_DISPLAYED_QUANTITY = "The order item quantity " + + "provided is invalid"; + public static final String MESSAGE_INVALID_PRICE = "%s is not a non-negative unsigned real number."; + public static final String MESSAGE_PRICE_GREATER_THAN_LIMIT = "$%.2f is too large of a price!"; + public static final String MESSAGE_INVALID_INEQUALITY = "%s is not a valid inequality sign. It must be either " + + "\"<\", \"<=\", \">\", or \">=\" (without quotes). See User Guide for more info."; + + public static final String MESSAGE_VENDOR_NOT_SELECTED = "A vendor has not been selected yet," + + " please choose a vendor."; + public static final String MESSAGE_EMPTY_ORDER = "The order is currently empty," + + " please add an order."; + public static final String MESSAGE_INSUFFICIENT_ARGUMENTS = "%s command requires at least %s argument(s).\n%s"; + public static final String MESSAGE_TOO_MANY_ARGUMENTS = "%s command should not have more than %s arguments.\n%s"; + public static final String MESSAGE_CHAIN = + "1. You can further chain the current filter with another `find` or `price` or `sort` command.\n" + + "2. Filters can be reset with the `menu` command."; + public static final String MESSAGE_FOOD_LISTED_OVERVIEW = "%1$d food listed!\n" + MESSAGE_CHAIN; + public static final String MESSAGE_FOOD_LISTED_PRICE_CONTEXT = "%1$d food with price %2$s listed!\n" + + MESSAGE_CHAIN; + public static final String MESSAGE_FOOD_SORTED = "Food successfully sorted!\n" + MESSAGE_CHAIN; + public static final String MESSAGE_MENU_LIST = "All food listed!"; + public static final String MESSAGE_ORDERITEM_QUANTITY_EXCEED = "Cannot have more than 100 of the an order item."; + public static final String MESSAGE_EMPTY_PROFILE = "Please create a profile first before using the submit command."; + public static final String MESSAGE_AMBIGUOUS_COMMAND = "Ambiguous command. The following commands matches the " + + "prefix: %s"; + public static final String MESSAGE_PRESET_SAVE_NO_ORDER = "You have not added any items to your order to be saved!"; + public static final String MESSAGE_PRESET_NO_SAVED_PRESETS = "You have not saved any presets for this vendor!"; + public static final String MESSAGE_PRESET_OVERWRITE_SUCCESS = "Preset %s has been overwritten."; + public static final String MESSAGE_PRESET_SAVE_SUCCESS = "Preset %s has been saved."; + public static final String MESSAGE_PRESET_LOAD_SUCCESS = "Preset %s has been loaded."; + public static final String MESSAGE_PRESET_DELETE_SUCCESS = "Preset %s has been deleted."; + public static final String MESSAGE_PRESET_LOAD_ERROR = "Presets cannot be read."; + public static final String MESSAGE_PRESET_NOT_FOUND = "Preset %s cannot be found."; + public static final String MESSAGE_NO_INPUT_NAME = "You must specify a preset name to delete!"; + public static final String MESSAGE_EXISTING_TAG = "'%s' is already tagged to the order item!"; } diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java index 19124db485c..1ab7f11e90c 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java @@ -11,11 +11,4 @@ public IllegalValueException(String message) { super(message); } - /** - * @param message should contain relevant information on the failed constraint(s) - * @param cause of the main exception - */ - public IllegalValueException(String message, Throwable cause) { - super(message, cause); - } } diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/seedu/address/commons/util/JsonUtil.java index 8ef609f055d..313a82c5dcd 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/seedu/address/commons/util/JsonUtil.java @@ -107,7 +107,7 @@ public static T fromJsonString(String json, Class instanceClass) throws I * @return JSON data representation of the given class instance, in string */ public static String toJsonString(T instance) throws JsonProcessingException { - return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(instance); + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(instance) + "\n"; } /** diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/address/commons/util/StringUtil.java index 61cc8c9a1cb..7779a8fe4f8 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/address/commons/util/StringUtil.java @@ -7,6 +7,8 @@ import java.io.StringWriter; import java.util.Arrays; +import seedu.address.logic.commands.enums.Inequality; + /** * Helper functions for handling strings. */ @@ -27,15 +29,14 @@ public static boolean containsWordIgnoreCase(String sentence, String word) { requireNonNull(sentence); requireNonNull(word); - String preppedWord = word.trim(); + String preppedWord = word.toLowerCase().trim(); checkArgument(!preppedWord.isEmpty(), "Word parameter cannot be empty"); checkArgument(preppedWord.split("\\s+").length == 1, "Word parameter should be a single word"); - String preppedSentence = sentence; - String[] wordsInPreppedSentence = preppedSentence.split("\\s+"); + String[] wordsInPreppedSentence = sentence.toLowerCase().split("\\s+"); return Arrays.stream(wordsInPreppedSentence) - .anyMatch(preppedWord::equalsIgnoreCase); + .anyMatch(x -> x.contains(preppedWord)); } /** @@ -65,4 +66,31 @@ public static boolean isNonZeroUnsignedInteger(String s) { return false; } } + + /** + * Returns true if {@code s} represents a non-negative unsigned double + * + * @throws NullPointerException if {@code s} is null. + */ + public static boolean isNonNegativeUnsignedDouble(String s) { + requireNonNull(s); + + try { + double value = Double.parseDouble(s); + return value >= 0 && !s.startsWith("+"); + } catch (NumberFormatException nfe) { + return false; + } + } + + /** + * Returns true if {@code s} represents the symbol of inequality. + * + * @throws NullPointerException if {@code s} is null. + */ + public static boolean isInequality(String s) { + requireNonNull(s); + + return Inequality.get(s) != null; + } } diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java index 92cd8fa605a..2c001012c7c 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/address/logic/Logic.java @@ -7,8 +7,10 @@ import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.food.MenuItem; +import seedu.address.model.order.OrderItem; +import seedu.address.model.vendor.Vendor; + /** * API of the Logic component @@ -23,20 +25,19 @@ public interface Logic { */ CommandResult execute(String commandText) throws CommandException, ParseException; - /** - * Returns the AddressBook. - * - * @see seedu.address.model.Model#getAddressBook() - */ - ReadOnlyAddressBook getAddressBook(); + /** Returns an unmodifiable view of the filtered list of vendors */ + ObservableList getObservableVendorList(); - /** Returns an unmodifiable view of the filtered list of persons */ - ObservableList getFilteredPersonList(); + /** Returns an unmodifiable view of the filtered list of foods */ + ObservableList getFilteredMenuItemList(); + + /** Returns an unmodifiable view of the filtered list of foods */ + ObservableList getFilteredOrderItemList(); /** * Returns the user prefs' address book file path. */ - Path getAddressBookFilePath(); + Path getVendorManagerFilePath(); /** * Returns the user prefs' GUI settings. @@ -47,4 +48,6 @@ public interface Logic { * Set the user prefs' GUI settings. */ void setGuiSettings(GuiSettings guiSettings); + + boolean isSelected(); } diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 9d9c6d15bdc..04b6cb79d09 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -1,6 +1,5 @@ package seedu.address.logic; -import java.io.IOException; import java.nio.file.Path; import java.util.logging.Logger; @@ -10,23 +9,24 @@ import seedu.address.logic.commands.Command; import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.AddressBookParser; +import seedu.address.logic.parser.SupperStrikersParser; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.food.MenuItem; +import seedu.address.model.order.OrderItem; +import seedu.address.model.vendor.Vendor; import seedu.address.storage.Storage; /** * The main LogicManager of the app. */ public class LogicManager implements Logic { - public static final String FILE_OPS_ERROR_MESSAGE = "Could not save data to file: "; + public static final String FILE_OPS_ERROR_MESSAGE = "Could not load user data from file."; private final Logger logger = LogsCenter.getLogger(LogicManager.class); private final Model model; private final Storage storage; - private final AddressBookParser addressBookParser; + private final SupperStrikersParser supperStrikersParser; /** * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. @@ -34,39 +34,45 @@ public class LogicManager implements Logic { public LogicManager(Model model, Storage storage) { this.model = model; this.storage = storage; - addressBookParser = new AddressBookParser(); + this.supperStrikersParser = new SupperStrikersParser(); } + @Override public CommandResult execute(String commandText) throws CommandException, ParseException { logger.info("----------------[USER COMMAND][" + commandText + "]"); CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); - commandResult = command.execute(model); + Command command = supperStrikersParser.parseCommand(commandText); - try { - storage.saveAddressBook(model.getAddressBook()); - } catch (IOException ioe) { - throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); - } + commandResult = command.execute(model, storage); return commandResult; } @Override - public ReadOnlyAddressBook getAddressBook() { - return model.getAddressBook(); + public ObservableList getObservableVendorList() { + return model.getVendorManager().getVendorList(); + } + + @Override + public ObservableList getFilteredMenuItemList() { + return model.getFilteredMenuItemList(); + } + + @Override + public ObservableList getFilteredOrderItemList() { + return model.getObservableOrderItemList(); } @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); + public boolean isSelected() { + return model.getVendorIndex() != -1; } @Override - public Path getAddressBookFilePath() { - return model.getAddressBookFilePath(); + public Path getVendorManagerFilePath() { + return model.getVendorManagerFilePath(); } @Override diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java index 71656d7c5c8..def38264eba 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddCommand.java @@ -1,16 +1,17 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import javafx.collections.ObservableList; +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.address.model.food.MenuItem; +import seedu.address.model.order.OrderItem; +import seedu.address.storage.Storage; +//TODO change to fit vendor better /** * Adds a person to the address book. */ @@ -18,50 +19,68 @@ public class AddCommand extends Command { public static final String COMMAND_WORD = "add"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " - + "Parameters: " - + PREFIX_NAME + "NAME " - + PREFIX_PHONE + "PHONE " - + PREFIX_EMAIL + "EMAIL " - + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " " - + PREFIX_NAME + "John Doe " - + PREFIX_PHONE + "98765432 " - + PREFIX_EMAIL + "johnd@example.com " - + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; - - public static final String MESSAGE_SUCCESS = "New person added: %1$s"; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; - - private final Person toAdd; + //TODO: Update message to be closer to user guide + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an OrderItem to the OrderManager.\n" + + "Format: add INDEX [QUANTITY]\n" + + "- The INDEX refers to the index number shown on the displayed menu list.\n" + + "- INDEX must be a positive integer and must not exceed the size of the menu list.\n" + + "- QUANTITY can be specified to indicate the number of item to be added.\n" + + " Otherwise, it adds one quantity of the item at the specified index.\n" + + "Examples:\n" + + "add 1 1: add item at INDEX 1, of QUANTITY 1\n" + + "add 2 3: add item at INDEX 2, of QUANTITY 3\n" + + "add 1: add item at INDEX 1, of default QUANTITY 1"; + + public static final String MESSAGE_ADD_SUCCESS = "%s x%d has been added to your Order"; + + private final Index addIndex; + private final int quantity; + + /** + * Creates an AddCommand to add the specified {@code Food} + */ + public AddCommand(Index index) { + requireNonNull(index); + this.addIndex = index; + this.quantity = 1; + } /** - * Creates an AddCommand to add the specified {@code Person} + * Creates an AddCommand to add the specified {@code Food} */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; + public AddCommand(Index index, int quantity) { + requireNonNull(index); + this.addIndex = index; + this.quantity = quantity; } + @Override - public CommandResult execute(Model model) throws CommandException { + public CommandResult execute(Model model, Storage storage) throws CommandException { requireNonNull(model); + // Todo: This index value will be that of the chosen vendor. As of now the first menu on the list is chosen + assert model != null; + ObservableList menu = model.getFilteredMenuItemList(); + int index = addIndex.getZeroBased(); - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + if (menu.size() <= index) { + throw new CommandException(Messages.MESSAGE_INVALID_ORDERITEM_DISPLAYED_INDEX); } - model.addPerson(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + OrderItem orderItem = new OrderItem(menu.get(index), quantity); + model.addOrderItem(orderItem); + + return new CommandResult(String.format(MESSAGE_ADD_SUCCESS, orderItem.getName(), this.quantity)); } @Override public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof AddCommand // instanceof handles nulls - && toAdd.equals(((AddCommand) other).toAdd)); + && addIndex.equals(((AddCommand) other).addIndex)); } + } diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java index 9c86b1fa6e4..3ec3365d639 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java @@ -2,8 +2,10 @@ import static java.util.Objects.requireNonNull; -import seedu.address.model.AddressBook; +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; +import seedu.address.storage.Storage; /** * Clears the address book. @@ -11,13 +13,26 @@ public class ClearCommand extends Command { public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; - + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Removes everything from the Order.\n" + + "Format: clear\n" + + "Examples:\n" + + "clear: clears all items on current order"; + public static final String MESSAGE_SUCCESS = "Order has been cleared!"; + public static final String MESSAGE_EMPTY_ORDER = "Order is still empty!"; @Override - public CommandResult execute(Model model) { + public CommandResult execute(Model model, Storage storage) throws CommandException { requireNonNull(model); - model.setAddressBook(new AddressBook()); + requireNonNull(model.getOrderManager()); + + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + + if (model.getOrderSize() == 0) { + return new CommandResult(MESSAGE_EMPTY_ORDER); + } + model.clearOrder(); return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/address/logic/commands/Command.java index 64f18992160..d28ea2fa187 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/address/logic/commands/Command.java @@ -2,6 +2,7 @@ import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; +import seedu.address.storage.Storage; /** * Represents a command with hidden internal logic and the ability to be executed. @@ -12,9 +13,10 @@ public abstract class Command { * Executes the command and returns the result message. * * @param model {@code Model} which the command should operate on. + * @param storage * @return feedback message of the operation result for display * @throws CommandException If an error occurs during command execution. */ - public abstract CommandResult execute(Model model) throws CommandException; + public abstract CommandResult execute(Model model, Storage storage) throws CommandException; } diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java index 92f900b7916..9a326e063db 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/address/logic/commands/CommandResult.java @@ -11,19 +11,31 @@ public class CommandResult { private final String feedbackToUser; - /** Help information should be shown to the user. */ + /** + * Help information should be shown to the user. + */ private final boolean showHelp; - /** The application should exit. */ + /** + * The application should exit. + */ private final boolean exit; + /** + * Preset is being set in this command. + */ + private final boolean isPreset; + private final boolean isVendor; + /** * Constructs a {@code CommandResult} with the specified fields. */ - public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, boolean isPreset, boolean isVendor) { this.feedbackToUser = requireNonNull(feedbackToUser); this.showHelp = showHelp; this.exit = exit; + this.isPreset = isPreset; + this.isVendor = isVendor; } /** @@ -31,13 +43,17 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { * and other fields set to their default value. */ public CommandResult(String feedbackToUser) { - this(feedbackToUser, false, false); + this(feedbackToUser, false, false, false, false); } public String getFeedbackToUser() { return feedbackToUser; } + public boolean isVendor() { + return isVendor; + } + public boolean isShowHelp() { return showHelp; } @@ -46,6 +62,10 @@ public boolean isExit() { return exit; } + public boolean isPreset() { + return isPreset; + } + @Override public boolean equals(Object other) { if (other == this) { @@ -65,7 +85,12 @@ public boolean equals(Object other) { @Override public int hashCode() { - return Objects.hash(feedbackToUser, showHelp, exit); + return Objects.hash(feedbackToUser, showHelp, exit, isPreset); + } + + @Override + public String toString() { + return feedbackToUser; } } diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java deleted file mode 100644 index 02fd256acba..00000000000 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ /dev/null @@ -1,53 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; - -/** - * Deletes a person identified using it's displayed index from the address book. - */ -public class DeleteCommand extends Command { - - public static final String COMMAND_WORD = "delete"; - - public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Deletes the person identified by the index number used in the displayed person list.\n" - + "Parameters: INDEX (must be a positive integer)\n" - + "Example: " + COMMAND_WORD + " 1"; - - public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; - - private final Index targetIndex; - - public DeleteCommand(Index targetIndex) { - this.targetIndex = targetIndex; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - - if (targetIndex.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - Person personToDelete = lastShownList.get(targetIndex.getZeroBased()); - model.deletePerson(personToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof DeleteCommand // instanceof handles nulls - && targetIndex.equals(((DeleteCommand) other).targetIndex)); // state check - } -} diff --git a/src/main/java/seedu/address/logic/commands/DeletePresetCommand.java b/src/main/java/seedu/address/logic/commands/DeletePresetCommand.java new file mode 100644 index 00000000000..856451910a6 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeletePresetCommand.java @@ -0,0 +1,76 @@ +package seedu.address.logic.commands; + +import static seedu.address.logic.LogicManager.FILE_OPS_ERROR_MESSAGE; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.IntStream; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.preset.Preset; +import seedu.address.model.vendor.Name; +import seedu.address.storage.Storage; + +public class DeletePresetCommand extends PresetCommand { + + private final Name presetName; + + /** + * Creates a DeletePresetCommand to delete the specified preset {@code Name} + * @param presetName + */ + public DeletePresetCommand(Optional presetName) { + assert presetName.isPresent(); + this.presetName = presetName.get(); + } + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + try { + List> allLists = storage.readPresetManager() + .orElseThrow(() -> new CommandException(FILE_OPS_ERROR_MESSAGE)); + int currentIndex = model.getVendorIndex(); + if (currentIndex >= allLists.size()) { + IntStream.rangeClosed(allLists.size(), currentIndex) + .forEach(x -> allLists.add(new ArrayList<>())); + } + // check entire menu???? whether order is valid + List currentVendorPresets = allLists.get(model.getVendorIndex()); + + Optional preset = currentVendorPresets.stream() + .filter(x -> x.getName().equals(presetName.toString())) + .findFirst(); + if (preset.isEmpty()) { + throw new CommandException(String.format(Messages.MESSAGE_PRESET_NOT_FOUND, + presetName)); + } + currentVendorPresets.remove(preset.get()); + + storage.savePresetManager(allLists); + } catch (IOException | DataConversionException ioe) { + throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); + } + return new CommandResult(String.format(Messages.MESSAGE_PRESET_DELETE_SUCCESS, presetName), + false, false, true, false); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof DeletePresetCommand)) { + return false; + } + DeletePresetCommand other = (DeletePresetCommand) obj; + return presetName.equals(other.presetName); + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java deleted file mode 100644 index 7e36114902f..00000000000 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ /dev/null @@ -1,226 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.CollectionUtil; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Edits the details of an existing person in the address book. - */ -public class EditCommand extends Command { - - public static final String COMMAND_WORD = "edit"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified " - + "by the index number used in the displayed person list. " - + "Existing values will be overwritten by the input values.\n" - + "Parameters: INDEX (must be a positive integer) " - + "[" + PREFIX_NAME + "NAME] " - + "[" + PREFIX_PHONE + "PHONE] " - + "[" + PREFIX_EMAIL + "EMAIL] " - + "[" + PREFIX_ADDRESS + "ADDRESS] " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " 1 " - + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; - - public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; - public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book."; - - private final Index index; - private final EditPersonDescriptor editPersonDescriptor; - - /** - * @param index of the person in the filtered person list to edit - * @param editPersonDescriptor details to edit the person with - */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { - requireNonNull(index); - requireNonNull(editPersonDescriptor); - - this.index = index; - this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - - if (index.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - Person personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); - - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.setPerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); - } - - /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. - */ - private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { - assert personToEdit != null; - - Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); - Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); - Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); - Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); - Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditCommand)) { - return false; - } - - // state check - EditCommand e = (EditCommand) other; - return index.equals(e.index) - && editPersonDescriptor.equals(e.editPersonDescriptor); - } - - /** - * Stores the details to edit the person with. Each non-empty field value will replace the - * corresponding field value of the person. - */ - public static class EditPersonDescriptor { - private Name name; - private Phone phone; - private Email email; - private Address address; - private Set tags; - - public EditPersonDescriptor() {} - - /** - * Copy constructor. - * A defensive copy of {@code tags} is used internally. - */ - public EditPersonDescriptor(EditPersonDescriptor toCopy) { - setName(toCopy.name); - setPhone(toCopy.phone); - setEmail(toCopy.email); - setAddress(toCopy.address); - setTags(toCopy.tags); - } - - /** - * Returns true if at least one field is edited. - */ - public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); - } - - public void setName(Name name) { - this.name = name; - } - - public Optional getName() { - return Optional.ofNullable(name); - } - - public void setPhone(Phone phone) { - this.phone = phone; - } - - public Optional getPhone() { - return Optional.ofNullable(phone); - } - - public void setEmail(Email email) { - this.email = email; - } - - public Optional getEmail() { - return Optional.ofNullable(email); - } - - public void setAddress(Address address) { - this.address = address; - } - - public Optional
getAddress() { - return Optional.ofNullable(address); - } - - /** - * Sets {@code tags} to this object's {@code tags}. - * A defensive copy of {@code tags} is used internally. - */ - public void setTags(Set tags) { - this.tags = (tags != null) ? new HashSet<>(tags) : null; - } - - /** - * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - * Returns {@code Optional#empty()} if {@code tags} is null. - */ - public Optional> getTags() { - return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditPersonDescriptor)) { - return false; - } - - // state check - EditPersonDescriptor e = (EditPersonDescriptor) other; - - return getName().equals(e.getName()) - && getPhone().equals(e.getPhone()) - && getEmail().equals(e.getEmail()) - && getAddress().equals(e.getAddress()) - && getTags().equals(e.getTags()); - } - } -} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java index 3dd85a8ba90..0f5edd2dc06 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java @@ -1,6 +1,7 @@ package seedu.address.logic.commands; import seedu.address.model.Model; +import seedu.address.storage.Storage; /** * Terminates the program. @@ -12,8 +13,8 @@ public class ExitCommand extends Command { public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; @Override - public CommandResult execute(Model model) { - return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + public CommandResult execute(Model model, Storage storage) { + return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false, false); } } diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java index d6b19b0a0de..c777d421b36 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/seedu/address/logic/commands/FindCommand.java @@ -3,21 +3,27 @@ import static java.util.Objects.requireNonNull; import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.vendor.NameContainsKeywordsPredicate; +import seedu.address.storage.Storage; /** - * Finds and lists all persons in address book whose name contains any of the argument keywords. + * Finds and lists all vendors in address book whose name contains any of the argument keywords. * Keyword matching is case insensitive. */ public class FindCommand extends Command { public static final String COMMAND_WORD = "find"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of " - + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" - + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" - + "Example: " + COMMAND_WORD + " alice bob charlie"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds and lists all food items containing " + + "any of the specified keywords in their name\n" + + "Format: find KEYWORD [MORE_KEYWORDS]...\n" + + "- KEYWORD are NOT case-sensitive\n" + + "- KEYWORD filters tags as well.\n" + + "Examples:\n" + + "find milo: lists all food items containing the word 'milo' in their name.\n" + + "find milo dinosaur: lists all food items containing the word 'milo' or 'dinosaur' in their name."; private final NameContainsKeywordsPredicate predicate; @@ -26,11 +32,17 @@ public FindCommand(NameContainsKeywordsPredicate predicate) { } @Override - public CommandResult execute(Model model) { + public CommandResult execute(Model model, Storage storage) throws CommandException { requireNonNull(model); - model.updateFilteredPersonList(predicate); + + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + + model.updateFilteredMenuItemList(predicate); return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); + String.format(Messages.MESSAGE_FOOD_LISTED_OVERVIEW, + model.getFilteredMenuItemListSize()), false, false, false, true); } @Override diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java index bf824f91bd0..54cc2b38e9d 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java @@ -1,6 +1,7 @@ package seedu.address.logic.commands; import seedu.address.model.Model; +import seedu.address.storage.Storage; /** * Format full help instructions for every command for display. @@ -15,7 +16,8 @@ public class HelpCommand extends Command { public static final String SHOWING_HELP_MESSAGE = "Opened help window."; @Override - public CommandResult execute(Model model) { - return new CommandResult(SHOWING_HELP_MESSAGE, true, false); + public CommandResult execute(Model model, Storage storage) { + return new CommandResult(SHOWING_HELP_MESSAGE, true, + false, false, false); } } diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java deleted file mode 100644 index 84be6ad2596..00000000000 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ /dev/null @@ -1,24 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import seedu.address.model.Model; - -/** - * Lists all persons in the address book to the user. - */ -public class ListCommand extends Command { - - public static final String COMMAND_WORD = "list"; - - public static final String MESSAGE_SUCCESS = "Listed all persons"; - - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/main/java/seedu/address/logic/commands/LoadPresetCommand.java b/src/main/java/seedu/address/logic/commands/LoadPresetCommand.java new file mode 100644 index 00000000000..72da4f2b0bd --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/LoadPresetCommand.java @@ -0,0 +1,86 @@ +package seedu.address.logic.commands; + +import static seedu.address.logic.LogicManager.FILE_OPS_ERROR_MESSAGE; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.order.OrderItem; +import seedu.address.model.preset.Preset; +import seedu.address.model.vendor.Name; +import seedu.address.storage.Storage; + +public class LoadPresetCommand extends PresetCommand { + + private final Name presetName; + private final boolean displayAllPresets; + + /** + * Creates a LoadPresetCommand to load the specified preset {@code Name} + * @param presetName + */ + public LoadPresetCommand(Optional presetName) { + this.displayAllPresets = presetName.isEmpty(); + this.presetName = presetName.orElseGet(() -> new Name("Invalid")); + } + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + + try { + List> allLists = storage.readPresetManager() + .orElseThrow(() -> new CommandException(FILE_OPS_ERROR_MESSAGE)); + + int currentIndex = model.getVendorIndex(); + if (currentIndex >= allLists.size()) { + throw displayAllPresets + ? new CommandException(Messages.MESSAGE_PRESET_NO_SAVED_PRESETS) + : new CommandException(String.format(Messages.MESSAGE_PRESET_NOT_FOUND, presetName)); + } + if (displayAllPresets) { + List vendorPresets = allLists.get(currentIndex); + if (vendorPresets.isEmpty()) { + throw new CommandException(Messages.MESSAGE_PRESET_NO_SAVED_PRESETS); + } + StringBuilder message = new StringBuilder(); + for (Preset preset:vendorPresets) { + message.append(preset.getName()).append(", "); + } + String removeComma = message.toString().trim(); + removeComma = removeComma.substring(0, removeComma.length() - 1); + return new CommandResult(PresetCommand.MESSAGE_DISPLAY_ALL_PRESETS + removeComma, + false, false, true, false); + } + List orderItems = allLists.get(currentIndex) + .stream() + .filter(preset -> preset.getName().equals(presetName.toString())) + .findFirst() + .map(Preset::getOrderItems) + .orElseThrow(() -> new CommandException(String.format(Messages.MESSAGE_PRESET_NOT_FOUND, + presetName))); + + model.setOrder(orderItems); + + } catch (DataConversionException | IOException | IndexOutOfBoundsException e) { + throw new CommandException(Messages.MESSAGE_PRESET_LOAD_ERROR); + } + return new CommandResult(String.format(Messages.MESSAGE_PRESET_LOAD_SUCCESS, presetName), + false, false, true, false); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof LoadPresetCommand // instanceof handles nulls + && this.presetName.equals(((LoadPresetCommand) other).presetName)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/MenuCommand.java b/src/main/java/seedu/address/logic/commands/MenuCommand.java new file mode 100644 index 00000000000..1ba3cf29da0 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/MenuCommand.java @@ -0,0 +1,26 @@ +package seedu.address.logic.commands; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.storage.Storage; + +public class MenuCommand extends Command { + + public static final String COMMAND_WORD = "menu"; + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + model.showDefaultMenu(); + return new CommandResult( + Messages.MESSAGE_MENU_LIST, false, false, false, true); + } + + @Override + public boolean equals(Object obj) { + return obj == this || obj instanceof MenuCommand; + } +} diff --git a/src/main/java/seedu/address/logic/commands/PresetCommand.java b/src/main/java/seedu/address/logic/commands/PresetCommand.java new file mode 100644 index 00000000000..4ba330b9690 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/PresetCommand.java @@ -0,0 +1,28 @@ +package seedu.address.logic.commands; + +public abstract class PresetCommand extends Command { + + public static final String COMMAND_WORD = "preset"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Saves or Loads a preset of the user's supper order.\n" + + "Format: preset MODE [NAME]\n" + + "- MODE dictates what the system will perform for the user's supper orders," + + " represented by the formats:\n" + + " save: Used to save a preset." + + " (If used without a NAME, will save with a default preset name of 'MyPreset')\n" + + " load: Used to load a preset." + + " (If used without a NAME, will list all saved presets)\n" + + "- NAME is the preset name which the system will save the preset as," + + " or tries to load the given preset by the given name.\n" + + " - if NAME already exists, the new preset will overwrite the existing preset.\n" + + " - NAME is Case-Sensitive.\n" + + " - NAME is Vendor Specific, and is unique to each vendor.\n" + + "Examples:\n" + + "preset save: saves the user's supper order with the default preset name.\n" + + "preset load MyPreset: loads the current default preset if it exists.\n" + + "preset save vegan: save the user's supper order with a preset name of 'vegan'.\n" + + "preset load vegan: loads the preset supper order with the preset name 'vegan'."; + + public static final String MESSAGE_DISPLAY_ALL_PRESETS = "Here are your list of saved presets!\n"; +} diff --git a/src/main/java/seedu/address/logic/commands/PriceCommand.java b/src/main/java/seedu/address/logic/commands/PriceCommand.java new file mode 100644 index 00000000000..790ebaa5a45 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/PriceCommand.java @@ -0,0 +1,54 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.food.PriceWithinRangePredicate; +import seedu.address.storage.Storage; + +public class PriceCommand extends Command { + + public static final String COMMAND_WORD = "price"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Filters all food item within a specified price range.\n" + + "Format: price INEQUALITY PRICE\n" + + "- INEQUALITY is an inequality sign, of the below formats:\n" + + " <: Strictly less than\n" + + " <=: Less than or Equal to\n" + + " >: Greater than\n" + + " >=: Greater than or Equal to\n" + + "- PRICE must be a non-negative real number.\n" + + "Examples:\n" + + "price < 3: lists all food item with price less than $3.\n" + + "price >= 2: lists all food item with price from $2."; + + private final PriceWithinRangePredicate predicate; + + public PriceCommand(PriceWithinRangePredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + requireNonNull(model); + + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + + model.updateFilteredMenuItemList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_FOOD_LISTED_PRICE_CONTEXT, + model.getFilteredMenuItemListSize(), predicate), false, false, false, true); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof PriceCommand // instanceof handles nulls + && predicate.equals(((PriceCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/ProfileCommand.java b/src/main/java/seedu/address/logic/commands/ProfileCommand.java new file mode 100644 index 00000000000..c798b21fcf3 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ProfileCommand.java @@ -0,0 +1,64 @@ +package seedu.address.logic.commands; + +import java.io.IOException; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.profile.Profile; +import seedu.address.model.vendor.Address; +import seedu.address.model.vendor.Phone; +import seedu.address.storage.Storage; + + +public class ProfileCommand extends Command { + + public static final String COMMAND_WORD = "profile"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Add delivery address and phone number for submission.\n" + + "Format: profile PHONE ADDRESS\n" + + "- PHONE represents your contact number and must be at least 3 digits long.\n" + + "- ADDRESS represents your delivery address.\n" + + "Examples:\n" + + "profile 92030888 25 Lower Kent Ridge Rd, Singapore 119081: " + + "Saves your address as '25 Lower Kent Ridge Rd, Singapore 119081' and phone number as '92030888'"; + + public static final String MESSAGE_SUCCESS = "Profile has been set!"; + + private final Address address; + private final Phone phone; + + /** + * ProfileCommand constructor + * @param address address of Profile as String + * @param phone phone number of Profile as String + */ + public ProfileCommand(Phone phone, Address address) { + this.phone = phone; + this.address = address; + } + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + try { + Profile profile = new Profile(phone, address); + storage.saveProfileManager(profile); + } catch (IOException ie) { + throw new CommandException(ProfileCommand.MESSAGE_USAGE); + } + + return new CommandResult(ProfileCommand.MESSAGE_SUCCESS); + } + + @Override + public String toString() { + return "Profile added:\nAddress: " + address + "\nPhone Number: " + phone; + } + + @Override + public boolean equals(Object other) { + return other != null && other instanceof ProfileCommand + && ((ProfileCommand) other).address.equals(address) + && ((ProfileCommand) other).phone.equals(phone); + } +} diff --git a/src/main/java/seedu/address/logic/commands/RemoveCommand.java b/src/main/java/seedu/address/logic/commands/RemoveCommand.java new file mode 100644 index 00000000000..f22ad8690d5 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/RemoveCommand.java @@ -0,0 +1,93 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import javafx.collections.ObservableList; +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.order.OrderItem; +import seedu.address.storage.Storage; + +/** + * Removes a order item identified using it's displayed index from the address book. + */ +public class RemoveCommand extends Command { + + public static final String COMMAND_WORD = "remove"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Removes the specified item from the supper order.\n" + + "Format: remove INDEX [QUANTITY]\n" + + "- INDEX refers to the index number shown in the displayed supper order list.\n" + + "- INDEX must be a positive integer and must not exceed the size of the supper order list.\n" + + "- QUANTITY can be specified to indicate the number of item to be deleted." + + " Otherwise, it deletes all quantities of the item at the specified index.\n" + + "Examples:\n" + + "remove 2: remove item at INDEX 2.\n" + + "remove 1 2: remove item at INDEX 1, of quantity 2.\n"; + + public static final String MESSAGE_REMOVE_ORDERITEM_SUCCESS = "Removed order item: %1$s"; + + private final Index targetIndex; + private final int quantity; + private final boolean specified; + + /** + * Creates a RemoveCommand to remove the OrderItem at the specified {@code targetIndex} and remove all its quantity + */ + public RemoveCommand(Index targetIndex) { + requireNonNull(targetIndex); + this.targetIndex = targetIndex; + this.quantity = Integer.MAX_VALUE; + specified = false; + } + + /** + * Creates a RemoveCommand to remove the OrderItem at the specified {@code targetIndex} and remove its quantity by + * the specified {@code quantity} + */ + public RemoveCommand(Index targetIndex, int quantity) { + requireNonNull(targetIndex); + assert quantity > 0; + this.targetIndex = targetIndex; + this.quantity = quantity; + specified = true; + } + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + requireNonNull(model); + + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + + ObservableList order = model.getObservableOrderItemList(); + int index = targetIndex.getZeroBased(); + + if (order.size() <= index) { + throw new CommandException(Messages.MESSAGE_INVALID_ORDERITEM_DISPLAYED_INDEX); + } else if (model.getOrderItemQuantity(index) < quantity && specified) { + throw new CommandException(Messages.MESSAGE_INVALID_ORDERITEM_DISPLAYED_QUANTITY); + } + OrderItem oldItem = order.get(index); + + OrderItem orderItem = new OrderItem(order.get(index), quantity); + model.removeOrderItem(orderItem); + + if (order.size() == index || !order.get(index).isSameOrderItemDescription(orderItem)) { + orderItem = oldItem; + } + + return new CommandResult(String.format(MESSAGE_REMOVE_ORDERITEM_SUCCESS, orderItem)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof RemoveCommand // instanceof handles nulls + && targetIndex.equals(((RemoveCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/SavePresetCommand.java b/src/main/java/seedu/address/logic/commands/SavePresetCommand.java new file mode 100644 index 00000000000..c6d435b3d5d --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SavePresetCommand.java @@ -0,0 +1,76 @@ +package seedu.address.logic.commands; + +import static seedu.address.logic.LogicManager.FILE_OPS_ERROR_MESSAGE; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.IntStream; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.preset.Preset; +import seedu.address.model.vendor.Name; +import seedu.address.storage.Storage; + +public class SavePresetCommand extends PresetCommand { + + private final Name presetName; + + public SavePresetCommand(Optional presetName) { + this.presetName = presetName.orElseGet(() -> new Name("MyPreset")); + } + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + if (model.getOrderSize() == 0) { + throw new CommandException(Messages.MESSAGE_PRESET_SAVE_NO_ORDER); + } + + try { + List> allLists = storage.readPresetManager() + .orElseThrow(() -> new CommandException(FILE_OPS_ERROR_MESSAGE)); + int currentIndex = model.getVendorIndex(); + if (currentIndex >= allLists.size()) { + IntStream.rangeClosed(allLists.size(), currentIndex) + .forEach(x -> allLists.add(new ArrayList<>())); + } + // check entire menu???? whether order is valid + List currentVendorPresets = allLists.get(model.getVendorIndex()); + + Preset newPreset = new Preset(presetName.toString(), + model.getObservableOrderItemList()); + + Optional preset = currentVendorPresets.stream() + .filter(x -> x.getName().equals(presetName.toString())) + .findFirst(); + + String message = Messages.MESSAGE_PRESET_SAVE_SUCCESS; + if (preset.isPresent()) { + currentVendorPresets.remove(preset.get()); + message = Messages.MESSAGE_PRESET_OVERWRITE_SUCCESS; + } + + currentVendorPresets.add(newPreset); + + storage.savePresetManager(allLists); + + return new CommandResult(String.format(message, presetName), false, false, true, false); + } catch (IOException | DataConversionException ioe) { + throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SavePresetCommand // instanceof handles nulls + && presetName.equals(((SavePresetCommand) other).presetName)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/SortCommand.java b/src/main/java/seedu/address/logic/commands/SortCommand.java new file mode 100644 index 00000000000..87f0996f098 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SortCommand.java @@ -0,0 +1,70 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.storage.Storage; + +/** + * Sorts the current selected menu. + */ +public class SortCommand extends Command { + public static final String NAME = "n"; + public static final String PRICE = "p"; + + public static final String COMMAND_WORD = "sort"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sorts the menu by either price or name.\n" + + "Format: sort MODE [DIRECTION]\n" + + "- MODE can either be \"n\" or \"p\" (without quotes), indicating sort by name or price respectively.\n" + + "- DIRECTION can either be \"a\" or \"d\" (without quotes), indicating sort in ascending or descending " + + "order.\n" + + "- If DIRECTION is not specified or if direction is \"t\" (without quotes), it will" + + " be treated as a toggle," + + " and ascending direction will be sorted as descending order and vice versa.\n" + + "Examples:\n" + + "sort n a: sorts the menu by name in ascending direction.\n" + + "sort p: sorts the menu by price in opposite direction as last sorted."; + + private final String sortedBy; + private final boolean ascending; + private final boolean toggle; + + + /** + * Creates an SortCommand to sort the current menu with the respective sort type + */ + public SortCommand(String sortedBy, String ascending) { + assert sortedBy.equals(SortCommand.NAME) || sortedBy.equals(SortCommand.PRICE); + assert ascending.equals("a") || ascending.equals("d") || ascending.equals("t"); + + this.sortedBy = sortedBy; + if (ascending.equals("t")) { + this.toggle = true; + this.ascending = false; + } else { + this.toggle = false; + this.ascending = ascending.equals("a"); + } + } + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + requireNonNull(model); + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + model.sortMenuItemBy(sortedBy, ascending, toggle); + return new CommandResult(Messages.MESSAGE_FOOD_SORTED, false, false, false, true); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SortCommand // instanceof handles nulls + && sortedBy.equals(((SortCommand) other).sortedBy)) + && ascending == ((SortCommand) other).ascending + && toggle == ((SortCommand) other).toggle; + } +} diff --git a/src/main/java/seedu/address/logic/commands/SubmitCommand.java b/src/main/java/seedu/address/logic/commands/SubmitCommand.java new file mode 100644 index 00000000000..3197e97e3b6 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SubmitCommand.java @@ -0,0 +1,86 @@ +package seedu.address.logic.commands; + +import java.awt.HeadlessException; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; +import java.util.Optional; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.order.Order; +import seedu.address.model.order.OrderItem; +import seedu.address.model.profile.Profile; +import seedu.address.model.vendor.Address; +import seedu.address.model.vendor.Phone; +import seedu.address.storage.Storage; + +public class SubmitCommand extends Command { + + public static final String COMMAND_WORD = "submit"; + public static final String CLIPBOARD_SUCCESS_MESSAGE = "Successfully copied to clipboard!\n"; + public static final String ESTIMATE_TOTAL_MESSAGE = "Estimated total: $%.2f\n"; + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + Order order = new Order(); + order.setOrderItems(model.getObservableOrderItemList()); + double total = order.getTotal(); + + if (total == 0.0) { + throw new CommandException(Messages.MESSAGE_EMPTY_ORDER); + } + + StringBuilder profileText = new StringBuilder(); + try { + Optional optionalProfile = storage.readProfileManager(); + if (optionalProfile.isEmpty()) { + throw new CommandException(Messages.MESSAGE_EMPTY_PROFILE); + } else { + Profile profile = optionalProfile.get(); + Address address = profile.getAddress(); + Phone phone = profile.getPhone(); + profileText.append(String.format("Address: %s\n", address.toString())); + profileText.append(String.format("Phone: %s\n", phone.toString())); + profileText.append("---------------------------------\n"); + } + } catch (DataConversionException de) { + throw new CommandException(Messages.MESSAGE_EMPTY_PROFILE); + } + + StringBuilder orderText = new StringBuilder(); + orderText.append(profileText); + for (OrderItem orderItem: order) { + orderText.append(orderItem.toOrderText()); + } + + boolean copySuccess = true; + try { + StringSelection stringSelection = new StringSelection(orderText.toString()); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(stringSelection, null); + } catch (HeadlessException e) { + copySuccess = false; + e.printStackTrace(); + } + + StringBuilder feedback = new StringBuilder(); + if (copySuccess) { + feedback.append(CLIPBOARD_SUCCESS_MESSAGE); + } + feedback.append(orderText.toString()); + feedback.append("\n" + String.format(ESTIMATE_TOTAL_MESSAGE, order.getTotal())); + + return new CommandResult(feedback.toString()); + } + + @Override + public boolean equals(Object obj) { + return obj == this || obj instanceof SubmitCommand; + } +} diff --git a/src/main/java/seedu/address/logic/commands/SwitchVendorCommand.java b/src/main/java/seedu/address/logic/commands/SwitchVendorCommand.java new file mode 100644 index 00000000000..1e844862d66 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SwitchVendorCommand.java @@ -0,0 +1,74 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import javafx.collections.ObservableList; +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.vendor.Vendor; +import seedu.address.storage.Storage; + +/** + * Selects a Vendor to order from. + */ +public class SwitchVendorCommand extends VendorCommand { + + public static final String COMMAND_WORD = "vendor"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Selects a Vendor in the VendorManager. " + + "Parameters: " + + " Index of Vendor"; + + public static final String MESSAGE_SELECT_VENDOR_SUCCESS = "The vendor %s, has been selected."; + public static final String MESSAGE_SELECT_VENDOR_SAME = "You are already on the vendor %s,\n" + + "1. Use the clear command if you wish to clear your current order.\n" + + "2. Use the menu command if you wish to reset to the original menu.\n" + + "3. Use the vendor command if you wish to reselect vendors"; + + private final Index vendorIndex; + + /** + * Creates a VendorCommand to select the vendor at the specified {@code index} of the VendorManager + */ + public SwitchVendorCommand(Index index) { + requireNonNull(index); + this.vendorIndex = index; + } + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + requireNonNull(model); + ObservableList vendors = model.getObservableVendorList(); + int index = vendorIndex.getZeroBased(); + + if (vendors.size() <= index) { + throw new CommandException(Messages.MESSAGE_INVALID_VENDOR_DISPLAYED_INDEX); + } + + int oldIndex = model.getVendorIndex(); + model.selectVendor(index); + + Vendor currVendor = vendors.get(index); + String message; + boolean isSameVendor = false; + if (oldIndex != index) { + model.resetOrder(); + message = String.format(MESSAGE_SELECT_VENDOR_SUCCESS, currVendor.getName()); + } else { + message = String.format(MESSAGE_SELECT_VENDOR_SAME, currVendor.getName()); + isSameVendor = true; + } + + return new CommandResult(message, false, false, false, !isSameVendor); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SwitchVendorCommand // instanceof handles nulls + && vendorIndex.equals(((SwitchVendorCommand) other).vendorIndex)); + } + +} diff --git a/src/main/java/seedu/address/logic/commands/TagCommand.java b/src/main/java/seedu/address/logic/commands/TagCommand.java new file mode 100644 index 00000000000..7b44c24441d --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/TagCommand.java @@ -0,0 +1,70 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import javafx.collections.ObservableList; +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.order.OrderItem; +import seedu.address.model.tag.Tag; +import seedu.address.storage.Storage; + +public class TagCommand extends Command { + public static final String COMMAND_WORD = "tag"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Tags an order item with remark.\n" + + "Format: tag INDEX REMARK\n" + + "- The INDEX refers to the index number of the order item.\n" + + "- INDEX must be positive number and must not exceed the size of the order list.\n" + + "- REMARK is any non-empty string\n" + + "Examples:\n" + + "tag 2 1 no ice: tags order item INDEX 2, with tag \"1 no ice\""; + public static final String MESSAGE_TAG_SUCCESS = "Tag \"%s\" has been added to order item %d.\n"; + + private final Index tagIndex; + private final Tag tag; + + /** + * Creates a {@code tag} for the order item at index {@code tagIndex}. + * + * @param tagIndex + * @param tag + */ + public TagCommand(Index tagIndex, Tag tag) { + requireAllNonNull(tagIndex, tag); + this.tagIndex = tagIndex; + this.tag = tag; + } + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + requireNonNull(model); + assert model != null; + ObservableList order = model.getObservableOrderItemList(); + int index = tagIndex.getZeroBased(); + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + if (order.size() <= index) { + throw new CommandException(Messages.MESSAGE_INVALID_ORDERITEM_DISPLAYED_INDEX); + } + OrderItem orderItem = order.get(index); + if (orderItem.getTags().contains(tag)) { + throw new CommandException(String.format(Messages.MESSAGE_EXISTING_TAG, tag.tagName)); + } + model.tagOrderItem(orderItem, tag); + return new CommandResult(String.format(MESSAGE_TAG_SUCCESS, tag.tagName, index + 1)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TagCommand // instanceof handles nulls + && tagIndex.equals(((TagCommand) other).tagIndex)) + && tag.equals(((TagCommand) other).tag); + } +} diff --git a/src/main/java/seedu/address/logic/commands/TotalCommand.java b/src/main/java/seedu/address/logic/commands/TotalCommand.java new file mode 100644 index 00000000000..42f924ee3f3 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/TotalCommand.java @@ -0,0 +1,34 @@ +package seedu.address.logic.commands; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.order.Order; +import seedu.address.storage.Storage; + +public class TotalCommand extends Command { + + public static final String COMMAND_WORD = "total"; + + public static final String MESSAGE_RESULT = "Total is $%.2f."; + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + if (model.getOrderSize() == 0) { + throw new CommandException(Messages.MESSAGE_EMPTY_ORDER); + } + Order order = new Order(); + order.setOrderItems(model.getObservableOrderItemList()); + double total = order.getTotal(); + + return new CommandResult(String.format(MESSAGE_RESULT, total)); + } + + @Override + public boolean equals(Object obj) { + return obj == this || obj instanceof TotalCommand; + } +} diff --git a/src/main/java/seedu/address/logic/commands/UndoCommand.java b/src/main/java/seedu/address/logic/commands/UndoCommand.java new file mode 100644 index 00000000000..4cd6624d3b7 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/UndoCommand.java @@ -0,0 +1,35 @@ +package seedu.address.logic.commands; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.storage.Storage; + +public class UndoCommand extends Command { + + public static final String COMMAND_WORD = "undo"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Undoes the last change to the order."; + + public static final String MESSAGE_UNDO_SUCCESS = "Successfully undone last change."; + public static final String MESSAGE_UNDO_EMPTY = "No changes left to undo."; + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + + if (model.getOrderHistorySize() <= 1) { + return new CommandResult(MESSAGE_UNDO_EMPTY); + } + model.undoOrder(); + return new CommandResult(MESSAGE_UNDO_SUCCESS); + } + + @Override + public boolean equals(Object obj) { + return obj == this || obj instanceof UndoCommand; + } +} diff --git a/src/main/java/seedu/address/logic/commands/UntagCommand.java b/src/main/java/seedu/address/logic/commands/UntagCommand.java new file mode 100644 index 00000000000..5a3d694a691 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/UntagCommand.java @@ -0,0 +1,56 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import javafx.collections.ObservableList; +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.order.OrderItem; +import seedu.address.storage.Storage; + +public class UntagCommand extends Command { + public static final String COMMAND_WORD = "untag"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Clears all the tag of an order item.\n" + + "Format: untag INDEX\n"; + public static final String MESSAGE_UNTAG_SUCCESS = "Cleared tag(s) of order item %d.\n"; + + private final Index tagIndex; + + /** + * Creates a {@code tag} for the order item at index {@code tagIndex}. + * + * @param tagIndex + */ + public UntagCommand(Index tagIndex) { + requireNonNull(tagIndex); + this.tagIndex = tagIndex; + } + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + requireNonNull(model); + assert model != null; + ObservableList order = model.getObservableOrderItemList(); + int index = tagIndex.getZeroBased(); + if (!model.isSelected()) { + throw new CommandException(Messages.MESSAGE_VENDOR_NOT_SELECTED); + } + if (order.size() <= index) { + throw new CommandException(Messages.MESSAGE_INVALID_ORDERITEM_DISPLAYED_INDEX); + } + OrderItem orderItem = order.get(index); + model.untagOrderItem(orderItem); + return new CommandResult(String.format(MESSAGE_UNTAG_SUCCESS, index + 1)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UntagCommand // instanceof handles nulls + && tagIndex.equals(((UntagCommand) other).tagIndex)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/VendorCommand.java b/src/main/java/seedu/address/logic/commands/VendorCommand.java new file mode 100644 index 00000000000..1e6f4c2e310 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/VendorCommand.java @@ -0,0 +1,23 @@ +package seedu.address.logic.commands; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.storage.Storage; + +public class VendorCommand extends Command { + public static final String COMMAND_WORD = "vendor"; + public static final String MESSAGE_RESET_VENDOR_SUCCESS = "Vendors have been displayed."; + + @Override + public CommandResult execute(Model model, Storage storage) throws CommandException { + model.selectVendor(-1); + model.clearOrder(); + return new CommandResult(MESSAGE_RESET_VENDOR_SUCCESS, false, false, false, true); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof VendorCommand); // instanceof handles nulls + } +} diff --git a/src/main/java/seedu/address/logic/commands/enums/Inequality.java b/src/main/java/seedu/address/logic/commands/enums/Inequality.java new file mode 100644 index 00000000000..c955ee09577 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/enums/Inequality.java @@ -0,0 +1,31 @@ +package seedu.address.logic.commands.enums; + +public enum Inequality { + LESSER_THAN("<"), + LESSER_THAN_OR_EQUAL_TO("<="), + GREATER_THAN(">"), + GREATER_THAN_OR_EQUAL_TO(">="); + + private String s; + Inequality(String s) { + this.s = s; + } + + public boolean matches(String s) { + return this.s.equals(s); + } + + public static Inequality get(String s) { + for (Inequality ineq: Inequality.values()) { + if (ineq.matches(s)) { + return ineq; + } + } + return null; + } + + @Override + public String toString() { + return s; + } +} diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java index a16bd14f2cd..8b8bad8338e 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java @@ -14,4 +14,17 @@ public CommandException(String message) { public CommandException(String message, Throwable cause) { super(message, cause); } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (!(obj instanceof CommandException)) { + return false; + } + + return ((CommandException) obj).getMessage().equals(getMessage()); + } } diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java index 3b8bfa035e8..310fe96f7df 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java @@ -1,23 +1,8 @@ package seedu.address.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import java.util.Set; -import java.util.stream.Stream; - +import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.AddCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; /** * Parses input arguments and creates a new AddCommand object @@ -29,32 +14,21 @@ public class AddCommandParser implements Parser { * and returns an AddCommand object for execution. * @throws ParseException if the user input does not conform the expected format */ + @Override public AddCommand parse(String args) throws ParseException { - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); - - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) - || !argMultimap.getPreamble().isEmpty()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + String trimArgs = args.trim(); + String finalArgs = trimArgs.replaceAll("( )+", " "); + String[] argsArr = finalArgs.split(" "); + ParserUtil.checkArgsLength(argsArr, AddCommand.COMMAND_WORD, AddCommand.MESSAGE_USAGE, 1, 2); + Index index = ParserUtil.parseIndex(argsArr[0], "Menu Index"); + + if (argsArr.length == 1) { + return new AddCommand(index); } - Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); - Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); - Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); - Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); - Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); - - Person person = new Person(name, phone, email, address, tagList); - - return new AddCommand(person); + int quantity = ParserUtil.parseQuantity(argsArr[1]); + return new AddCommand(index, quantity); } +} - /** - * Returns true if none of the prefixes contains empty {@code Optional} values in the given - * {@code ArgumentMultimap}. - */ - private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { - return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); - } -} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java deleted file mode 100644 index 1e466792b46..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.ClearCommand; -import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.ExitCommand; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.parser.exceptions.ParseException; - -/** - * Parses user input. - */ -public class AddressBookParser { - - /** - * Used for initial separation of command word and args. - */ - private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); - - /** - * Parses user input into command for execution. - * - * @param userInput full user input string - * @return the command based on the user input - * @throws ParseException if the user input does not conform the expected format - */ - public Command parseCommand(String userInput) throws ParseException { - final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); - if (!matcher.matches()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); - } - - final String commandWord = matcher.group("commandWord"); - final String arguments = matcher.group("arguments"); - switch (commandWord) { - - case AddCommand.COMMAND_WORD: - return new AddCommandParser().parse(arguments); - - case EditCommand.COMMAND_WORD: - return new EditCommandParser().parse(arguments); - - case DeleteCommand.COMMAND_WORD: - return new DeleteCommandParser().parse(arguments); - - case ClearCommand.COMMAND_WORD: - return new ClearCommand(); - - case FindCommand.COMMAND_WORD: - return new FindCommandParser().parse(arguments); - - case ListCommand.COMMAND_WORD: - return new ListCommand(); - - case ExitCommand.COMMAND_WORD: - return new ExitCommand(); - - case HelpCommand.COMMAND_WORD: - return new HelpCommand(); - - default: - throw new ParseException(MESSAGE_UNKNOWN_COMMAND); - } - } - -} diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java deleted file mode 100644 index 954c8e18f8e..00000000000 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.logic.parser; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -/** - * Stores mapping of prefixes to their respective arguments. - * Each key may be associated with multiple argument values. - * Values for a given key are stored in a list, and the insertion ordering is maintained. - * Keys are unique, but the list of argument values may contain duplicate argument values, i.e. the same argument value - * can be inserted multiple times for the same prefix. - */ -public class ArgumentMultimap { - - /** Prefixes mapped to their respective arguments**/ - private final Map> argMultimap = new HashMap<>(); - - /** - * Associates the specified argument value with {@code prefix} key in this map. - * If the map previously contained a mapping for the key, the new value is appended to the list of existing values. - * - * @param prefix Prefix key with which the specified argument value is to be associated - * @param argValue Argument value to be associated with the specified prefix key - */ - public void put(Prefix prefix, String argValue) { - List argValues = getAllValues(prefix); - argValues.add(argValue); - argMultimap.put(prefix, argValues); - } - - /** - * Returns the last value of {@code prefix}. - */ - public Optional getValue(Prefix prefix) { - List values = getAllValues(prefix); - return values.isEmpty() ? Optional.empty() : Optional.of(values.get(values.size() - 1)); - } - - /** - * Returns all values of {@code prefix}. - * If the prefix does not exist or has no values, this will return an empty list. - * Modifying the returned list will not affect the underlying data structure of the ArgumentMultimap. - */ - public List getAllValues(Prefix prefix) { - if (!argMultimap.containsKey(prefix)) { - return new ArrayList<>(); - } - return new ArrayList<>(argMultimap.get(prefix)); - } - - /** - * Returns the preamble (text before the first valid prefix). Trims any leading/trailing spaces. - */ - public String getPreamble() { - return getValue(new Prefix("")).orElse(""); - } -} diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java deleted file mode 100644 index 5c9aebfa488..00000000000 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ /dev/null @@ -1,148 +0,0 @@ -package seedu.address.logic.parser; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Tokenizes arguments string of the form: {@code preamble value value ...}
- * e.g. {@code some preamble text t/ 11.00 t/12.00 k/ m/ July} where prefixes are {@code t/ k/ m/}.
- * 1. An argument's value can be an empty string e.g. the value of {@code k/} in the above example.
- * 2. Leading and trailing whitespaces of an argument value will be discarded.
- * 3. An argument may be repeated and all its values will be accumulated e.g. the value of {@code t/} - * in the above example.
- */ -public class ArgumentTokenizer { - - /** - * Tokenizes an arguments string and returns an {@code ArgumentMultimap} object that maps prefixes to their - * respective argument values. Only the given prefixes will be recognized in the arguments string. - * - * @param argsString Arguments string of the form: {@code preamble value value ...} - * @param prefixes Prefixes to tokenize the arguments string with - * @return ArgumentMultimap object that maps prefixes to their arguments - */ - public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) { - List positions = findAllPrefixPositions(argsString, prefixes); - return extractArguments(argsString, positions); - } - - /** - * Finds all zero-based prefix positions in the given arguments string. - * - * @param argsString Arguments string of the form: {@code preamble value value ...} - * @param prefixes Prefixes to find in the arguments string - * @return List of zero-based prefix positions in the given arguments string - */ - private static List findAllPrefixPositions(String argsString, Prefix... prefixes) { - return Arrays.stream(prefixes) - .flatMap(prefix -> findPrefixPositions(argsString, prefix).stream()) - .collect(Collectors.toList()); - } - - /** - * {@see findAllPrefixPositions} - */ - private static List findPrefixPositions(String argsString, Prefix prefix) { - List positions = new ArrayList<>(); - - int prefixPosition = findPrefixPosition(argsString, prefix.getPrefix(), 0); - while (prefixPosition != -1) { - PrefixPosition extendedPrefix = new PrefixPosition(prefix, prefixPosition); - positions.add(extendedPrefix); - prefixPosition = findPrefixPosition(argsString, prefix.getPrefix(), prefixPosition); - } - - return positions; - } - - /** - * Returns the index of the first occurrence of {@code prefix} in - * {@code argsString} starting from index {@code fromIndex}. An occurrence - * is valid if there is a whitespace before {@code prefix}. Returns -1 if no - * such occurrence can be found. - * - * E.g if {@code argsString} = "e/hip/900", {@code prefix} = "p/" and - * {@code fromIndex} = 0, this method returns -1 as there are no valid - * occurrences of "p/" with whitespace before it. However, if - * {@code argsString} = "e/hi p/900", {@code prefix} = "p/" and - * {@code fromIndex} = 0, this method returns 5. - */ - private static int findPrefixPosition(String argsString, String prefix, int fromIndex) { - int prefixIndex = argsString.indexOf(" " + prefix, fromIndex); - return prefixIndex == -1 ? -1 - : prefixIndex + 1; // +1 as offset for whitespace - } - - /** - * Extracts prefixes and their argument values, and returns an {@code ArgumentMultimap} object that maps the - * extracted prefixes to their respective arguments. Prefixes are extracted based on their zero-based positions in - * {@code argsString}. - * - * @param argsString Arguments string of the form: {@code preamble value value ...} - * @param prefixPositions Zero-based positions of all prefixes in {@code argsString} - * @return ArgumentMultimap object that maps prefixes to their arguments - */ - private static ArgumentMultimap extractArguments(String argsString, List prefixPositions) { - - // Sort by start position - prefixPositions.sort((prefix1, prefix2) -> prefix1.getStartPosition() - prefix2.getStartPosition()); - - // Insert a PrefixPosition to represent the preamble - PrefixPosition preambleMarker = new PrefixPosition(new Prefix(""), 0); - prefixPositions.add(0, preambleMarker); - - // Add a dummy PrefixPosition to represent the end of the string - PrefixPosition endPositionMarker = new PrefixPosition(new Prefix(""), argsString.length()); - prefixPositions.add(endPositionMarker); - - // Map prefixes to their argument values (if any) - ArgumentMultimap argMultimap = new ArgumentMultimap(); - for (int i = 0; i < prefixPositions.size() - 1; i++) { - // Extract and store prefixes and their arguments - Prefix argPrefix = prefixPositions.get(i).getPrefix(); - String argValue = extractArgumentValue(argsString, prefixPositions.get(i), prefixPositions.get(i + 1)); - argMultimap.put(argPrefix, argValue); - } - - return argMultimap; - } - - /** - * Returns the trimmed value of the argument in the arguments string specified by {@code currentPrefixPosition}. - * The end position of the value is determined by {@code nextPrefixPosition}. - */ - private static String extractArgumentValue(String argsString, - PrefixPosition currentPrefixPosition, - PrefixPosition nextPrefixPosition) { - Prefix prefix = currentPrefixPosition.getPrefix(); - - int valueStartPos = currentPrefixPosition.getStartPosition() + prefix.getPrefix().length(); - String value = argsString.substring(valueStartPos, nextPrefixPosition.getStartPosition()); - - return value.trim(); - } - - /** - * Represents a prefix's position in an arguments string. - */ - private static class PrefixPosition { - private int startPosition; - private final Prefix prefix; - - PrefixPosition(Prefix prefix, int startPosition) { - this.prefix = prefix; - this.startPosition = startPosition; - } - - int getStartPosition() { - return startPosition; - } - - Prefix getPrefix() { - return prefix; - } - } - -} diff --git a/src/main/java/seedu/address/logic/parser/ClearCommandParser.java b/src/main/java/seedu/address/logic/parser/ClearCommandParser.java new file mode 100644 index 00000000000..ae6f77351d8 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ClearCommandParser.java @@ -0,0 +1,26 @@ +package seedu.address.logic.parser; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.ClearCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input command and creates a new ClearCommand object + */ +public class ClearCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the ClearCommand + * and returns an ClearCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + @Override + public ClearCommand parse(String args) throws ParseException { + String trimArgs = args.trim(); + String errorMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + ClearCommand.MESSAGE_USAGE); + if (trimArgs.length() != 0) { + throw new ParseException(errorMessage); + } + return new ClearCommand(); + } +} diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java deleted file mode 100644 index 75b1a9bf119..00000000000 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ /dev/null @@ -1,15 +0,0 @@ -package seedu.address.logic.parser; - -/** - * Contains Command Line Interface (CLI) syntax definitions common to multiple commands - */ -public class CliSyntax { - - /* Prefix definitions */ - public static final Prefix PREFIX_NAME = new Prefix("n/"); - public static final Prefix PREFIX_PHONE = new Prefix("p/"); - public static final Prefix PREFIX_EMAIL = new Prefix("e/"); - public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); - public static final Prefix PREFIX_TAG = new Prefix("t/"); - -} diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java deleted file mode 100644 index 522b93081cc..00000000000 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ /dev/null @@ -1,29 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.parser.exceptions.ParseException; - -/** - * Parses input arguments and creates a new DeleteCommand object - */ -public class DeleteCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the DeleteCommand - * and returns a DeleteCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public DeleteCommand parse(String args) throws ParseException { - try { - Index index = ParserUtil.parseIndex(args); - return new DeleteCommand(index); - } catch (ParseException pe) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe); - } - } - -} diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java deleted file mode 100644 index 845644b7dea..00000000000 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ /dev/null @@ -1,82 +0,0 @@ -package seedu.address.logic.parser; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import java.util.Collection; -import java.util.Collections; -import java.util.Optional; -import java.util.Set; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.tag.Tag; - -/** - * Parses input arguments and creates a new EditCommand object - */ -public class EditCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the EditCommand - * and returns an EditCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public EditCommand parse(String args) throws ParseException { - requireNonNull(args); - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); - - Index index; - - try { - index = ParserUtil.parseIndex(argMultimap.getPreamble()); - } catch (ParseException pe) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); - } - - EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); - if (argMultimap.getValue(PREFIX_NAME).isPresent()) { - editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); - } - if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { - editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); - } - if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { - editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); - } - if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { - editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); - } - parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); - - if (!editPersonDescriptor.isAnyFieldEdited()) { - throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); - } - - return new EditCommand(index, editPersonDescriptor); - } - - /** - * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. - * If {@code tags} contain only one element which is an empty string, it will be parsed into a - * {@code Set} containing zero tags. - */ - private Optional> parseTagsForEdit(Collection tags) throws ParseException { - assert tags != null; - - if (tags.isEmpty()) { - return Optional.empty(); - } - Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; - return Optional.of(ParserUtil.parseTags(tagSet)); - } - -} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java index 4fb71f23103..7db7a509a75 100644 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java @@ -6,7 +6,7 @@ import seedu.address.logic.commands.FindCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.vendor.NameContainsKeywordsPredicate; /** * Parses input arguments and creates a new FindCommand object @@ -20,12 +20,12 @@ public class FindCommandParser implements Parser { */ public FindCommand parse(String args) throws ParseException { String trimmedArgs = args.trim(); - if (trimmedArgs.isEmpty()) { + String finalArgs = trimmedArgs.replaceAll("( )+", " "); + if (finalArgs.isEmpty()) { throw new ParseException( String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); } - - String[] nameKeywords = trimmedArgs.split("\\s+"); + String[] nameKeywords = finalArgs.split("\\s+"); return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); } diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/address/logic/parser/Parser.java index d6551ad8e3f..73673a7ae3f 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/seedu/address/logic/parser/Parser.java @@ -13,4 +13,5 @@ public interface Parser { * @throws ParseException if {@code userInput} does not conform the expected format */ T parse(String userInput) throws ParseException; + } diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index b117acb9c55..0179687c639 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -1,30 +1,67 @@ package seedu.address.logic.parser; import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_INDEX; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_QUANTITY; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Set; +import seedu.address.commons.core.Messages; import seedu.address.commons.core.index.Index; import seedu.address.commons.util.StringUtil; +import seedu.address.logic.commands.PresetCommand; +import seedu.address.logic.commands.enums.Inequality; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; +import seedu.address.logic.parser.exceptions.ParseIndexException; import seedu.address.model.tag.Tag; +import seedu.address.model.vendor.Address; +import seedu.address.model.vendor.Email; +import seedu.address.model.vendor.Name; +import seedu.address.model.vendor.Phone; /** * Contains utility methods used for parsing strings in the various *Parser classes. */ public class ParserUtil { - public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; + /** + * Parses {@code inequality} into an {@code Inequality} and returns it. Leading and trailing whitespaces will be + * trimmed. + * + * @throws ParseException if the string is not recognized as an inequality symbol + */ + public static Inequality parseInequality(String inequality) throws ParseException { + String trimmedInequality = inequality.trim(); + + if (!StringUtil.isInequality(trimmedInequality)) { + throw new ParseException(String.format(Messages.MESSAGE_INVALID_INEQUALITY, trimmedInequality)); + } + + return Inequality.get(trimmedInequality); + } + + /** + * Parses {@code price} into a double and returns it. Leading and trailing whitespaces will be + * trimmed. + * + * @throws ParseException if the string is not a valid price + */ + public static double parsePrice(String price) throws ParseException { + String trimmedPrice = price.trim(); + if (!StringUtil.isNonNegativeUnsignedDouble(trimmedPrice)) { + throw new ParseException(String.format(Messages.MESSAGE_INVALID_PRICE, trimmedPrice)); + } + return Double.parseDouble(trimmedPrice); + } /** * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be * trimmed. + * * @throws ParseException if the specified index is invalid (not non-zero unsigned integer). */ public static Index parseIndex(String oneBasedIndex) throws ParseException { @@ -35,6 +72,35 @@ public static Index parseIndex(String oneBasedIndex) throws ParseException { return Index.fromOneBased(Integer.parseInt(trimmedIndex)); } + /** + * Parses {@code oneBasedIndex} into an {@code Index} with indexName + * and returns it. Leading and trailing whitespaces will be + * trimmed. + * + * @throws ParseIndexException if the specified index is invalid (not non-zero unsigned integer). + */ + public static Index parseIndex(String oneBasedIndex, String indexName) throws ParseIndexException { + String trimmedIndex = oneBasedIndex.trim(); + if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { + throw new ParseIndexException(String.format(MESSAGE_INVALID_INDEX, indexName)); + } + return Index.fromOneBased(Integer.parseInt(trimmedIndex)); + } + + /** + * Parses {@code quantity} into an {@code Integer} and returns it. Leading and trailing whitespaces will be + * trimmed. + * + * @throws ParseIndexException if the specified quantity is a zero or negative value, or not an Integer. + */ + public static int parseQuantity(String quantity) throws ParseIndexException { + String trimmedQuantity = quantity.trim(); + if (!StringUtil.isNonZeroUnsignedInteger(trimmedQuantity)) { + throw new ParseIndexException(MESSAGE_INVALID_QUANTITY); + } + return Integer.parseInt(trimmedQuantity); + } + /** * Parses a {@code String name} into a {@code Name}. * Leading and trailing whitespaces will be trimmed. @@ -121,4 +187,73 @@ public static Set parseTags(Collection tags) throws ParseException } return tagSet; } + + /** + * Parses and validates the string for Preset Command. + */ + public static String[] checkPresetSyntax(String modeType) throws ParseException { + requireNonNull(modeType); + String trimArgs = modeType.trim(); + if (modeType.equals("")) { + throw new ParseException(PresetCommand.MESSAGE_USAGE); + } + int firstSpace = trimArgs.indexOf(' '); + String[] argsArr = new String[2]; + + argsArr[0] = trimArgs; + argsArr[1] = ""; + if (firstSpace != -1) { + argsArr[0] = trimArgs.substring(0, firstSpace).trim(); + argsArr[1] = trimArgs.substring(firstSpace + 1).trim(); + } + + String[] commands = { + "save", + "load", + "delete" + }; + ArrayList matchingCommands = new ArrayList<>(Arrays.asList(commands)); + matchingCommands.removeIf(s -> !s.startsWith(argsArr[0])); + if (matchingCommands.size() == 0) { + throw new ParseException(PresetCommand.MESSAGE_USAGE); + } + + return argsArr; + } + + /** + * Checks whether the number of arguments are from min to max inclusive. + * Throws a ParseException if given String is empty. + */ + public static void checkArgsLength(String[] argsArr, String commandWord, String messageUsage, + int min, int max) throws ParseException { + // Check for empty String + if (argsArr.length == 1 && argsArr[0].equals("") || argsArr.length < min) { + throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + String.format(Messages.MESSAGE_INSUFFICIENT_ARGUMENTS, commandWord, min, messageUsage))); + } else if (argsArr.length > max) { + throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + String.format(Messages.MESSAGE_TOO_MANY_ARGUMENTS, commandWord, max, messageUsage))); + } + } + + /** + * Checks whether the number of arguments are equal to argNum. + * Throws a ParseException if given String is empty. + */ + public static void checkArgsLength(String[] argsArr, String commandWord, + String messageUsage, int argNum) throws ParseException { + checkArgsLength(argsArr, commandWord, messageUsage, argNum, argNum); + } + + /** + * Checks whether the number of argument is at least min. + */ + public static void argsLengthAtLeast(String[] argsArr, String commandWord, + String messageUsage, int min) throws ParseException { + if (argsArr.length == 1 && argsArr[0].equals("") || argsArr.length < min) { + throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + String.format(Messages.MESSAGE_INSUFFICIENT_ARGUMENTS, commandWord, min, messageUsage))); + } + } } diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/seedu/address/logic/parser/Prefix.java deleted file mode 100644 index c859d5fa5db..00000000000 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ /dev/null @@ -1,39 +0,0 @@ -package seedu.address.logic.parser; - -/** - * A prefix that marks the beginning of an argument in an arguments string. - * E.g. 't/' in 'add James t/ friend'. - */ -public class Prefix { - private final String prefix; - - public Prefix(String prefix) { - this.prefix = prefix; - } - - public String getPrefix() { - return prefix; - } - - public String toString() { - return getPrefix(); - } - - @Override - public int hashCode() { - return prefix == null ? 0 : prefix.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Prefix)) { - return false; - } - if (obj == this) { - return true; - } - - Prefix otherPrefix = (Prefix) obj; - return otherPrefix.getPrefix().equals(getPrefix()); - } -} diff --git a/src/main/java/seedu/address/logic/parser/PresetCommandParser.java b/src/main/java/seedu/address/logic/parser/PresetCommandParser.java new file mode 100644 index 00000000000..557d1441798 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/PresetCommandParser.java @@ -0,0 +1,40 @@ +package seedu.address.logic.parser; + +import java.util.Optional; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.DeletePresetCommand; +import seedu.address.logic.commands.LoadPresetCommand; +import seedu.address.logic.commands.PresetCommand; +import seedu.address.logic.commands.SavePresetCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.vendor.Name; + +public class PresetCommandParser implements Parser { + + @Override + public PresetCommand parse(String args) throws ParseException { + String[] argsArr = ParserUtil.checkPresetSyntax(args); + ParserUtil.checkArgsLength(argsArr, PresetCommand.COMMAND_WORD, PresetCommand.MESSAGE_USAGE, 2, 2); + + String modeType = argsArr[0]; + + boolean save = modeType.startsWith("s"); + boolean load = false; + if (!save) { + load = modeType.startsWith("l"); + } + Optional presetName = Optional.empty(); + if (!argsArr[1].equals("")) { + presetName = Optional.of(ParserUtil.parseName(argsArr[1])); + } + + if (!save && !load && argsArr[1].equals("")) { + throw new ParseException(Messages.MESSAGE_NO_INPUT_NAME); + } + + return save ? new SavePresetCommand(presetName) + : load ? new LoadPresetCommand(presetName) + : new DeletePresetCommand(presetName); + } +} diff --git a/src/main/java/seedu/address/logic/parser/PriceCommandParser.java b/src/main/java/seedu/address/logic/parser/PriceCommandParser.java new file mode 100644 index 00000000000..6c021b592f7 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/PriceCommandParser.java @@ -0,0 +1,25 @@ +package seedu.address.logic.parser; + +import seedu.address.logic.commands.PriceCommand; +import seedu.address.logic.commands.enums.Inequality; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.food.PriceWithinRangePredicate; + +public class PriceCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the PriceCommand + * and returns an PriceCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public PriceCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + String finalArgs = trimmedArgs.replaceAll("( )+", " "); + String[] argsArr = finalArgs.split(" "); + ParserUtil.checkArgsLength(argsArr, PriceCommand.COMMAND_WORD, PriceCommand.MESSAGE_USAGE, 2); + Inequality inequality = ParserUtil.parseInequality(argsArr[0]); + double price = ParserUtil.parsePrice(argsArr[1]); + price = ((int) (price * 100)) / 100.0; + return new PriceCommand(new PriceWithinRangePredicate(inequality, price)); + } +} diff --git a/src/main/java/seedu/address/logic/parser/ProfileCommandParser.java b/src/main/java/seedu/address/logic/parser/ProfileCommandParser.java new file mode 100644 index 00000000000..461814cf4e9 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ProfileCommandParser.java @@ -0,0 +1,35 @@ +package seedu.address.logic.parser; + +import seedu.address.logic.commands.ProfileCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.vendor.Address; +import seedu.address.model.vendor.Phone; + +public class ProfileCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the SortCommand + * and returns an SortCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + @Override + public ProfileCommand parse(String args) throws ParseException { + String cleanedArgs = args.replaceAll("( )+", " "); + String trimArgs = cleanedArgs.trim(); + int firstSpace = trimArgs.indexOf(' '); + String[] argsArr = new String[2]; + argsArr[0] = trimArgs; + argsArr[1] = ""; + if (firstSpace != -1) { + argsArr[0] = trimArgs.substring(0, firstSpace).trim(); + argsArr[1] = trimArgs.substring(firstSpace + 1).trim(); + } + + if (argsArr[1].equals("")) { + throw new ParseException(ProfileCommand.MESSAGE_USAGE); + } + + Phone phone = ParserUtil.parsePhone(argsArr[0]); + Address address = ParserUtil.parseAddress(argsArr[1]); + return new ProfileCommand(phone, address); + } +} diff --git a/src/main/java/seedu/address/logic/parser/RemoveCommandParser.java b/src/main/java/seedu/address/logic/parser/RemoveCommandParser.java new file mode 100644 index 00000000000..e9383dc1e3d --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/RemoveCommandParser.java @@ -0,0 +1,31 @@ +package seedu.address.logic.parser; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.RemoveCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new RemoveCommand object + */ +public class RemoveCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the RemoveCommand + * and returns a RemoveCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public RemoveCommand parse(String args) throws ParseException { + String cleanedArgs = args.replaceAll("( )+", " "); + String trimArgs = cleanedArgs.trim(); + String[] argsArr = trimArgs.split(" "); + ParserUtil.checkArgsLength(argsArr, RemoveCommand.COMMAND_WORD, RemoveCommand.MESSAGE_USAGE, 1, 2); + Index index = ParserUtil.parseIndex(argsArr[0], "Menu Index"); + + if (argsArr.length == 1) { + return new RemoveCommand(index); + } + + int quantity = ParserUtil.parseQuantity(argsArr[1]); + return new RemoveCommand(index, quantity); + } +} diff --git a/src/main/java/seedu/address/logic/parser/SortCommandParser.java b/src/main/java/seedu/address/logic/parser/SortCommandParser.java new file mode 100644 index 00000000000..bfdb089aa36 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/SortCommandParser.java @@ -0,0 +1,40 @@ +package seedu.address.logic.parser; + +import seedu.address.logic.commands.SortCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new SortCommand object + */ +public class SortCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the SortCommand + * and returns an SortCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + @Override + public SortCommand parse(String args) throws ParseException { + String cleanedArgs = args.replaceAll("( )+", " "); + String trimArgs = cleanedArgs.trim(); + String[] argsArr = trimArgs.split(" "); + ParserUtil.checkArgsLength(argsArr, SortCommand.COMMAND_WORD, SortCommand.MESSAGE_USAGE, 1, 2); + + String sortedBy = argsArr[0]; + String ascending = "t"; + if (argsArr.length == 2) { + ascending = argsArr[1]; + } + + + if (!sortedBy.equals(SortCommand.NAME) && !sortedBy.equals(SortCommand.PRICE)) { + throw new ParseException(SortCommand.MESSAGE_USAGE); + } + + if (!ascending.equals("a") && !ascending.equals("d") && !ascending.equals("t")) { + throw new ParseException(SortCommand.MESSAGE_USAGE); + } + + return new SortCommand(sortedBy, ascending); + } +} diff --git a/src/main/java/seedu/address/logic/parser/SupperStrikersParser.java b/src/main/java/seedu/address/logic/parser/SupperStrikersParser.java new file mode 100644 index 00000000000..63f139efe74 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/SupperStrikersParser.java @@ -0,0 +1,147 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_AMBIGUOUS_COMMAND; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.ClearCommand; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.ExitCommand; +import seedu.address.logic.commands.FindCommand; +import seedu.address.logic.commands.HelpCommand; +import seedu.address.logic.commands.MenuCommand; +import seedu.address.logic.commands.PresetCommand; +import seedu.address.logic.commands.PriceCommand; +import seedu.address.logic.commands.ProfileCommand; +import seedu.address.logic.commands.RemoveCommand; +import seedu.address.logic.commands.SortCommand; +import seedu.address.logic.commands.SubmitCommand; +import seedu.address.logic.commands.TagCommand; +import seedu.address.logic.commands.TotalCommand; +import seedu.address.logic.commands.UndoCommand; +import seedu.address.logic.commands.UntagCommand; +import seedu.address.logic.commands.VendorCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses user input. + */ +public class SupperStrikersParser { + + /** + * Used for initial separation of command word and args. + */ + private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + + /** + * Parses user input into command for execution. + * + * @param userInput full user input string + * @return the command based on the user input + * @throws ParseException if the user input does not conform the expected format + */ + public Command parseCommand(String userInput) throws ParseException { + final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); + if (!matcher.matches()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + + final String commandPrefix = matcher.group("commandWord"); + final String arguments = matcher.group("arguments"); + + String[] commands = { + AddCommand.COMMAND_WORD, + RemoveCommand.COMMAND_WORD, + TagCommand.COMMAND_WORD, + UntagCommand.COMMAND_WORD, + SortCommand.COMMAND_WORD, + ClearCommand.COMMAND_WORD, + FindCommand.COMMAND_WORD, + PriceCommand.COMMAND_WORD, + MenuCommand.COMMAND_WORD, + TotalCommand.COMMAND_WORD, + SubmitCommand.COMMAND_WORD, + UndoCommand.COMMAND_WORD, + ExitCommand.COMMAND_WORD, + HelpCommand.COMMAND_WORD, + VendorCommand.COMMAND_WORD, + ProfileCommand.COMMAND_WORD, + PresetCommand.COMMAND_WORD, + }; + + ArrayList matchingCommands = new ArrayList<>(Arrays.asList(commands)); + + matchingCommands.removeIf(s -> !s.startsWith(commandPrefix)); + + String commandWord = ""; + if (matchingCommands.size() == 1) { + commandWord = matchingCommands.get(0); + } + + switch (commandWord) { + + case AddCommand.COMMAND_WORD: + return new AddCommandParser().parse(arguments); + + case RemoveCommand.COMMAND_WORD: + return new RemoveCommandParser().parse(arguments); + + case TagCommand.COMMAND_WORD: + return new TagCommandParser().parse(arguments); + + case UntagCommand.COMMAND_WORD: + return new UntagCommandParser().parse(arguments); + + case SortCommand.COMMAND_WORD: + return new SortCommandParser().parse(arguments); + + case ClearCommand.COMMAND_WORD: + return new ClearCommandParser().parse(arguments); + + case FindCommand.COMMAND_WORD: + return new FindCommandParser().parse(arguments); + + case PriceCommand.COMMAND_WORD: + return new PriceCommandParser().parse(arguments); + + case MenuCommand.COMMAND_WORD: + return new MenuCommand(); + + case TotalCommand.COMMAND_WORD: + return new TotalCommand(); + + case SubmitCommand.COMMAND_WORD: + return new SubmitCommand(); + + case UndoCommand.COMMAND_WORD: + return new UndoCommand(); + + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); + + case VendorCommand.COMMAND_WORD: + return new VendorCommandParser().parse(arguments); + + case PresetCommand.COMMAND_WORD: + return new PresetCommandParser().parse(arguments); + + case ProfileCommand.COMMAND_WORD: + return new ProfileCommandParser().parse(arguments); + + default: + throw matchingCommands.size() == 0 + ? new ParseException(MESSAGE_UNKNOWN_COMMAND) + : new ParseException(String.format(MESSAGE_AMBIGUOUS_COMMAND, matchingCommands)); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/TagCommandParser.java b/src/main/java/seedu/address/logic/parser/TagCommandParser.java new file mode 100644 index 00000000000..24d449b6f4e --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/TagCommandParser.java @@ -0,0 +1,24 @@ +package seedu.address.logic.parser; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.TagCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.tag.Tag; + +public class TagCommandParser implements Parser { + @Override + public TagCommand parse(String args) throws ParseException { + String trimArgs = args.trim(); + String[] argsArr = trimArgs.split(" +"); + ParserUtil.argsLengthAtLeast(argsArr, TagCommand.COMMAND_WORD, TagCommand.MESSAGE_USAGE, 2); + + Index index = ParserUtil.parseIndex(argsArr[0], "Order Item Index"); + StringBuilder tagText = new StringBuilder(); + for (int i = 1; i < argsArr.length; i++) { + tagText.append((i > 1 ? " " : "") + argsArr[i]); + } + Tag tag = ParserUtil.parseTag(tagText.toString()); + + return new TagCommand(index, tag); + } +} diff --git a/src/main/java/seedu/address/logic/parser/UntagCommandParser.java b/src/main/java/seedu/address/logic/parser/UntagCommandParser.java new file mode 100644 index 00000000000..b909634e14c --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/UntagCommandParser.java @@ -0,0 +1,19 @@ +package seedu.address.logic.parser; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.UntagCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +public class UntagCommandParser implements Parser { + + @Override + public UntagCommand parse(String args) throws ParseException { + String trimArgs = args.trim(); + String[] argsArr = trimArgs.split(" +"); + ParserUtil.checkArgsLength(argsArr, UntagCommand.COMMAND_WORD, UntagCommand.MESSAGE_USAGE, 1); + + Index index = ParserUtil.parseIndex(argsArr[0], "Order Item Index"); + + return new UntagCommand(index); + } +} diff --git a/src/main/java/seedu/address/logic/parser/VendorCommandParser.java b/src/main/java/seedu/address/logic/parser/VendorCommandParser.java new file mode 100644 index 00000000000..160d34e5061 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/VendorCommandParser.java @@ -0,0 +1,33 @@ +package seedu.address.logic.parser; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.SwitchVendorCommand; +import seedu.address.logic.commands.VendorCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new VendorCommand object + */ +public class VendorCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the VendorCommand + * and returns an VendorCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + @Override + public VendorCommand parse(String args) throws ParseException { + String cleanedArgs = args.replaceAll("( )+", " "); + String trimArgs = cleanedArgs.trim(); + String[] argsArr = trimArgs.split(" "); + // No arguments supplied + if (argsArr.length == 1 && argsArr[0].equals("")) { + return new VendorCommand(); + } + ParserUtil.checkArgsLength(argsArr, VendorCommand.COMMAND_WORD, SwitchVendorCommand.MESSAGE_USAGE, 1); + Index index = ParserUtil.parseIndex(argsArr[0], "Vendor Index"); + return new SwitchVendorCommand(index); + } +} + + diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java index 158a1a54c1c..ec6620c763e 100644 --- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java +++ b/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java @@ -11,7 +11,16 @@ public ParseException(String message) { super(message); } - public ParseException(String message, Throwable cause) { - super(message, cause); + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (!(obj instanceof ParseException)) { + return false; + } + + return ((ParseException) obj).getMessage().equals(getMessage()); } } diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseIndexException.java b/src/main/java/seedu/address/logic/parser/exceptions/ParseIndexException.java new file mode 100644 index 00000000000..4d95e7e4f17 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/exceptions/ParseIndexException.java @@ -0,0 +1,8 @@ +package seedu.address.logic.parser.exceptions; + +public class ParseIndexException extends ParseException { + + public ParseIndexException(String message) { + super(message); + } +} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java deleted file mode 100644 index 1a943a0781a..00000000000 --- a/src/main/java/seedu/address/model/AddressBook.java +++ /dev/null @@ -1,120 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; - -/** - * Wraps all data at the address-book level - * Duplicates are not allowed (by .isSamePerson comparison) - */ -public class AddressBook implements ReadOnlyAddressBook { - - private final UniquePersonList persons; - - /* - * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication - * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html - * - * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication - * among constructors. - */ - { - persons = new UniquePersonList(); - } - - public AddressBook() {} - - /** - * Creates an AddressBook using the Persons in the {@code toBeCopied} - */ - public AddressBook(ReadOnlyAddressBook toBeCopied) { - this(); - resetData(toBeCopied); - } - - //// list overwrite operations - - /** - * Replaces the contents of the person list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - this.persons.setPersons(persons); - } - - /** - * Resets the existing data of this {@code AddressBook} with {@code newData}. - */ - public void resetData(ReadOnlyAddressBook newData) { - requireNonNull(newData); - - setPersons(newData.getPersonList()); - } - - //// person-level operations - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - public boolean hasPerson(Person person) { - requireNonNull(person); - return persons.contains(person); - } - - /** - * Adds a person to the address book. - * The person must not already exist in the address book. - */ - public void addPerson(Person p) { - persons.add(p); - } - - /** - * Replaces the given person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. - */ - public void setPerson(Person target, Person editedPerson) { - requireNonNull(editedPerson); - - persons.setPerson(target, editedPerson); - } - - /** - * Removes {@code key} from this {@code AddressBook}. - * {@code key} must exist in the address book. - */ - public void removePerson(Person key) { - persons.remove(key); - } - - //// util methods - - @Override - public String toString() { - return persons.asUnmodifiableObservableList().size() + " persons"; - // TODO: refine later - } - - @Override - public ObservableList getPersonList() { - return persons.asUnmodifiableObservableList(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddressBook // instanceof handles nulls - && persons.equals(((AddressBook) other).persons)); - } - - @Override - public int hashCode() { - return persons.hashCode(); - } -} diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index d54df471c1f..92cafba8dd2 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -1,18 +1,26 @@ package seedu.address.model; import java.nio.file.Path; +import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.food.MenuItem; +import seedu.address.model.menu.ReadOnlyMenuManager; +import seedu.address.model.order.OrderItem; +import seedu.address.model.order.ReadOnlyOrderManager; +import seedu.address.model.tag.Tag; +import seedu.address.model.vendor.ReadOnlyVendorManager; +import seedu.address.model.vendor.Vendor; + /** * The API of the Model component. */ public interface Model { - /** {@code Predicate} that always evaluate to true */ - Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; /** * Replaces user prefs data with the data in {@code userPrefs}. @@ -37,51 +45,135 @@ public interface Model { /** * Returns the user prefs' address book file path. */ - Path getAddressBookFilePath(); + Path getVendorManagerFilePath(); + + /** + * Sets the user prefs' menu manager file path. + */ + void setVendorManagerFilePath(Path vendorManagerFilePath); + + /** + * Replaces menu manager data with the data in {@code menuManager}. + */ + + void setVendorManager(ReadOnlyVendorManager vendorManager); + + /** + * Returns the VendorManager + */ + ReadOnlyVendorManager getVendorManager(); + + /** + * Returns true if a vendor with the same identity as {@code vendor} exists in the address book. + */ + boolean hasVendor(Vendor vendor); + + /** + * Selects the vendor with index {@code vendorIndex} . + * {@code vendorIndex} must be a valid index in the model. + */ + void selectVendor(int vendorIndex); + + ObservableList getObservableVendorList(); + + /** + * Replaces address book data with the data in {@code menuManager}. + */ + void setMenuManager(ReadOnlyMenuManager menuManager, int index); + + /** + * Returns the MenuManager at the ith index + */ + ReadOnlyMenuManager getMenuManager(int index); + + void sortMenuItemBy(String sortedBy, boolean ascending, boolean toggle); + + void showDefaultMenu(); + /** + * Replaces OrderManager data with the data in {@code orderItems}. + */ + void setOrder(List orderItems); /** - * Sets the user prefs' address book file path. + * Replaces OrderManager data with the data in {@code orderManager}. */ - void setAddressBookFilePath(Path addressBookFilePath); + void setOrderManager(ReadOnlyOrderManager orderManager); /** - * Replaces address book data with the data in {@code addressBook}. + * Returns the MenuManager at the ith index */ - void setAddressBook(ReadOnlyAddressBook addressBook); + ReadOnlyOrderManager getOrderManager(); - /** Returns the AddressBook */ - ReadOnlyAddressBook getAddressBook(); + /** + * Returns true if an orderItem with the same identity as {@code orderItem} exists in the address book. + */ + boolean hasOrderItem(OrderItem orderItem); /** - * Returns true if a person with the same identity as {@code person} exists in the address book. + * Deletes the given orderItem. + * The orderItem must exist in the order manager. */ - boolean hasPerson(Person person); + void removeOrderItem(OrderItem target); /** - * Deletes the given person. - * The person must exist in the address book. + * Adds the given orderItem. + * {@code orderItem} if orderItem exists in order manageradd to the current quantity. + * @throws CommandException if after adding, there is more than 100 of the same order item */ - void deletePerson(Person target); + void addOrderItem(OrderItem orderItem) throws CommandException; /** - * Adds the given person. - * {@code person} must not already exist in the address book. + * Returns an unmodifiable view of the filtered menu item list at the corresponding index */ - void addPerson(Person person); + ObservableList getFilteredMenuItemList(); + + int getFilteredMenuItemListSize(); /** - * Replaces the given person {@code target} with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. + * Clears the order. */ - void setPerson(Person target, Person editedPerson); + void clearOrder(); - /** Returns an unmodifiable view of the filtered person list */ - ObservableList getFilteredPersonList(); + /** + * Clears the orderHistory. + */ + void resetOrder(); /** - * Updates the filter of the filtered person list to filter by the given {@code predicate}. + * Updates the filter of the filtered menu item list at the corresponding index to filter by the given + * {@code predicate}. + * * @throws NullPointerException if {@code predicate} is null. */ - void updateFilteredPersonList(Predicate predicate); + void updateFilteredMenuItemList(Predicate predicate); + + /** + * Updates the filter of the filtered menu item list at the corresponding index to filter by the given + * {@code comparator}. + * Only used for test cases. + * @throws NullPointerException if {@code comparator} is null. + */ + void updateFilteredMenuItemList(Comparator comparator, boolean isSortedAsc); + + + /** + * Returns an unmodifiable view of the filtered orderItem list at the corresponding index + */ + ObservableList getObservableOrderItemList(); + + int getOrderSize(); + + int getVendorIndex(); + + int getOrderHistorySize(); + + int getOrderItemQuantity(int index); + + void undoOrder(); + + boolean isSelected(); + + void tagOrderItem(OrderItem orderItem, Tag tag); + + void untagOrderItem(OrderItem orderItem); } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 0650c954f5c..554ae3d65c0 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -4,6 +4,9 @@ import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; import java.util.logging.Logger; @@ -11,34 +14,57 @@ import javafx.collections.transformation.FilteredList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; +import seedu.address.logic.commands.SortCommand; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.food.MenuItem; +import seedu.address.model.menu.MenuManager; +import seedu.address.model.menu.ReadOnlyMenuManager; +import seedu.address.model.order.OrderItem; +import seedu.address.model.order.OrderManager; +import seedu.address.model.order.ReadOnlyOrderManager; +import seedu.address.model.tag.Tag; +import seedu.address.model.vendor.ReadOnlyVendorManager; +import seedu.address.model.vendor.Vendor; +import seedu.address.model.vendor.VendorManager; + /** - * Represents the in-memory model of the address book data. + * Represents the in-memory model of the supper strikers data. */ public class ModelManager implements Model { private static final Logger logger = LogsCenter.getLogger(ModelManager.class); - private final AddressBook addressBook; + private final VendorManager vendorManager; + private final List menuManagers; + private final OrderManager orderManager; + private final UserPrefs userPrefs; - private final FilteredList filteredPersons; + private ObservableList filteredMenuItems; + + private boolean isSortedAsc = false; /** - * Initializes a ModelManager with the given addressBook and userPrefs. + * Initializes a ModelManager with the given vendorManager, userPrefs, menuManager and orderManager. */ - public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { + public ModelManager( + ReadOnlyVendorManager vendorManager, + ReadOnlyUserPrefs userPrefs, + List menuManagers, + OrderManager orderManager + ) { super(); - requireAllNonNull(addressBook, userPrefs); + requireAllNonNull(vendorManager, userPrefs, menuManagers, orderManager); - logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); + logger.fine("Initializing with address book: " + vendorManager + " and user prefs " + userPrefs); - this.addressBook = new AddressBook(addressBook); + this.vendorManager = new VendorManager(vendorManager); this.userPrefs = new UserPrefs(userPrefs); - filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); + this.menuManagers = menuManagers; + this.orderManager = orderManager; } public ModelManager() { - this(new AddressBook(), new UserPrefs()); + this(new VendorManager(), new UserPrefs(), new ArrayList<>(), new OrderManager()); } //=========== UserPrefs ================================================================================== @@ -66,67 +92,222 @@ public void setGuiSettings(GuiSettings guiSettings) { } @Override - public Path getAddressBookFilePath() { - return userPrefs.getAddressBookFilePath(); + public Path getVendorManagerFilePath() { + return userPrefs.getVendorManagerFilePath(); + } + + @Override + public void setVendorManagerFilePath(Path vendorManagerFilePath) { + requireNonNull(vendorManagerFilePath); + userPrefs.setVendorManagerFilePath(vendorManagerFilePath); } + //=========== VendorManager ================================================================================ + @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - userPrefs.setAddressBookFilePath(addressBookFilePath); + public void setVendorManager(ReadOnlyVendorManager vendorManager) { + this.vendorManager.resetData(vendorManager); } - //=========== AddressBook ================================================================================ + @Override + public ReadOnlyVendorManager getVendorManager() { + return vendorManager; + } @Override - public void setAddressBook(ReadOnlyAddressBook addressBook) { - this.addressBook.resetData(addressBook); + public boolean hasVendor(Vendor vendor) { + requireNonNull(vendor); + return vendorManager.hasVendor(vendor); } @Override - public ReadOnlyAddressBook getAddressBook() { - return addressBook; + public void selectVendor(int vendorIndex) { + this.vendorManager.selectVendor(vendorIndex); + if (vendorIndex != -1) { + this.filteredMenuItems = new FilteredList<>(this.menuManagers.get(vendorIndex).getMenuItemList()); + } } @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return addressBook.hasPerson(person); + public int getVendorIndex() { + return this.vendorManager.getVendorIndex(); } @Override - public void deletePerson(Person target) { - addressBook.removePerson(target); + public boolean isSelected() { + return this.vendorManager.getVendorIndex() != -1; } @Override - public void addPerson(Person person) { - addressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + public ObservableList getObservableVendorList() { + return vendorManager.getVendorList(); } + //=========== MenuManager ================================================================================ @Override - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); + public void setMenuManager(ReadOnlyMenuManager menuManager, int index) { + this.menuManagers.get(index).resetData(menuManager); + } - addressBook.setPerson(target, editedPerson); + @Override + public ReadOnlyMenuManager getMenuManager(int index) { + return menuManagers.get(index); } - //=========== Filtered Person List Accessors ============================================================= + @Override + public void sortMenuItemBy(String sortedBy, boolean ascending, boolean toggle) { + int index = getVendorIndex(); + if (index < 0 || index >= menuManagers.size()) { + return; + } + // not suppose to modify menumanager's menus + Comparator itemComparator; + switch (sortedBy) { + case SortCommand.NAME: + itemComparator = Comparator.comparing(MenuItem::getName); + break; + case SortCommand.PRICE: + itemComparator = Comparator.comparing(MenuItem::getPrice); + break; + default: + throw new IllegalStateException("Unexpected value: " + sortedBy); + } + + filteredMenuItems = filteredMenuItems.sorted(itemComparator); + if ((toggle && isSortedAsc) || (!toggle && !ascending)) { + filteredMenuItems = filteredMenuItems.sorted(itemComparator.reversed()); + } + isSortedAsc = toggle ? !isSortedAsc : ascending; + } /** - * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of - * {@code versionedAddressBook} + * Shows the current menu at the default state. */ + public void showDefaultMenu() { + filteredMenuItems = new FilteredList<>(getMenuManager(getVendorIndex()).getMenuItemList()); + isSortedAsc = false; + } + + + //=========== OrderManager ================================================================================ + + @Override + public void setOrderManager(ReadOnlyOrderManager orderManager) { + this.orderManager.resetData(orderManager); + } + @Override - public ObservableList getFilteredPersonList() { - return filteredPersons; + public void setOrder(List orderItems) { + this.orderManager.setOrder(orderItems); } @Override - public void updateFilteredPersonList(Predicate predicate) { + public ReadOnlyOrderManager getOrderManager() { + return orderManager; + } + + @Override + public boolean hasOrderItem(OrderItem orderItem) { + requireNonNull(orderItem); + return orderManager.hasOrderItem(orderItem); + } + + @Override + public void removeOrderItem(OrderItem target) { + requireNonNull(target); + orderManager.removeOrderItem(target); + } + + @Override + public void addOrderItem(OrderItem orderItem) throws CommandException { + requireNonNull(orderItem); + try { + orderManager.addOrderItem(orderItem); + } catch (CommandException e) { + throw e; + } + } + + @Override + public void tagOrderItem(OrderItem orderItem, Tag tag) { + requireAllNonNull(orderItem, tag); + orderManager.tagOrderItem(orderItem, tag); + } + + @Override + public void untagOrderItem(OrderItem orderItem) { + requireNonNull(orderItem); + orderManager.untagOrderItem(orderItem); + } + + @Override + public int getOrderHistorySize() { + return orderManager.getOrderHistorySize(); + } + + @Override + public int getOrderItemQuantity(int index) { + return orderManager.getQuantity(index); + } + + @Override + public void undoOrder() { + orderManager.undoChanges(); + } + + @Override + public void clearOrder() { + orderManager.setOrder(new ArrayList<>()); + } + + @Override + public void resetOrder() { + orderManager.resetOrder(); + } + + @Override + public int getOrderSize() { + return orderManager.getOrderItemList().size(); + } + + @Override + public ObservableList getObservableOrderItemList() { + return orderManager.getOrderItemList(); + } + + //=========== Filtered Food List Accessors ============================================================= + + @Override + public ObservableList getFilteredMenuItemList() { + return filteredMenuItems; + } + + @Override + public int getFilteredMenuItemListSize() { + return filteredMenuItems == null ? 0 : getFilteredMenuItemList().size(); + } + + @Override + public void updateFilteredMenuItemList(Predicate predicate) { requireNonNull(predicate); - filteredPersons.setPredicate(predicate); + int index = getVendorIndex(); + if (index < 0 || index >= menuManagers.size()) { + return; + } + // not suppose to modify menumanager's menus + filteredMenuItems = filteredMenuItems.filtered(predicate); + } + + @Override + public void updateFilteredMenuItemList(Comparator comparator, boolean isSortedAsc) { + requireNonNull(comparator); + int index = getVendorIndex(); + if (index < 0 || index >= menuManagers.size()) { + return; + } + // not suppose to modify menumanager's menus + filteredMenuItems = filteredMenuItems.sorted(comparator); + this.isSortedAsc = isSortedAsc; } @Override @@ -143,9 +324,12 @@ public boolean equals(Object obj) { // state check ModelManager other = (ModelManager) obj; - return addressBook.equals(other.addressBook) + return vendorManager.equals(other.vendorManager) && userPrefs.equals(other.userPrefs) - && filteredPersons.equals(other.filteredPersons); + && menuManagers.equals(other.menuManagers) + && orderManager.equals(other.orderManager) + && filteredMenuItems.equals(other.filteredMenuItems) + && isSortedAsc == other.isSortedAsc; } } diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java deleted file mode 100644 index 6ddc2cd9a29..00000000000 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ /dev/null @@ -1,17 +0,0 @@ -package seedu.address.model; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; - -/** - * Unmodifiable view of an address book - */ -public interface ReadOnlyAddressBook { - - /** - * Returns an unmodifiable view of the persons list. - * This list will not contain any duplicate persons. - */ - ObservableList getPersonList(); - -} diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java index befd58a4c73..9d8eaeb6dae 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java @@ -11,6 +11,6 @@ public interface ReadOnlyUserPrefs { GuiSettings getGuiSettings(); - Path getAddressBookFilePath(); + Path getVendorManagerFilePath(); } diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java index 25a5fd6eab9..456612fe0c0 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/address/model/UserPrefs.java @@ -14,7 +14,9 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path vendorManagerFilePath = Paths.get("data" , "vendormanager.json"); + private Path orderManagerFilePath = Paths.get("data", "presets.json"); + private Path profileManagerFilePath = Paths.get("data", "profile.json"); /** * Creates a {@code UserPrefs} with default values. @@ -35,7 +37,7 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) { public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); - setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setVendorManagerFilePath(newUserPrefs.getVendorManagerFilePath()); } public GuiSettings getGuiSettings() { @@ -47,13 +49,21 @@ public void setGuiSettings(GuiSettings guiSettings) { this.guiSettings = guiSettings; } - public Path getAddressBookFilePath() { - return addressBookFilePath; + public Path getVendorManagerFilePath() { + return vendorManagerFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - this.addressBookFilePath = addressBookFilePath; + public void setVendorManagerFilePath(Path vendorManagerFilePath) { + requireNonNull(vendorManagerFilePath); + this.vendorManagerFilePath = vendorManagerFilePath; + } + + public Path getOrderManagerFilePath() { + return orderManagerFilePath; + } + + public Path getProfileManagerFilePath() { + return profileManagerFilePath; } @Override @@ -68,19 +78,21 @@ public boolean equals(Object other) { UserPrefs o = (UserPrefs) other; return guiSettings.equals(o.guiSettings) - && addressBookFilePath.equals(o.addressBookFilePath); + && vendorManagerFilePath.equals(o.vendorManagerFilePath) + && orderManagerFilePath.equals(o.orderManagerFilePath) + && profileManagerFilePath.equals(o.profileManagerFilePath); } @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, vendorManagerFilePath); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Gui Settings : " + guiSettings); - sb.append("\nLocal data file location : " + addressBookFilePath); + sb.append("\nLocal data file location : " + vendorManagerFilePath); return sb.toString(); } diff --git a/src/main/java/seedu/address/model/food/Food.java b/src/main/java/seedu/address/model/food/Food.java new file mode 100644 index 00000000000..5ff4a68ff8e --- /dev/null +++ b/src/main/java/seedu/address/model/food/Food.java @@ -0,0 +1,109 @@ +package seedu.address.model.food; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import seedu.address.model.tag.Tag; + +/** + * Represents a Food in the address book. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public abstract class Food { + + // Identity fields + protected final String name; + protected final double price; + + // Data fields + protected final Set tags = new HashSet<>(); + + /** + * Every field must be present and not null. + */ + public Food(String name, double price, Set tags) { + requireAllNonNull(name, price, tags); + this.name = name; + this.price = price; + this.tags.addAll(tags); + } + + /** + * Checks whether the price of the food is valid. + */ + public static boolean isValidPrice(double price) { + if (price <= 0) { + return false; + } + + String priceString = String.valueOf(price); + String[] priceArray = priceString.split("\\."); + + // Price with 0 as fractional portion + if (priceArray.length == 1) { + return true; + } + assert priceArray.length == 2; + return priceArray[1].length() <= 2; + } + + public String getName() { + return name; + } + + public double getPrice() { + return price; + } + + /** + * Formats the price to 2 decimal places + */ + public String getPriceString() { + return String.format("$%.2f", price); + } + + public abstract Set getTags(); + + public abstract void setTags(Set tags); + + /** + * Returns true if both foods have the same identity and data fields. + * This defines a stronger notion of equality between two foods. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Food)) { + return false; + } + + Food otherFood = (Food) other; + return otherFood.getName().equals(getName()) + && otherFood.getPrice() == getPrice() + && otherFood.getTags().equals(getTags()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, price, tags); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName()) + .append(" Price: ") + .append(getPriceString()) + .append(" Tags: "); + getTags().forEach(builder::append); + return builder.toString(); + } + +} diff --git a/src/main/java/seedu/address/model/food/MenuItem.java b/src/main/java/seedu/address/model/food/MenuItem.java new file mode 100644 index 00000000000..990311acdf0 --- /dev/null +++ b/src/main/java/seedu/address/model/food/MenuItem.java @@ -0,0 +1,33 @@ +package seedu.address.model.food; + +import java.util.Set; + +import seedu.address.model.tag.Tag; + +public class MenuItem extends Food { + + private final String filePath; + + /** + * Every field must be present and not null. + */ + public MenuItem (String name, double price, Set tags, String filePath) { + super(name, price, tags); + this.filePath = filePath; + } + + @Override + public Set getTags() { + return this.tags; + } + + @Override + public void setTags(Set newTags) { + tags.clear(); + tags.addAll(newTags); + } + + public String getFilePath() { + return filePath; + } +} diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/food/NameContainsKeywordsPredicate.java similarity index 78% rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/seedu/address/model/food/NameContainsKeywordsPredicate.java index c9b5868427c..3b9c95eeb77 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/seedu/address/model/food/NameContainsKeywordsPredicate.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.address.model.food; import java.util.List; import java.util.function.Predicate; @@ -6,9 +6,9 @@ import seedu.address.commons.util.StringUtil; /** - * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. + * Tests that a {@code Food}'s {@code Name} matches any of the keywords given. */ -public class NameContainsKeywordsPredicate implements Predicate { +public class NameContainsKeywordsPredicate implements Predicate { private final List keywords; public NameContainsKeywordsPredicate(List keywords) { @@ -16,9 +16,9 @@ public NameContainsKeywordsPredicate(List keywords) { } @Override - public boolean test(Person person) { + public boolean test(MenuItem item) { return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(item.getName(), keyword)); } @Override diff --git a/src/main/java/seedu/address/model/food/PriceWithinRangePredicate.java b/src/main/java/seedu/address/model/food/PriceWithinRangePredicate.java new file mode 100644 index 00000000000..3bbf8d0e695 --- /dev/null +++ b/src/main/java/seedu/address/model/food/PriceWithinRangePredicate.java @@ -0,0 +1,56 @@ +package seedu.address.model.food; + +import java.util.function.Predicate; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.enums.Inequality; +import seedu.address.logic.parser.exceptions.ParseException; + +public class PriceWithinRangePredicate implements Predicate { + private final double price; + private final Inequality inequality; + + /** + * Creates a predicate, taking in an inequality and a price. Used to compare the price of a food item to the price + * with the specified inequality. + */ + public PriceWithinRangePredicate(Inequality inequality, double price) throws ParseException { + this.inequality = inequality; + if (price < 0) { + throw new ParseException(String.format(Messages.MESSAGE_INVALID_PRICE, price)); + } + if (price > 1000) { + throw new ParseException(String.format(Messages.MESSAGE_PRICE_GREATER_THAN_LIMIT, price)); + } + this.price = price; + } + + @Override + public boolean test(MenuItem item) { + switch (inequality) { + case LESSER_THAN: + return item.getPrice() < price; + case LESSER_THAN_OR_EQUAL_TO: + return item.getPrice() <= price; + case GREATER_THAN: + return item.getPrice() > price; + case GREATER_THAN_OR_EQUAL_TO: + return item.getPrice() >= price; + default: + assert (false); + return false; + } + } + + @Override + public boolean equals (Object other) { + return other == this // short circuit if same object + || (other instanceof PriceWithinRangePredicate // instanceof handles nulls + && price == ((PriceWithinRangePredicate) other).price + && inequality.equals(((PriceWithinRangePredicate) other).inequality)); // state check + } + @Override + public String toString() { + return String.format("%s $%.2f", inequality.toString(), price); + } +} diff --git a/src/main/java/seedu/address/model/food/exceptions/DuplicateFoodException.java b/src/main/java/seedu/address/model/food/exceptions/DuplicateFoodException.java new file mode 100644 index 00000000000..d9d5b0f5725 --- /dev/null +++ b/src/main/java/seedu/address/model/food/exceptions/DuplicateFoodException.java @@ -0,0 +1,11 @@ +package seedu.address.model.food.exceptions; + +/** + * Signals that the operation will result in duplicate Foods (Foods are considered duplicates if they have the same + * identity). + */ +public class DuplicateFoodException extends RuntimeException { + public DuplicateFoodException() { + super("Operation would result in duplicate persons"); + } +} diff --git a/src/main/java/seedu/address/model/food/exceptions/FoodNotFoundException.java b/src/main/java/seedu/address/model/food/exceptions/FoodNotFoundException.java new file mode 100644 index 00000000000..79a71d4709a --- /dev/null +++ b/src/main/java/seedu/address/model/food/exceptions/FoodNotFoundException.java @@ -0,0 +1,11 @@ +package seedu.address.model.food.exceptions; + +/** + * Signals that the operation is unable to find the specified food. + */ +public class FoodNotFoundException extends RuntimeException { + + public FoodNotFoundException() { + super("Food not found"); + } +} diff --git a/src/main/java/seedu/address/model/menu/Menu.java b/src/main/java/seedu/address/model/menu/Menu.java new file mode 100644 index 00000000000..9a7c5589e00 --- /dev/null +++ b/src/main/java/seedu/address/model/menu/Menu.java @@ -0,0 +1,183 @@ +package seedu.address.model.menu; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.food.Food; +import seedu.address.model.food.MenuItem; +import seedu.address.model.food.exceptions.DuplicateFoodException; +import seedu.address.model.food.exceptions.FoodNotFoundException; +import seedu.address.storage.JsonAdaptedMenuItem; + +/** + * A list of menu items that enforces uniqueness between its elements and does not allow nulls. + * A menuItem is considered unique by comparing using {@code Food#equals(Food)}. As such, adding and updating of + * menu items uses Food#equals(Food) for equality so as to ensure that the menu item being added or updated is + * unique in terms of identity in the Menu. The removal of a menu item also uses Food#equals(Object) so + * as to ensure that the food with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Food#equals(Object) (Food) + */ +public class Menu implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent food as the given argument. + */ + public boolean contains(MenuItem toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::equals); + } + + /** + * Adds a menu item to the list. + * The menu item must not already exist in the list. + */ + public void add(MenuItem toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateFoodException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the menu item {@code target} in the list with {@code editedMenuItem}. + * {@code target} must exist in the list. + * The menu item identity of {@code editedMenuItem} must not be the same as another existing menu item in the list. + */ + public void setMenuItem(MenuItem target, MenuItem editedItem) { + requireAllNonNull(target, editedItem); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new FoodNotFoundException(); + } + + if (!target.equals(editedItem) && contains(editedItem)) { + throw new DuplicateFoodException(); + } + + internalList.set(index, editedItem); + } + + /** + * Removes the equivalent menu item from the list. + * The menu item must exist in the list. + */ + public void remove(MenuItem toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new FoodNotFoundException(); + } + } + + public void setMenuItems(Menu replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + + /** + * Replaces the contents of this list with {@code menuItems}. + * {@code menuItems} must not contain duplicate menu items. + */ + public void setMenuItems(List items) { + requireAllNonNull(items); + if (!itemsAreUnique(items)) { + throw new DuplicateFoodException(); + } + + internalList.setAll(items); + } + + /** + * Sort Menu items in menu by name + * @param ascending decide whether to sort ascending or descending + */ + public void sortMenuItemByName(boolean ascending) { + Comparator itemComparator = Comparator.comparing(MenuItem::getName); + if (ascending) { + internalList.sort(itemComparator); + } else { + internalList.sort(itemComparator.reversed()); + } + } + + /** + * Sort Menu items in menu by price + * @param ascending decide whether to sort ascending or descending + */ + public void sortMenuItemByPrice(boolean ascending) { + Comparator itemComparator = Comparator.comparing(MenuItem::getPrice); + if (ascending) { + internalList.sort(itemComparator); + } else { + internalList.sort(itemComparator.reversed()); + } + } + + /** + * Gets the contents of the list. + * {@code menuItems} must not contain duplicate menuItems. + */ + public List getMenuItems() { + requireAllNonNull(internalList); + ArrayList menuItemList = new ArrayList<>(); + + for (MenuItem menuItem : internalList) { + menuItemList.add(new JsonAdaptedMenuItem(menuItem)); + } + return menuItemList; + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Menu // instanceof handles nulls + && internalList.equals(((Menu) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code menuItems} contains only unique menu items. + */ + private boolean itemsAreUnique(List items) { + for (int i = 0; i < items.size() - 1; i++) { + for (int j = i + 1; j < items.size(); j++) { + if (items.get(i).equals(items.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/address/model/menu/MenuManager.java b/src/main/java/seedu/address/model/menu/MenuManager.java new file mode 100644 index 00000000000..a50bda1eb76 --- /dev/null +++ b/src/main/java/seedu/address/model/menu/MenuManager.java @@ -0,0 +1,136 @@ +package seedu.address.model.menu; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.address.model.food.MenuItem; + + +/** + * Wraps all data at the address-book level + * Duplicates are not allowed (by .isSameFood comparison) + */ +public class MenuManager implements ReadOnlyMenuManager { + + private Menu menu; + + /* + * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication + * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html + * + * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * among constructors. + */ + { + menu = new Menu(); + } + + public MenuManager() {} + + /** + * Creates a MenuManager using the Foods in the {@code toBeCopied} + */ + public MenuManager(ReadOnlyMenuManager toBeCopied) { + this(); + resetData(toBeCopied); + } + + /** + * Creates a MenuManager using the menu in the {@code toBeCopied} + */ + public MenuManager(Menu toBeCopied) { + this.menu = toBeCopied; + } + + //// list overwrite operations + + /** + * Replaces the contents of the menu item list with {@code items}. + * {@code items} must not contain duplicate items. + */ + public void setMenu(List items) { + this.menu.setMenuItems(items); + } + + /** + * Resets the existing data of this {@code MenuManager} with {@code newData}. + */ + public void resetData(ReadOnlyMenuManager newData) { + requireNonNull(newData); + + setMenu(newData.getMenuItemList()); + } + + //// food-level operations + + /** + * Returns true if a food with the same identity as {@code food} exists in the menu manager. + */ + public boolean hasMenuItem(MenuItem item) { + requireNonNull(item); + return menu.contains(item); + } + + /** + * Adds a menuItem to the address book. + * The menuItem must not already exist in the address book. + */ + public void addMenuItem(MenuItem f) { + menu.add(f); + } + + /** + * Replaces the given menuItem {@code target} in the list with {@code editedMenuItem}. + * {@code target} must exist in the address book. + * The menuItem identity of {@code editedMenuItem} must not be the same as another existing menu item in the + * address book. + */ + public void setMenuItem(MenuItem target, MenuItem editedItem) { + requireNonNull(editedItem); + + menu.setMenuItem(target, editedItem); + } + + public void sortMenuItemByName(boolean ascending) { + menu.sortMenuItemByName(ascending); + } + + public void sortMenuItemByPrice(boolean ascending) { + menu.sortMenuItemByPrice(ascending); + } + + /** + * Removes {@code key} from this {@code MenuManager}. + * {@code key} must exist in the address book. + */ + public void removeMenuItem(MenuItem key) { + menu.remove(key); + } + + //// util methods + + @Override + public String toString() { + return menu.asUnmodifiableObservableList().size() + " foods"; + // TODO: refine later + } + + @Override + public ObservableList getMenuItemList() { + return menu.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof MenuManager // instanceof handles nulls + && menu.equals(((MenuManager) other).menu)); + } + + @Override + public int hashCode() { + return menu.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/menu/ReadOnlyMenuManager.java b/src/main/java/seedu/address/model/menu/ReadOnlyMenuManager.java new file mode 100644 index 00000000000..0642a6c65e4 --- /dev/null +++ b/src/main/java/seedu/address/model/menu/ReadOnlyMenuManager.java @@ -0,0 +1,17 @@ +package seedu.address.model.menu; + +import javafx.collections.ObservableList; +import seedu.address.model.food.MenuItem; + +/** + * Unmodifiable view of a menu + */ +public interface ReadOnlyMenuManager { + + /** + * Returns an unmodifiable view of the foods list. + * This list will not contain any duplicate foods. + */ + ObservableList getMenuItemList(); + +} diff --git a/src/main/java/seedu/address/model/order/Order.java b/src/main/java/seedu/address/model/order/Order.java new file mode 100644 index 00000000000..807a017db79 --- /dev/null +++ b/src/main/java/seedu/address/model/order/Order.java @@ -0,0 +1,227 @@ +package seedu.address.model.order; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.order.exceptions.DuplicateOrderItemException; +import seedu.address.model.order.exceptions.OrderItemNotFoundException; +import seedu.address.model.tag.Tag; +import seedu.address.model.vendor.Vendor; + +public class Order implements Iterable { + protected Vendor vendor; + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + public void setVendor(Vendor vendor) { + this.vendor = vendor; + } + + /** + * Returns true if the list contains an equivalent OrderItem as the given argument. + */ + public boolean contains(OrderItem toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameOrderItemDescription); + } + + /** + * Returns a copy of the order. The orderItems are also copies. + */ + public Order makeCopy() { + Order newOrder = new Order(); + for (OrderItem orderItem: internalList) { + newOrder.add(orderItem.makeCopy()); + } + return newOrder; + } + + /** + * Adds OrderItem to the list. + */ + public void add(OrderItem toAdd) { + requireNonNull(toAdd); + int newQuantity = toAdd.getQuantity() + getQuantity(toAdd); + if (newQuantity > 100) { + throw new IllegalArgumentException(); + } + assert(newQuantity <= 100); + if (contains(toAdd)) { + int index = internalList.indexOf(toAdd); + OrderItem existingItem = internalList.get(index); + toAdd.setQuantity(newQuantity); + toAdd.setTags(existingItem.getTags()); + setOrderItem(existingItem, toAdd); + } else { + internalList.add(toAdd); + } + } + + public OrderItem getOrderItem(String name) { + requireNonNull(name); + for (OrderItem orderItem: internalList) { + if (orderItem.getName().equals(name)) { + return orderItem; + } + } + throw new OrderItemNotFoundException(); + } + + /** + * Replaces the OrderItem {@code target} in the list with {@code editedOrderItem}. + * {@code target} must exist in the list. + * The OrderItem identity of {@code editedOrderItem} must not be the same as another existing OrderItem in the list. + */ + public void setOrderItem(OrderItem target, OrderItem editedOrderItem) { + requireAllNonNull(target, editedOrderItem); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new OrderItemNotFoundException(); + } + + if (!target.isSameOrderItemDescription(editedOrderItem) && contains(editedOrderItem)) { + throw new DuplicateOrderItemException(); + } + + internalList.set(index, editedOrderItem); + } + + /** + * Removes the equivalent OrderItem from the list. + * The OrderItem must exist in the list. + */ + public void remove(OrderItem toRemove) { + requireNonNull(toRemove); + if (!contains(toRemove)) { + throw new OrderItemNotFoundException(); + } + int index = internalList.indexOf(toRemove); + + OrderItem existingItem = internalList.get(index); + int currQty = existingItem.getQuantity(); + int newQty = currQty - toRemove.getQuantity(); + + if (!OrderItem.isValidQuantity(newQty)) { + // Remove all + internalList.remove(toRemove); + } else { + // Reduce + OrderItem reduce = new OrderItem(toRemove, newQty); + setOrderItem(existingItem, reduce); + } + } + + public void setOrder(Order replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + public void setOrderItems(List orderItems) { + requireAllNonNull(orderItems); + if (!orderItemsAreUnique(orderItems)) { + throw new DuplicateOrderItemException(); + } + internalList.setAll(orderItems); + } + + public double getTotal() { + double total = 0; + for (OrderItem orderItem: internalList) { + total += orderItem.getQuantity() * orderItem.getPrice(); + } + return total; + } + + @Override + public String toString() { + StringBuilder text = new StringBuilder(); + for (OrderItem orderItem: internalList) { + text.append(orderItem.toString() + '\n'); + } + return text.toString(); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + public int getQuantity(int index) { + assert(index < internalList.size()); + return internalList.get(index).getQuantity(); + } + + /** + * Gets the quantity of the orderItem in current order equal to the {@code orderItem}. Returns 0 if it doesn't + * exist. + */ + public int getQuantity(OrderItem orderItem) { + requireNonNull(orderItem); + try { + OrderItem foundItem = getOrderItem(orderItem.getName()); + return foundItem.getQuantity(); + } catch (OrderItemNotFoundException e) { + return 0; + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Order // instanceof handles nulls + && internalList.equals(((Order) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code orderItems} contains only unique orderItems. + */ + private boolean orderItemsAreUnique(List orderItems) { + for (int i = 0; i < orderItems.size() - 1; i++) { + for (int j = i + 1; j < orderItems.size(); j++) { + if (orderItems.get(i).isSameOrderItemDescription(orderItems.get(j))) { + return false; + } + } + } + return true; + } + + /** + * Tags an {@code orderItem} with a {@code tag}. + */ + public void tagOrderItem(OrderItem orderItem, Tag tag) { + OrderItem newOrderItem = orderItem.makeCopy(); + newOrderItem.addTag(tag); + setOrderItem(orderItem, newOrderItem); + } + + /** + * Clears all tag of {@code orderItem}. + */ + public void untagOrderItem(OrderItem orderItem) { + OrderItem newOrderItem = orderItem.makeCopy(); + newOrderItem.setTags(new HashSet<>()); + setOrderItem(orderItem, newOrderItem); + } +} diff --git a/src/main/java/seedu/address/model/order/OrderItem.java b/src/main/java/seedu/address/model/order/OrderItem.java new file mode 100644 index 00000000000..d2d1c479025 --- /dev/null +++ b/src/main/java/seedu/address/model/order/OrderItem.java @@ -0,0 +1,161 @@ +package seedu.address.model.order; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +import seedu.address.model.food.Food; +import seedu.address.model.food.MenuItem; +import seedu.address.model.tag.Tag; + +/** + * Represents an OrderItem in the Order. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class OrderItem extends Food { + // Identity fields + private int quantity; + + /** + * Every field must be present and not null. + */ + public OrderItem(String name, double price, Set tags, int quantity) { + super(name, price, tags); + requireAllNonNull(quantity); + this.quantity = quantity; + } + + /** + * Alternative constructor that takes in a menu item and quantity. Every field must be present and not null. + */ + public OrderItem(MenuItem menuItem, int quantity) { + super(menuItem.getName(), menuItem.getPrice(), menuItem.getTags()); + requireAllNonNull(quantity); + tags.clear(); + this.quantity = quantity; + } + + /** + * Alternative constructor that takes in a order item. Every field must be present and not null. + */ + public OrderItem(OrderItem orderItem, int quantity) { + super(orderItem.getName(), orderItem.getPrice(), orderItem.getTags()); + requireAllNonNull(quantity); + this.quantity = quantity; + } + + public OrderItem makeCopy() { + return new OrderItem(getName(), getPrice(), getTags(), getQuantity()); + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + public static boolean isValidQuantity(int quantity) { + return quantity > 0; + } + + /** + * Returns true only if both OrderItems have the same exact identity fields for all attributes. + */ + public boolean isSameOrderItem(OrderItem orderItem) { + if (orderItem == this) { + return true; + } + + return orderItem != null + && orderItem.getName().equals(getName()) + && (orderItem.getPrice() == (getPrice())) + && (orderItem.getQuantity() == getQuantity()) + && (orderItem.getTags().equals(getTags())); + } + + /** + * Returns true if both OrderItems have the same identity fields for all attributes except for quantity. + * This defines a weaker notion of equality between two OrderItems. + */ + public boolean isSameOrderItemDescription(OrderItem orderItem) { + if (orderItem == this) { + return true; + } + + return orderItem != null + && orderItem.getName().equals(getName()) + && (orderItem.getPrice() == (getPrice())); + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Set getTags() { + return Collections.unmodifiableSet(tags); + } + + @Override + public void setTags(Set newTags) { + tags.clear(); + tags.addAll(newTags); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof OrderItem)) { + return false; + } + + OrderItem otherFood = (OrderItem) other; + return otherFood.getName().equals(getName()) + && otherFood.getPrice() == getPrice(); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(super.name, super.price, super.tags, quantity); + } + + /** + * Creates a text suitable for order. + */ + public String toOrderText() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName() + " x " + getQuantity() + "\n"); + for (Tag tag: tags) { + builder.append(" - " + tag.tagName + "\n"); + } + return builder.toString(); + } + + public String addCommandString() { + return String.format("%s x%d", getName(), getQuantity()); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName()) + .append(" Price: ") + .append(getPriceString()) + .append(" Quantity: ") + .append(getQuantity()) + .append(" Tags: "); + getTags().forEach(builder::append); + return builder.toString(); + } + + public void addTag(Tag tag) { + tags.add(tag); + } +} diff --git a/src/main/java/seedu/address/model/order/OrderManager.java b/src/main/java/seedu/address/model/order/OrderManager.java new file mode 100644 index 00000000000..d42c3b5e9d6 --- /dev/null +++ b/src/main/java/seedu/address/model/order/OrderManager.java @@ -0,0 +1,182 @@ +package seedu.address.model.order; + +import static java.util.Objects.requireNonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +import javafx.collections.ObservableList; +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.tag.Tag; + +/** + * Wraps all data at the address-book level + * Duplicates are not allowed (by .isSameOrderItem comparison) + */ +public class OrderManager implements ReadOnlyOrderManager { + + private final Stack orderHistory; // head of orderHistory + private final Order order; + + /* + * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication + * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html + * + * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * among constructors. + */ + { + orderHistory = new Stack<>(); + order = new Order(); + saveChanges(); + } + + public OrderManager() {} + + /** + * Creates a OrderManager using the OrderItems in the {@code toBeCopied} + */ + public OrderManager(ReadOnlyOrderManager toBeCopied) { + this(); + resetData(toBeCopied); + } + + private void saveChanges() { + orderHistory.add(order.makeCopy()); + } + + /** + * Undoes the last change to the order. + */ + public void undoChanges() { + assert(orderHistory.size() > 1); + orderHistory.pop(); + order.setOrder(orderHistory.peek().makeCopy()); + } + + //// list overwrite operations + + /** + * Replaces the contents of the orderItem list with {@code orderItems}. + * {@code orderItems} must not contain duplicate orderItems. + */ + public void setOrder(List orderItems) { + this.order.setOrderItems(orderItems); + saveChanges(); + } + + /** + * Clears the contents of orderHistory. + */ + public void resetOrder() { + this.orderHistory.clear(); + this.order.setOrderItems(new ArrayList<>()); + saveChanges(); + } + + /** + * Resets the existing data of this {@code OrderManager} with {@code newData}. + */ + public void resetData(ReadOnlyOrderManager newData) { + requireNonNull(newData); + + setOrder(newData.getOrderItemList()); + } + + //// orderItem-level operations + + /** + * Returns true if a orderItem with the same identity as {@code orderItem} exists in the order manager. + */ + public boolean hasOrderItem(OrderItem orderItem) { + requireNonNull(orderItem); + return order.contains(orderItem); + } + + /** + * Adds a orderItem to the address book. + * The orderItem must not already exist in the address book. + */ + public void addOrderItem(OrderItem orderItem) throws CommandException { + try { + order.add(orderItem); + saveChanges(); + } catch (IllegalArgumentException e) { + throw new CommandException(Messages.MESSAGE_ORDERITEM_QUANTITY_EXCEED); + } + } + + + /** + * Replaces the given orderItem {@code target} in the list with {@code editedOrderItem}. + * {@code target} must exist in the address book. + * The orderItem identity of {@code editedOrderItem} must not be the same as another existing orderItem in the + * address book. + */ + public void setOrderItem(OrderItem target, OrderItem editedOrderItem) { + requireNonNull(editedOrderItem); + + order.setOrderItem(target, editedOrderItem); + saveChanges(); + } + + /** + * Removes {@code key} from this {@code OrderManager}. + * {@code key} must exist in the address book. + */ + public void removeOrderItem(OrderItem key) { + order.remove(key); + saveChanges(); + } + + public int getOrderHistorySize() { + return orderHistory.size(); + } + + //// util methods + + @Override + public String toString() { + return order.asUnmodifiableObservableList().size() + " orderItems"; + // TODO: refine later + } + + @Override + public ObservableList getOrderItemList() { + return order.asUnmodifiableObservableList(); + } + + public int getQuantity(int index) { + return order.getQuantity(index); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof OrderManager // instanceof handles nulls + && order.equals(((OrderManager) other).order)); + } + + @Override + public int hashCode() { + return order.hashCode(); + } + + /** + * Tags {@code orderItem} with {@code tag}. + */ + public void tagOrderItem(OrderItem orderItem, Tag tag) { + order.tagOrderItem(orderItem, tag); + saveChanges(); + } + + /** + * Clears all tag of {@code orderItem}. + */ + public void untagOrderItem(OrderItem orderItem) { + order.untagOrderItem(orderItem); + saveChanges();; + } +} diff --git a/src/main/java/seedu/address/model/order/ReadOnlyOrderManager.java b/src/main/java/seedu/address/model/order/ReadOnlyOrderManager.java new file mode 100644 index 00000000000..06c1300ade6 --- /dev/null +++ b/src/main/java/seedu/address/model/order/ReadOnlyOrderManager.java @@ -0,0 +1,14 @@ +package seedu.address.model.order; + +import javafx.collections.ObservableList; + +/** + * Unmodifiable view of an order + */ +public interface ReadOnlyOrderManager { + /** + * Returns an unmodifiable view of the OrderItem list. + * This list will not contain any duplicate OrderItems. + */ + ObservableList getOrderItemList(); +} diff --git a/src/main/java/seedu/address/model/order/exceptions/DuplicateOrderItemException.java b/src/main/java/seedu/address/model/order/exceptions/DuplicateOrderItemException.java new file mode 100644 index 00000000000..acb130c6f32 --- /dev/null +++ b/src/main/java/seedu/address/model/order/exceptions/DuplicateOrderItemException.java @@ -0,0 +1,7 @@ +package seedu.address.model.order.exceptions; + +public class DuplicateOrderItemException extends RuntimeException { + public DuplicateOrderItemException() { + super("Operation would result in duplicate order item"); + } +} diff --git a/src/main/java/seedu/address/model/order/exceptions/OrderItemNotFoundException.java b/src/main/java/seedu/address/model/order/exceptions/OrderItemNotFoundException.java new file mode 100644 index 00000000000..dff5937e10f --- /dev/null +++ b/src/main/java/seedu/address/model/order/exceptions/OrderItemNotFoundException.java @@ -0,0 +1,10 @@ +package seedu.address.model.order.exceptions; + +/** + * Signals that the operation is unable to find the specified food. + */ +public class OrderItemNotFoundException extends RuntimeException { + public OrderItemNotFoundException() { + super("OrderItem not found"); + } +} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java deleted file mode 100644 index 0fee4fe57e6..00000000000 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ /dev/null @@ -1,137 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Iterator; -import java.util.List; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; - -/** - * A list of persons that enforces uniqueness between its elements and does not allow nulls. - * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of - * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is - * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so - * as to ensure that the person with exactly the same fields will be removed. - * - * Supports a minimal set of list operations. - * - * @see Person#isSamePerson(Person) - */ -public class UniquePersonList implements Iterable { - - private final ObservableList internalList = FXCollections.observableArrayList(); - private final ObservableList internalUnmodifiableList = - FXCollections.unmodifiableObservableList(internalList); - - /** - * Returns true if the list contains an equivalent person as the given argument. - */ - public boolean contains(Person toCheck) { - requireNonNull(toCheck); - return internalList.stream().anyMatch(toCheck::isSamePerson); - } - - /** - * Adds a person to the list. - * The person must not already exist in the list. - */ - public void add(Person toAdd) { - requireNonNull(toAdd); - if (contains(toAdd)) { - throw new DuplicatePersonException(); - } - internalList.add(toAdd); - } - - /** - * Replaces the person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the list. - * The person identity of {@code editedPerson} must not be the same as another existing person in the list. - */ - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - int index = internalList.indexOf(target); - if (index == -1) { - throw new PersonNotFoundException(); - } - - if (!target.isSamePerson(editedPerson) && contains(editedPerson)) { - throw new DuplicatePersonException(); - } - - internalList.set(index, editedPerson); - } - - /** - * Removes the equivalent person from the list. - * The person must exist in the list. - */ - public void remove(Person toRemove) { - requireNonNull(toRemove); - if (!internalList.remove(toRemove)) { - throw new PersonNotFoundException(); - } - } - - public void setPersons(UniquePersonList replacement) { - requireNonNull(replacement); - internalList.setAll(replacement.internalList); - } - - /** - * Replaces the contents of this list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - requireAllNonNull(persons); - if (!personsAreUnique(persons)) { - throw new DuplicatePersonException(); - } - - internalList.setAll(persons); - } - - /** - * Returns the backing list as an unmodifiable {@code ObservableList}. - */ - public ObservableList asUnmodifiableObservableList() { - return internalUnmodifiableList; - } - - @Override - public Iterator iterator() { - return internalList.iterator(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof UniquePersonList // instanceof handles nulls - && internalList.equals(((UniquePersonList) other).internalList)); - } - - @Override - public int hashCode() { - return internalList.hashCode(); - } - - /** - * Returns true if {@code persons} contains only unique persons. - */ - private boolean personsAreUnique(List persons) { - for (int i = 0; i < persons.size() - 1; i++) { - for (int j = i + 1; j < persons.size(); j++) { - if (persons.get(i).isSamePerson(persons.get(j))) { - return false; - } - } - } - return true; - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java deleted file mode 100644 index d7290f59442..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ /dev/null @@ -1,11 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same - * identity). - */ -public class DuplicatePersonException extends RuntimeException { - public DuplicatePersonException() { - super("Operation would result in duplicate persons"); - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java deleted file mode 100644 index fa764426ca7..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ /dev/null @@ -1,6 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation is unable to find the specified person. - */ -public class PersonNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/preset/Preset.java b/src/main/java/seedu/address/model/preset/Preset.java new file mode 100644 index 00000000000..9150c895ed7 --- /dev/null +++ b/src/main/java/seedu/address/model/preset/Preset.java @@ -0,0 +1,74 @@ +package seedu.address.model.preset; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.order.OrderItem; + +public class Preset { + public static final String MESSAGE_DUPLICATE_ORDERITEM = "Order contains duplicate orderItems."; + + private String name; + private List orderItems; + + /** + * Creates a Preset with the given name and the order items. + */ + public Preset(String name, List orderItems) { + this.name = name; + this.orderItems = orderItems; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public List getOrderItems() { + return this.orderItems; + } + + /** + * Adds an order item to the preset. + */ + public void addOrderItem(OrderItem orderItem) throws IllegalValueException { + if (orderItems.contains(orderItem)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_ORDERITEM); + } + orderItems.add(orderItem); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Preset)) { + return false; + } + + Preset otherPreset = (Preset) other; + return otherPreset.getName().equals(getName()) + && otherPreset.getOrderItems().equals(getOrderItems()); + } + + /////// Methods below are not in use + + /** + * Checks whether {@code orderItem} exists in Preset + */ + public boolean contains(OrderItem orderItem) { + requireNonNull(orderItem); + return this.orderItems.contains(orderItem); + } + + public void addAllOrderItems(List orderItems) { + this.orderItems.addAll(orderItems); + } +} diff --git a/src/main/java/seedu/address/model/profile/Profile.java b/src/main/java/seedu/address/model/profile/Profile.java new file mode 100644 index 00000000000..5d5ddff6b19 --- /dev/null +++ b/src/main/java/seedu/address/model/profile/Profile.java @@ -0,0 +1,42 @@ +package seedu.address.model.profile; + +import seedu.address.model.vendor.Address; +import seedu.address.model.vendor.Phone; + +public class Profile { + private Address address; + private Phone phone; + + /** + * Profile constructor + * @param address address of profile as String + * @param phone phone number of profile as String + */ + public Profile(Phone phone, Address address) { + this.phone = phone; + this.address = address; + } + + public Address getAddress() { + return this.address; + } + + public Phone getPhone() { + return this.phone; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Profile)) { + return false; + } + + Profile otherPreset = (Profile) other; + return otherPreset.getAddress().equals(getAddress()) + && otherPreset.getPhone().equals(getPhone()); + } +} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java index b0ea7e7dad7..734cc930e02 100644 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ b/src/main/java/seedu/address/model/tag/Tag.java @@ -3,6 +3,9 @@ import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * Represents a Tag in the address book. * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} @@ -10,7 +13,7 @@ public class Tag { public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric"; - public static final String VALIDATION_REGEX = "\\p{Alnum}+"; + public static final String VALIDATION_REGEX = "^[A-Za-z0-9 ]+$"; public final String tagName; @@ -29,7 +32,13 @@ public Tag(String tagName) { * Returns true if a given string is a valid tag name. */ public static boolean isValidTagName(String test) { - return test.matches(VALIDATION_REGEX); + Pattern p = Pattern.compile(VALIDATION_REGEX); + Matcher m = p.matcher(test); + return m.matches(); + } + + public Tag makeCopy() { + return new Tag(tagName); } @Override diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 1806da4facf..813954e48c4 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -1,49 +1,148 @@ package seedu.address.model.util; import java.util.Arrays; +import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; +import seedu.address.model.food.MenuItem; +import seedu.address.model.menu.Menu; import seedu.address.model.tag.Tag; +import seedu.address.model.vendor.Address; +import seedu.address.model.vendor.Email; +import seedu.address.model.vendor.Name; +import seedu.address.model.vendor.Phone; +import seedu.address.model.vendor.ReadOnlyVendorManager; +import seedu.address.model.vendor.Vendor; +import seedu.address.model.vendor.VendorManager; + + /** - * Contains utility methods for populating {@code AddressBook} with sample data. + * Contains utility methods for populating {@code VendorManager} with sample data. */ +//todo: Add the image urls under the file path parameter here public class SampleDataUtil { - public static Person[] getSamplePersons() { - return new Person[] { - new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), - new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), - new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), - new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), - new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), - new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) + + public static Vendor[] getSampleVendors() { + HashSet starters = new HashSet<>(); + starters.add(new Tag("Starter")); + + HashSet drinks = new HashSet<>(); + drinks.add(new Tag("Drink")); + + HashSet hotdogs = new HashSet<>(); + hotdogs.add(new Tag("HotDog")); + + HashSet burgers = new HashSet<>(); + burgers.add(new Tag("Burger")); + + Menu menu1 = new Menu(); + menu1.add(new MenuItem("Butter Chicken", 7, new HashSet<>(), "al_amaan/butter_chicken.png")); + menu1.add(new MenuItem("Pattaya", 5.5, new HashSet<>(), "al_amaan/pattaya.png")); + + HashSet vegTags = new HashSet<>(); + HashSet spicyTags = new HashSet<>(); + spicyTags.add(new Tag("Spicy")); + vegTags.add(new Tag("Vegetarian")); + + menu1.add(new MenuItem("Veg Briyani", 5, new HashSet<>(vegTags), + "al_amaan/veg_briyani.png")); + menu1.add(new MenuItem("Cheese Fries", 4, new HashSet<>(), + "al_amaan/cheese_fries.png")); + menu1.add(new MenuItem("Kampong Style Fried Rice", 4.8, new HashSet<>(), + "al_amaan/kampong.png")); + //note that this is fried rice + menu1.add(new MenuItem("Sambal Chicken Fried Rice", 4.8, new HashSet<>(), + "al_amaan/sambal_chicken.png")); + menu1.add(new MenuItem("Roti John", 4, new HashSet<>(), "al_amaan/roti_john.png")); + + // Add cold and hot + menu1.add(new MenuItem("Milo Cold", 1.5, drinks, "al_amaan/milo.png")); + menu1.add(new MenuItem("Milo Hot", 1.3, drinks, "invalid.png")); + + menu1.add(new MenuItem("Milo Dinosaur", 2.5, drinks, "al_amaan/milo_dino.png")); + menu1.add(new MenuItem("Milo Godzilla", 3, drinks, + "al_amaan/milo_god.png")); + + + Menu menu2 = new Menu(); + HashSet tagList = new HashSet<>(); + tagList.add(new Tag("Egg")); + tagList.add(new Tag("Cheese")); + menu2.add(new MenuItem("Pattaya", 6.5, new HashSet<>(), "makan_xpress/pattaya.png")); + menu2.add(new MenuItem("Roti John Chicken", 5.5, new HashSet<>(), + "al_amaan/roti_john.png")); + menu2.add(new MenuItem("Meatball Spaghetti", 7.7, new HashSet<>(), + "makan_xpress/meatball_spagetti.png")); + menu2.add(new MenuItem("Naan", 1.7, new HashSet<>(), "makan_xpress/naan.png")); + menu2.add(new MenuItem("Tandoori", 6.5, new HashSet<>(), "makan_xpress/tandoori.png")); + menu2.add(new MenuItem("Plain Prata", 1.2, new HashSet<>(), "makan_xpress/prata.png")); + menu2.add(new MenuItem("Tom Yam", 6.5, new HashSet<>(), "makan_xpress/tomyam.png")); + menu2.add(new MenuItem("Nasi Goreng Ikan Bilis", 4.5, new HashSet<>(), + "makan_xpress/nasi_goreng.png")); + menu2.add(new MenuItem("Milo Cold", 2, drinks, "al_amaan/milo.png")); + menu2.add(new MenuItem("Teh Ping Cold", 2, drinks, "al_amaan/teh_ping.png")); + + + Menu menu3 = new Menu(); + menu3.add(new MenuItem("Bacon & Cheese Hotdog", 13.90, hotdogs, + "de_frank/bacon_cheese_hotdog.png")); + menu3.add(new MenuItem("Mozzarella Corn Dog (2pcs)", 12.90, hotdogs, + "de_frank/corn_dog.png")); + menu3.add(new MenuItem("Sauerkraut Hotdog", 11.90, hotdogs, + "de_frank/sauerkraut_hotdog.png")); + menu3.add(new MenuItem("Signature Ribeye Burger", 17.90, burgers, + "de_frank/ribeye_burger.png")); + menu3.add(new MenuItem("Chicken Cutlet Burger", 13.90, burgers, + "de_frank/chicken_burger.png")); + menu3.add(new MenuItem("Breaded Fish Burger", 13.90, burgers, + "de_frank/fish_burger.png")); + menu3.add(new MenuItem("Foie Gras Sliced Beef Roll", 18.90, hotdogs, + "invalid.png")); + menu3.add(new MenuItem("Cheese Steak Sliders (3pcs)", 16.90, burgers, + "de_frank/cheese_steak_slider.png")); + + Menu menu4 = new Menu(); + menu4.add(new MenuItem("Ayam Taliwang Dada/Taliwang Chicken Breast", 9.20, spicyTags, + "ayam_tali_wang/ayam_taliwang_dada.png")); + menu4.add(new MenuItem("Ayam Taliwang Peha/ Taliwang Chicken Thigh", 9.80, spicyTags, + "ayam_tali_wang/ayam_taliwang_peha.png")); + menu4.add(new MenuItem("Taliwang Dory/ Taliwang Pan Fried Fillet Dory", 9.50, spicyTags, + "ayam_tali_wang/taliwang_dory.png")); + menu4.add(new MenuItem("Tahu & Tempeh Goreng/ Fried Tofu & Tempeh", 4.70, starters, + "ayam_tali_wang/tahu_tempeh.png")); + menu4.add(new MenuItem("Keropok", 1.50, starters, + "ayam_tali_wang/keropok.png")); + menu4.add(new MenuItem("Jus Jeruk Nipis / Lime Juice", 2.50, drinks, + "ayam_tali_wang/lime_juice.png")); + menu4.add(new MenuItem("Teh Botol Sosro / Indonesian Bottled Tea Original", 2.50, drinks, + "ayam_tali_wang/teh_botol.png")); + menu4.add(new MenuItem("Minuman Kaleng / Canned Drink Coke", 2.30, drinks, + "ayam_tali_wang/coke.png")); + + return new Vendor[]{ + new Vendor(new Name("Al Amaan Restaurant"), new Phone("67740637"), + new Email("alamaanrestaurant@gmail.com"), + new Address("12 Clementi Road, Singapore 129742"), + getTagSet("halal"), menu1), + new Vendor(new Name("Xpress Makan Avenue"), new Phone("91076367"), new Email("xpressmakanavenue@gmail.com"), + new Address("14 Clementi Road, Singapore 129743"), + getTagSet("halal"), menu2), + new Vendor(new Name("BeFrank"), new Phone("97652509"), new Email("beFrank99@gmail.com"), + new Address("28 Clementi Road, Singapore 129754"), + getTagSet("western"), menu3), + new Vendor(new Name("Ayam Taliwang"), new Phone("69048773"), + new Email("taliwangIndonesianrestaurant@gmail.com"), + new Address("440 Pasir Panjang Road 118782 Singapore"), + getTagSet("indonesian"), menu4) }; } - public static ReadOnlyAddressBook getSampleAddressBook() { - AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); + public static ReadOnlyVendorManager getSampleVendorManager() { + VendorManager sampleAb = new VendorManager(); + for (Vendor sampleVendor : getSampleVendors()) { + sampleAb.addVendor(sampleVendor); } return sampleAb; } diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/vendor/Address.java similarity index 94% rename from src/main/java/seedu/address/model/person/Address.java rename to src/main/java/seedu/address/model/vendor/Address.java index 60472ca22a0..72df01239b7 100644 --- a/src/main/java/seedu/address/model/person/Address.java +++ b/src/main/java/seedu/address/model/vendor/Address.java @@ -1,10 +1,10 @@ -package seedu.address.model.person; +package seedu.address.model.vendor; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's address in the address book. + * Represents a vendor's address in the address book. * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} */ public class Address { diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/vendor/Email.java similarity index 96% rename from src/main/java/seedu/address/model/person/Email.java rename to src/main/java/seedu/address/model/vendor/Email.java index a5bbe0b6a5f..76be12d4d8c 100644 --- a/src/main/java/seedu/address/model/person/Email.java +++ b/src/main/java/seedu/address/model/vendor/Email.java @@ -1,10 +1,10 @@ -package seedu.address.model.person; +package seedu.address.model.vendor; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's email in the address book. + * Represents a vendor's email in the address book. * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} */ public class Email { diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/vendor/Name.java similarity index 94% rename from src/main/java/seedu/address/model/person/Name.java rename to src/main/java/seedu/address/model/vendor/Name.java index 79244d71cf7..9973d91f937 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/seedu/address/model/vendor/Name.java @@ -1,10 +1,10 @@ -package seedu.address.model.person; +package seedu.address.model.vendor; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's name in the address book. + * Represents a vendor's name in the address book. * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} */ public class Name { diff --git a/src/main/java/seedu/address/model/vendor/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/vendor/NameContainsKeywordsPredicate.java new file mode 100644 index 00000000000..5a6692f7c56 --- /dev/null +++ b/src/main/java/seedu/address/model/vendor/NameContainsKeywordsPredicate.java @@ -0,0 +1,34 @@ +package seedu.address.model.vendor; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; +import seedu.address.model.food.MenuItem; + +/** + * Tests that a {@code Vendor}'s {@code Name} matches any of the keywords given. + */ +public class NameContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public NameContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(MenuItem menuItem) { + StringBuilder sentence = new StringBuilder(menuItem.getName() + ' '); + menuItem.getTags().forEach(x -> sentence.append(x.tagName).append(' ')); + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(sentence.toString(), keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof NameContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((NameContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/vendor/Phone.java similarity index 71% rename from src/main/java/seedu/address/model/person/Phone.java rename to src/main/java/seedu/address/model/vendor/Phone.java index 872c76b382f..b8f34d9723d 100644 --- a/src/main/java/seedu/address/model/person/Phone.java +++ b/src/main/java/seedu/address/model/vendor/Phone.java @@ -1,18 +1,22 @@ -package seedu.address.model.person; +package seedu.address.model.vendor; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** - * Represents a Person's phone number in the address book. + * Represents a Vendor's phone number in the address book. * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} */ public class Phone { public static final String MESSAGE_CONSTRAINTS = - "Phone numbers should only contain numbers, and it should be at least 3 digits long"; - public static final String VALIDATION_REGEX = "\\d{3,}"; + "Phone numbers should only contain numbers, and it should be a valid phone number" + + " (First digit must start with a 6/8/9 and must be 8 digits long)"; + public static final String VALIDATION_REGEX = "[689][\\d]{7}"; public final String value; /** @@ -30,7 +34,9 @@ public Phone(String phone) { * Returns true if a given string is a valid phone number. */ public static boolean isValidPhone(String test) { - return test.matches(VALIDATION_REGEX); + Pattern p = Pattern.compile(VALIDATION_REGEX); + Matcher m = p.matcher(test); + return m.matches(); } @Override diff --git a/src/main/java/seedu/address/model/vendor/ReadOnlyVendorManager.java b/src/main/java/seedu/address/model/vendor/ReadOnlyVendorManager.java new file mode 100644 index 00000000000..24d17e600a7 --- /dev/null +++ b/src/main/java/seedu/address/model/vendor/ReadOnlyVendorManager.java @@ -0,0 +1,21 @@ +package seedu.address.model.vendor; + +import javafx.collections.ObservableList; + +/** + * Unmodifiable view of a vendor manager + */ +public interface ReadOnlyVendorManager { + + /** + * Returns an unmodifiable view of the vendors list. + * This list will not contain any duplicate vendors. + */ + ObservableList getVendorList(); + + /** + * Returns an unmodifiable view of the selected vendor index. + */ + int getVendorIndex(); + +} diff --git a/src/main/java/seedu/address/model/vendor/UniqueVendorList.java b/src/main/java/seedu/address/model/vendor/UniqueVendorList.java new file mode 100644 index 00000000000..2a6d2fb8e1f --- /dev/null +++ b/src/main/java/seedu/address/model/vendor/UniqueVendorList.java @@ -0,0 +1,137 @@ +package seedu.address.model.vendor; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.vendor.exceptions.DuplicateVendorException; +import seedu.address.model.vendor.exceptions.VendorNotFoundException; + +/** + * A list of vendors that enforces uniqueness between its elements and does not allow nulls. + * A vendor is considered unique by comparing using {@code Vendor#isSameVendor(Vendor)}. As such, adding and updating of + * vendors uses Vendor#isSameVendor(Vendor) for equality so as to ensure that the vendor being added or updated is + * unique in terms of identity in the UniqueVendorList. However, the removal of a vendor uses Vendor#equals(Object) so + * as to ensure that the vendor with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Vendor#isSameVendor(Vendor) + */ +public class UniqueVendorList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent vendor as the given argument. + */ + public boolean contains(Vendor toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameVendor); + } + + /** + * Adds a vendor to the list. + * The vendor must not already exist in the list. + */ + public void add(Vendor toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateVendorException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the vendor {@code target} in the list with {@code editedVendor}. + * {@code target} must exist in the list. + * The vendor identity of {@code editedVendor} must not be the same as another existing vendor in the list. + */ + public void setVendor(Vendor target, Vendor editedVendor) { + requireAllNonNull(target, editedVendor); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new VendorNotFoundException(); + } + + if (!target.isSameVendor(editedVendor) && contains(editedVendor)) { + throw new DuplicateVendorException(); + } + + internalList.set(index, editedVendor); + } + + /** + * Removes the equivalent vendor from the list. + * The vendor must exist in the list. + */ + public void remove(Vendor toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new VendorNotFoundException(); + } + } + + public void setVendors(UniqueVendorList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code vendors}. + * {@code vendors} must not contain duplicate vendors. + */ + public void setVendors(List vendors) { + requireAllNonNull(vendors); + if (!vendorsAreUnique(vendors)) { + throw new DuplicateVendorException(); + } + + internalList.setAll(vendors); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueVendorList // instanceof handles nulls + && internalList.equals(((UniqueVendorList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code vendors} contains only unique vendors. + */ + private boolean vendorsAreUnique(List vendors) { + for (int i = 0; i < vendors.size() - 1; i++) { + for (int j = i + 1; j < vendors.size(); j++) { + if (vendors.get(i).isSameVendor(vendors.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/vendor/Vendor.java similarity index 57% rename from src/main/java/seedu/address/model/person/Person.java rename to src/main/java/seedu/address/model/vendor/Vendor.java index 557a7a60cd5..709de024bd0 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/seedu/address/model/vendor/Vendor.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.address.model.vendor; import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; @@ -7,33 +7,28 @@ import java.util.Objects; import java.util.Set; +import seedu.address.model.menu.Menu; import seedu.address.model.tag.Tag; -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. - */ -public class Person { - - // Identity fields +public class Vendor { private final Name name; private final Phone phone; private final Email email; - - // Data fields private final Address address; + private final Menu menu; private final Set tags = new HashSet<>(); /** * Every field must be present and not null. */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - requireAllNonNull(name, phone, email, address, tags); + public Vendor(Name name, Phone phone, Email email, Address address, Set tags, Menu menu) { + requireAllNonNull(name, phone, email, address, tags, menu); this.name = name; this.phone = phone; this.email = email; this.address = address; this.tags.addAll(tags); + this.menu = menu; } public Name getName() { @@ -52,6 +47,10 @@ public Address getAddress() { return address; } + public Menu getMenu() { + return menu; + } + /** * Returns an immutable tag set, which throws {@code UnsupportedOperationException} * if modification is attempted. @@ -61,22 +60,22 @@ public Set getTags() { } /** - * Returns true if both persons of the same name have at least one other identity field that is the same. - * This defines a weaker notion of equality between two persons. + * Returns true if both vendors of the same name have at least one other identity field that is the same. + * This defines a weaker notion of equality between two vendors. */ - public boolean isSamePerson(Person otherPerson) { - if (otherPerson == this) { + public boolean isSameVendor(Vendor otherVendor) { + if (otherVendor == this) { return true; } - return otherPerson != null - && otherPerson.getName().equals(getName()) - && (otherPerson.getPhone().equals(getPhone()) || otherPerson.getEmail().equals(getEmail())); + return otherVendor != null + && otherVendor.getName().equals(getName()) + && (otherVendor.getPhone().equals(getPhone()) || otherVendor.getEmail().equals(getEmail())); } /** - * Returns true if both persons have the same identity and data fields. - * This defines a stronger notion of equality between two persons. + * Returns true if both vendors have the same identity and data fields. + * This defines a stronger notion of equality between two vendors. */ @Override public boolean equals(Object other) { @@ -84,22 +83,23 @@ public boolean equals(Object other) { return true; } - if (!(other instanceof Person)) { + if (!(other instanceof Vendor)) { return false; } - Person otherPerson = (Person) other; - return otherPerson.getName().equals(getName()) - && otherPerson.getPhone().equals(getPhone()) - && otherPerson.getEmail().equals(getEmail()) - && otherPerson.getAddress().equals(getAddress()) - && otherPerson.getTags().equals(getTags()); + Vendor otherVendor = (Vendor) other; + return otherVendor.getName().equals(getName()) + && otherVendor.getPhone().equals(getPhone()) + && otherVendor.getEmail().equals(getEmail()) + && otherVendor.getAddress().equals(getAddress()) + && otherVendor.getTags().equals(getTags()) + && otherVendor.getMenu().equals(getMenu()); } @Override public int hashCode() { // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); + return Objects.hash(name, phone, email, address, tags, menu); } @Override @@ -114,6 +114,7 @@ public String toString() { .append(getAddress()) .append(" Tags: "); getTags().forEach(builder::append); + //TODO append menu? return builder.toString(); } diff --git a/src/main/java/seedu/address/model/vendor/VendorManager.java b/src/main/java/seedu/address/model/vendor/VendorManager.java new file mode 100644 index 00000000000..ccf1e712209 --- /dev/null +++ b/src/main/java/seedu/address/model/vendor/VendorManager.java @@ -0,0 +1,150 @@ +package seedu.address.model.vendor; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; + +/** + * Wraps all data at the vendor manager level + * Duplicates are not allowed (by .isSameVendor comparison) + */ +public class VendorManager implements ReadOnlyVendorManager { + + private final UniqueVendorList vendors; + private int vendorIndex; + + /* + * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication + * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html + * + * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * among constructors. + */ { + vendors = new UniqueVendorList(); + + } + + /** + * Default vendor index is -1. Assumes that there is no vendor chosen yet. + */ + public VendorManager() { + this.vendorIndex = -1; + } + + /** + * Creates an VendorManager using the Vendors in the {@code toBeCopied} + */ + public VendorManager(ReadOnlyVendorManager toBeCopied) { + this(); + resetData(toBeCopied); + } + + /** + * Sets the vendorIndex of the VendorManager to {@code vendorIndex}. This method is only called when + * the selectVendor method is called. + */ + private VendorManager(List vendors, int vendorIndex) { + setVendors(vendors); + this.vendorIndex = vendorIndex; + } + + //// list overwrite operations + + /** + * Replaces the contents of the vendor list with {@code vendors}. + * {@code vendors} must not contain duplicate vendors. + */ + + public void setVendors(List vendors) { + this.vendors.setVendors(vendors); + } + + /** + * Resets the existing data of this {@code VendorManager} with {@code newData}. + */ + public void resetData(ReadOnlyVendorManager newData) { + requireNonNull(newData); + setVendors(newData.getVendorList()); + selectVendor(newData.getVendorIndex()); + } + + //// vendor-level operations + + /** + * Returns true if a vendor with the same identity as {@code vendor} exists in the address book. + */ + public boolean hasVendor(Vendor vendor) { + requireNonNull(vendor); + return vendors.contains(vendor); + } + + /** + * Adds a vendor to the vendor manager. + * The vendor must not already exist in the vendor manager. + */ + public void addVendor(Vendor p) { + vendors.add(p); + } + + /** + * Replaces the given vendor {@code target} in the list with {@code editedVendor}. + * {@code target} must exist in the address book. + * The vendor identity of {@code editedVendor} must not be the same as another existing vendor in the address book. + */ + public void setVendor(Vendor target, Vendor editedVendor) { + requireNonNull(editedVendor); + + vendors.setVendor(target, editedVendor); + } + + public void selectVendor(int vendorIndex) { + this.vendorIndex = vendorIndex; + } + + @Override + public int getVendorIndex() { + return this.vendorIndex; + } + + public Vendor getSelectedVendor() { + return this.vendors.asUnmodifiableObservableList().get(vendorIndex); + } + + /** + * Removes {@code key} from this {@code VendorManager}. + * {@code key} must exist in the address book. + */ + public void removeVendor(Vendor key) { + vendors.remove(key); + } + + //// util methods + + @Override + public String toString() { + return vendors.asUnmodifiableObservableList().size() + " vendors"; + // TODO: refine later + } + + @Override + public ObservableList getVendorList() { + return vendors.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof VendorManager // instanceof handles nulls + && vendors.equals(((VendorManager) other).vendors)) + && this.getVendorIndex() == ((VendorManager) other).getVendorIndex(); + } + + @Override + public int hashCode() { + return vendors.hashCode(); + } + + +} diff --git a/src/main/java/seedu/address/model/vendor/exceptions/DuplicateVendorException.java b/src/main/java/seedu/address/model/vendor/exceptions/DuplicateVendorException.java new file mode 100644 index 00000000000..2d1f0d474b6 --- /dev/null +++ b/src/main/java/seedu/address/model/vendor/exceptions/DuplicateVendorException.java @@ -0,0 +1,11 @@ +package seedu.address.model.vendor.exceptions; + +/** + * Signals that the operation will result in duplicate Vendors (Vendors are considered duplicates if they have the same + * properties). + */ +public class DuplicateVendorException extends RuntimeException { + public DuplicateVendorException() { + super("Operation would result in duplicate vendors"); + } +} diff --git a/src/main/java/seedu/address/model/vendor/exceptions/VendorNotFoundException.java b/src/main/java/seedu/address/model/vendor/exceptions/VendorNotFoundException.java new file mode 100644 index 00000000000..15591377079 --- /dev/null +++ b/src/main/java/seedu/address/model/vendor/exceptions/VendorNotFoundException.java @@ -0,0 +1,10 @@ +package seedu.address.model.vendor.exceptions; + +/** + * Signals that the operation is unable to find the specified vendor. + */ +public class VendorNotFoundException extends RuntimeException { + public VendorNotFoundException() { + super("Vendor not found"); + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedMenuItem.java b/src/main/java/seedu/address/storage/JsonAdaptedMenuItem.java new file mode 100644 index 00000000000..ca9ad2f8502 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedMenuItem.java @@ -0,0 +1,84 @@ +package seedu.address.storage; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.food.Food; +import seedu.address.model.food.MenuItem; +import seedu.address.model.tag.Tag; + +/** + * Jackson-friendly version of {@link seedu.address.model.food.Food}. + */ +public class JsonAdaptedMenuItem { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Food's %s field is missing!"; + public static final String INVALID_PRICE_FORMAT = "Price must be a double and non-negative."; + + private final String name; + private final double price; + private final String filePath; + private final List tagged = new ArrayList<>(); + + /** + * Constructs a {@code JsonAdaptedFood} with the given food details. + */ + @JsonCreator + public JsonAdaptedMenuItem(@JsonProperty("name") String name, @JsonProperty("price") double price, + @JsonProperty("tagged") List tagged, + @JsonProperty("imagePath") String filePath) { + this.name = name; + this.price = price; + if (tagged != null) { + this.tagged.addAll(tagged); + } + this.filePath = filePath; + } + + /** + * Converts a given {@code Food} into this class for Jackson use. + */ + public JsonAdaptedMenuItem(MenuItem source) { + name = source.getName(); + price = source.getPrice(); + tagged.addAll(source.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); + filePath = source.getFilePath(); + } + + /** + * Converts this Jackson-friendly adapted menu item object into the model's {@code MenuItem} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted food. + */ + public MenuItem toModelType() throws IllegalValueException { + final List foodTags = new ArrayList<>(); + for (JsonAdaptedTag tag : tagged) { + foodTags.add(tag.toModelType()); + } + + if (name == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "Name")); + } + + if (!Food.isValidPrice(price)) { + throw new IllegalValueException("Price must be a double and non-negative."); + } + + if (filePath == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "Image")); + } + + final Set modelTags = new HashSet<>(foodTags); + return new MenuItem(name, price, modelTags, filePath); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedOrderItem.java b/src/main/java/seedu/address/storage/JsonAdaptedOrderItem.java new file mode 100644 index 00000000000..e80da1b5831 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedOrderItem.java @@ -0,0 +1,85 @@ +package seedu.address.storage; + +import static seedu.address.storage.JsonAdaptedMenuItem.INVALID_PRICE_FORMAT; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.order.OrderItem; +import seedu.address.model.tag.Tag; + +/** + * Jackson-friendly version of {@link seedu.address.model.order.OrderItem}. + */ +class JsonAdaptedOrderItem { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "OrderItem's %s field is missing!"; + + private final String name; + private final double price; + private final List tagged = new ArrayList<>(); + private final int quantity; + + /** + * Constructs a {@code JsonAdaptedOrderItem} with the given orderItem details. + */ + @JsonCreator + public JsonAdaptedOrderItem(@JsonProperty("name") String name, @JsonProperty("price") double price, + @JsonProperty("tagged") List tagged, + @JsonProperty("quantity") int quantity) { + this.name = name; + this.price = price; + if (tagged != null) { + this.tagged.addAll(tagged); + } + this.quantity = quantity; + } + + /** + * Converts a given {@code OrderItem} into this class for Jackson use. + */ + public JsonAdaptedOrderItem(OrderItem source) { + name = source.getName(); + price = source.getPrice(); + quantity = source.getQuantity(); + tagged.addAll(source.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); + } + + + /** + * Converts this Jackson-friendly adapted orderItem object into the model's {@code OrderItem} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted orderItem. + */ + public OrderItem toModelType() throws IllegalValueException { + final List personTags = new ArrayList<>(); + for (JsonAdaptedTag tag : tagged) { + personTags.add(tag.toModelType()); + } + + if (name == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "Name")); + } + + if (!OrderItem.isValidPrice(price)) { + throw new IllegalValueException(INVALID_PRICE_FORMAT); + } + + if (!OrderItem.isValidQuantity(quantity)) { + throw new IllegalValueException("Quantity must be positive."); + } + + final Set modelTags = new HashSet<>(personTags); + return new OrderItem(name, price, modelTags, quantity); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPreset.java b/src/main/java/seedu/address/storage/JsonAdaptedPreset.java new file mode 100644 index 00000000000..3545307161e --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedPreset.java @@ -0,0 +1,77 @@ +package seedu.address.storage; + + +import java.util.List; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.order.OrderItem; +import seedu.address.model.preset.Preset; + +public class JsonAdaptedPreset { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Preset's %s field is missing!"; + + private String name; + private List orderItems; + + + /** + * Constructs a {@code JsonAdaptedPreset} with the given person details. + */ + @JsonCreator + public JsonAdaptedPreset(@JsonProperty("name") String name, + @JsonProperty("orderItems") List orderItems) { + this.name = name; + this.orderItems = orderItems; + } + + /** + * Converts a given {@code Preset} into this class for Jackson use. + */ + public JsonAdaptedPreset(Preset source) { + this.name = source.getName(); + this.orderItems = source + .getOrderItems() + .stream() + .map(JsonAdaptedOrderItem::new) + .collect(Collectors.toList()); + } + + public String getName() { + return this.name; + } + + public List getOrderItems() { + return this.orderItems; + } + + /** + * Converts this Jackson-friendly adapted orderItem object into the model's {@code OrderItem} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted orderItem. + */ + public Preset toModelType() throws IllegalValueException { + + if (name == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "Name")); + } + + if (orderItems == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "Order Items")); + } + + List newOrderItems = orderItems.stream().map(x -> { + try { + return x.toModelType(); + } catch (IllegalValueException e) { + e.printStackTrace(); + } + return null; + }).collect(Collectors.toList()); + return new Preset(name, newOrderItems); + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedProfile.java b/src/main/java/seedu/address/storage/JsonAdaptedProfile.java new file mode 100644 index 00000000000..edf0d260d3d --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedProfile.java @@ -0,0 +1,66 @@ +package seedu.address.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.profile.Profile; +import seedu.address.model.vendor.Address; +import seedu.address.model.vendor.Phone; + +public class JsonAdaptedProfile { + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Profile's %s field is missing!"; + + private String address; + private String phone; + + /** + * Constructs a {@code JsonAdaptedProfile} with the given person details. + */ + @JsonCreator + public JsonAdaptedProfile(@JsonProperty("address") String address, + @JsonProperty("phone") String phone) { + this.address = address; + this.phone = phone; + } + + /** + * Converts a given {@code Profile} into this class for Jackson use. + */ + public JsonAdaptedProfile(Profile source) { + this.address = source.getAddress().toString(); + this.phone = source.getPhone().toString(); + } + + public String getAddress() { + return this.address; + } + + public String getPhone() { + return this.phone; + } + + /** + * Converts this Jackson-friendly adapted profile object into the model's {@code Profile} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted Profile. + */ + public Profile toModelType() throws IllegalValueException { + + if (phone == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "Phone")); + } + if (!Phone.isValidPhone(phone)) { + throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); + } + + if (address == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "Address")); + } + if (!Address.isValidAddress(address)) { + throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); + } + + return new Profile(new Phone(phone), new Address(address)); + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedVendor.java similarity index 59% rename from src/main/java/seedu/address/storage/JsonAdaptedPerson.java rename to src/main/java/seedu/address/storage/JsonAdaptedVendor.java index a6321cec2ea..c874d110c7d 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ b/src/main/java/seedu/address/storage/JsonAdaptedVendor.java @@ -10,33 +10,36 @@ import com.fasterxml.jackson.annotation.JsonProperty; import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; +import seedu.address.model.menu.Menu; import seedu.address.model.tag.Tag; +import seedu.address.model.vendor.Address; +import seedu.address.model.vendor.Email; +import seedu.address.model.vendor.Name; +import seedu.address.model.vendor.Phone; +import seedu.address.model.vendor.Vendor; /** - * Jackson-friendly version of {@link Person}. + * Jackson-friendly version of {@link Vendor}. */ -class JsonAdaptedPerson { +class JsonAdaptedVendor { - public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Vendor's %s field is missing!"; private final String name; private final String phone; private final String email; private final String address; private final List tagged = new ArrayList<>(); + private List menu = new ArrayList<>(); /** - * Constructs a {@code JsonAdaptedPerson} with the given person details. + * Constructs a {@code JsonAdaptedVendor} with the given vendor details. */ @JsonCreator - public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone, + public JsonAdaptedVendor(@JsonProperty("name") String name, @JsonProperty("phone") String phone, @JsonProperty("email") String email, @JsonProperty("address") String address, - @JsonProperty("tagged") List tagged) { + @JsonProperty("tagged") List tagged, + @JsonProperty("menu") List menuItemList) { this.name = name; this.phone = phone; this.email = email; @@ -44,12 +47,15 @@ public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone if (tagged != null) { this.tagged.addAll(tagged); } + if (menuItemList != null) { + this.menu.addAll(menuItemList); + } } /** - * Converts a given {@code Person} into this class for Jackson use. + * Converts a given {@code Vendor} into this class for Jackson use. */ - public JsonAdaptedPerson(Person source) { + public JsonAdaptedVendor(Vendor source) { name = source.getName().fullName; phone = source.getPhone().value; email = source.getEmail().value; @@ -57,17 +63,23 @@ public JsonAdaptedPerson(Person source) { tagged.addAll(source.getTags().stream() .map(JsonAdaptedTag::new) .collect(Collectors.toList())); + menu.addAll(source.getMenu().getMenuItems()); + //TODO add a value in menu } /** - * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. + * Converts this Jackson-friendly adapted vendor object into the model's {@code Vendor} object. * - * @throws IllegalValueException if there were any data constraints violated in the adapted person. + * @throws IllegalValueException if there were any data constraints violated in the adapted vendor. */ - public Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); + public Vendor toModelType() throws IllegalValueException { + final List vendorTags = new ArrayList<>(); for (JsonAdaptedTag tag : tagged) { - personTags.add(tag.toModelType()); + vendorTags.add(tag.toModelType()); + } + final Menu vendorMenu = new Menu(); + for (JsonAdaptedMenuItem menuItem : menu) { + vendorMenu.add(menuItem.toModelType()); } if (name == null) { @@ -100,10 +112,17 @@ public Person toModelType() throws IllegalValueException { if (!Address.isValidAddress(address)) { throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); } + + if (menu == null || menu.size() == 0) { + // todo change error message + throw new IllegalValueException("Menu must not be empty."); + } final Address modelAddress = new Address(address); - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); + final Set modelTags = new HashSet<>(vendorTags); + + //TODO: check the menu + return new Vendor(modelName, modelPhone, modelEmail, modelAddress, modelTags, vendorMenu); } } diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonMenuManagerStorage.java similarity index 56% rename from src/main/java/seedu/address/storage/JsonAddressBookStorage.java rename to src/main/java/seedu/address/storage/JsonMenuManagerStorage.java index dfab9daaa0d..8ab69fb63b8 100644 --- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java +++ b/src/main/java/seedu/address/storage/JsonMenuManagerStorage.java @@ -12,47 +12,48 @@ import seedu.address.commons.exceptions.IllegalValueException; import seedu.address.commons.util.FileUtil; import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.menu.ReadOnlyMenuManager; + /** - * A class to access AddressBook data stored as a json file on the hard disk. + * A class to access MenuManager data stored as a json file on the hard disk. */ -public class JsonAddressBookStorage implements AddressBookStorage { +public class JsonMenuManagerStorage implements MenuManagerStorage { - private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class); + private static final Logger logger = LogsCenter.getLogger(JsonMenuManagerStorage.class); private Path filePath; - public JsonAddressBookStorage(Path filePath) { + public JsonMenuManagerStorage(Path filePath) { this.filePath = filePath; } - public Path getAddressBookFilePath() { + public Path getMenuManagerFilePath() { return filePath; } @Override - public Optional readAddressBook() throws DataConversionException { - return readAddressBook(filePath); + public Optional readMenuManager() throws DataConversionException { + return readMenuManager(filePath); } /** - * Similar to {@link #readAddressBook()}. - * + * Similar to {@link #readMenuManager()}. + * @param filePath location of the data. Cannot be null. * @throws DataConversionException if the file is not in the correct format. */ - public Optional readAddressBook(Path filePath) throws DataConversionException { + public Optional readMenuManager(Path filePath) throws DataConversionException { requireNonNull(filePath); - Optional jsonAddressBook = JsonUtil.readJsonFile( - filePath, JsonSerializableAddressBook.class); - if (!jsonAddressBook.isPresent()) { + Optional jsonMenuManager = JsonUtil.readJsonFile( + filePath, JsonSerializableMenuManager.class); + if (!jsonMenuManager.isPresent()) { return Optional.empty(); } try { - return Optional.of(jsonAddressBook.get().toModelType()); + return Optional.of(jsonMenuManager.get().toModelType()); } catch (IllegalValueException ive) { logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); throw new DataConversionException(ive); @@ -60,21 +61,21 @@ public Optional readAddressBook(Path filePath) throws DataC } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, filePath); + public void saveMenuManager(ReadOnlyMenuManager menuManager) throws IOException { + saveMenuManager(menuManager, filePath); } /** - * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}. + * Similar to {@link #saveMenuManager(ReadOnlyMenuManager)}. * * @param filePath location of the data. Cannot be null. */ - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - requireNonNull(addressBook); + public void saveMenuManager(ReadOnlyMenuManager menuManager, Path filePath) throws IOException { + requireNonNull(menuManager); requireNonNull(filePath); FileUtil.createIfMissing(filePath); - JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath); + JsonUtil.saveJsonFile(new JsonSerializableMenuManager(menuManager), filePath); } } diff --git a/src/main/java/seedu/address/storage/JsonPresetManagerStorage.java b/src/main/java/seedu/address/storage/JsonPresetManagerStorage.java new file mode 100644 index 00000000000..cb46fc9a70a --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonPresetManagerStorage.java @@ -0,0 +1,73 @@ +package seedu.address.storage; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.commons.util.FileUtil; +import seedu.address.commons.util.JsonUtil; +import seedu.address.model.preset.Preset; + +public class JsonPresetManagerStorage implements PresetManagerStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonPresetManagerStorage.class); + + private Path filePath; + + public JsonPresetManagerStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getPresetManagerFilePath() { + return filePath; + } + + @Override + public Optional>> readPresetManager() throws DataConversionException { + return readPresetManager(filePath); + } + + /** + * Similar to {@link #readPresetManager()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional>> readPresetManager(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonPresetManager = JsonUtil.readJsonFile( + filePath, JsonSerializablePresetManager.class); + if (!jsonPresetManager.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonPresetManager.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void savePresetManager(List> allPresets, Path filePath) throws IOException { + requireNonNull(allPresets); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializablePresetManager(allPresets, true), filePath); + } + + @Override + public void savePresetManager(List> allPresets) throws IOException { + savePresetManager(allPresets, filePath); + } +} diff --git a/src/main/java/seedu/address/storage/JsonProfileManagerStorage.java b/src/main/java/seedu/address/storage/JsonProfileManagerStorage.java new file mode 100644 index 00000000000..cdc8398a7e0 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonProfileManagerStorage.java @@ -0,0 +1,68 @@ +package seedu.address.storage; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.commons.util.FileUtil; +import seedu.address.commons.util.JsonUtil; +import seedu.address.model.profile.Profile; + +public class JsonProfileManagerStorage implements ProfileManagerStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonProfileManagerStorage.class); + + private Path filePath; + + public JsonProfileManagerStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getProfileManagerFilePath() { + return filePath; + } + + @Override + public Optional readProfileManager() throws DataConversionException { + return readProfileManager(filePath); + } + + /** + * Similar to {@link #readProfileManager()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readProfileManager(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonProfileManager = JsonUtil.readJsonFile( + filePath, + JsonSerializableProfileManager.class + ); + if (!jsonProfileManager.isPresent()) { + return Optional.empty(); + } + + return Optional.of(jsonProfileManager.get().toModelType()); + } + + @Override + public void saveProfileManager(Profile profile) throws IOException { + saveProfileManager(profile, filePath); + } + + @Override + public void saveProfileManager(Profile profile, Path filePath) throws IOException { + requireNonNull(profile); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableProfileManager(profile), filePath); + } +} diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java deleted file mode 100644 index 5efd834091d..00000000000 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonRootName; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; - -/** - * An Immutable AddressBook that is serializable to JSON format. - */ -@JsonRootName(value = "addressbook") -class JsonSerializableAddressBook { - - public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; - - private final List persons = new ArrayList<>(); - - /** - * Constructs a {@code JsonSerializableAddressBook} with the given persons. - */ - @JsonCreator - public JsonSerializableAddressBook(@JsonProperty("persons") List persons) { - this.persons.addAll(persons); - } - - /** - * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use. - * - * @param source future changes to this will not affect the created {@code JsonSerializableAddressBook}. - */ - public JsonSerializableAddressBook(ReadOnlyAddressBook source) { - persons.addAll(source.getPersonList().stream().map(JsonAdaptedPerson::new).collect(Collectors.toList())); - } - - /** - * Converts this address book into the model's {@code AddressBook} object. - * - * @throws IllegalValueException if there were any data constraints violated. - */ - public AddressBook toModelType() throws IllegalValueException { - AddressBook addressBook = new AddressBook(); - for (JsonAdaptedPerson jsonAdaptedPerson : persons) { - Person person = jsonAdaptedPerson.toModelType(); - if (addressBook.hasPerson(person)) { - throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON); - } - addressBook.addPerson(person); - } - return addressBook; - } - -} diff --git a/src/main/java/seedu/address/storage/JsonSerializableMenuManager.java b/src/main/java/seedu/address/storage/JsonSerializableMenuManager.java new file mode 100644 index 00000000000..ee57c3af39f --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonSerializableMenuManager.java @@ -0,0 +1,61 @@ +package seedu.address.storage; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.food.MenuItem; +import seedu.address.model.menu.MenuManager; +import seedu.address.model.menu.ReadOnlyMenuManager; + + +/** + * An Immutable MenuManager that is serializable to JSON format. + */ +@JsonRootName(value = "menuManager") +class JsonSerializableMenuManager { + + public static final String MESSAGE_DUPLICATE_FOOD = "Menu contains duplicate foods."; + + private final List menuItems = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableMenuManager} with the given menu items. + */ + @JsonCreator + public JsonSerializableMenuManager(@JsonProperty("foods") List menuItems) { + this.menuItems.addAll(menuItems); + } + + /** + * Converts a given {@code ReadOnlyMenuManager} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableMenuManager}. + */ + public JsonSerializableMenuManager(ReadOnlyMenuManager source) { + menuItems.addAll(source.getMenuItemList().stream().map(JsonAdaptedMenuItem::new).collect(Collectors.toList())); + } + + /** + * Converts this address book into the model's {@code MenuManager} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public MenuManager toModelType() throws IllegalValueException { + MenuManager menuManager = new MenuManager(); + for (JsonAdaptedMenuItem jsonAdaptedMenuItem : menuItems) { + MenuItem item = jsonAdaptedMenuItem.toModelType(); + if (menuManager.hasMenuItem(item)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_FOOD); + } + menuManager.addMenuItem(item); + } + return menuManager; + } + +} diff --git a/src/main/java/seedu/address/storage/JsonSerializablePresetManager.java b/src/main/java/seedu/address/storage/JsonSerializablePresetManager.java new file mode 100644 index 00000000000..b958d1c1db6 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonSerializablePresetManager.java @@ -0,0 +1,87 @@ +package seedu.address.storage; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.order.OrderItem; +import seedu.address.model.preset.Preset; + +/** + * An Immutable OrderManager that is serializable to JSON format. + */ +public class JsonSerializablePresetManager { + + private final List> presets = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableOrderManager} with the given orderItems. + */ + @JsonCreator + public JsonSerializablePresetManager(@JsonProperty("presets") List> presetOrderItems) { + this.presets.addAll(presetOrderItems); + + } + + /** + * Converts a given {@code ReadOnlyOrderManager} into this class for Jackson use. + * + * @param allPresets future changes to this will not affect the created {@code JsonSerializableOrderManager}. + */ + public JsonSerializablePresetManager(List> allPresets, boolean bool) { + // Added extra parameter to this constructor to avoid same signature. + if (!bool) { + + } + + List> finalList = new ArrayList<>(); + + for (List allPreset : allPresets) { + List vendorList = allPreset + .stream() + .map(JsonAdaptedPreset::new) + .collect(Collectors.toList()); + finalList.add(vendorList); + } + this.presets.addAll(finalList); + } + + /** + * Converts this address book into the model's {@code OrderManager} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public List> toModelType() throws IllegalValueException { + List> vendorPresetList = new ArrayList<>(); + for (List jsonAdaptedPresetList: presets) { + List presetsList = new ArrayList<>(); + HashSet presetNames = new HashSet<>(); + for (JsonAdaptedPreset jsonAdaptedPreset: jsonAdaptedPresetList) { + String presetName = jsonAdaptedPreset.getName(); + if (presetNames.contains(presetName)) { + throw new IllegalValueException("Duplicate Preset Name for the same vendor."); + } + + List jsonAdaptedOrderItems = jsonAdaptedPreset.getOrderItems(); + + Preset preset = new Preset(presetName, new ArrayList<>()); + presetNames.add(presetName); + + for (JsonAdaptedOrderItem jsonAdaptedOrderItem : jsonAdaptedOrderItems) { + OrderItem orderItem = jsonAdaptedOrderItem.toModelType(); + preset.addOrderItem(orderItem); + } + + presetsList.add(preset); + } + vendorPresetList.add(presetsList); + } + return vendorPresetList; + } +} + diff --git a/src/main/java/seedu/address/storage/JsonSerializableProfileManager.java b/src/main/java/seedu/address/storage/JsonSerializableProfileManager.java new file mode 100644 index 00000000000..772f28db218 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonSerializableProfileManager.java @@ -0,0 +1,42 @@ +package seedu.address.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.profile.Profile; +import seedu.address.model.vendor.Address; +import seedu.address.model.vendor.Phone; + +public class JsonSerializableProfileManager { + private final JsonAdaptedProfile jsonAdaptedProfile; + + /** + * Constructs a {@code JsonSerializableProfileManager} with the given menu items. + */ + @JsonCreator + public JsonSerializableProfileManager( + @JsonProperty("jsonAdaptedProfile") JsonAdaptedProfile jsonAdaptedProfile + ) { + this.jsonAdaptedProfile = new JsonAdaptedProfile( + jsonAdaptedProfile.getAddress(), + jsonAdaptedProfile.getPhone() + ); + } + + /** + * Converts a given {@code Profile} into this class for Jackson use. + */ + public JsonSerializableProfileManager(Profile profile) { + this.jsonAdaptedProfile = new JsonAdaptedProfile(profile); + } + + /** + * Converts this address book into the model's {@code Profile} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public Profile toModelType() { + return new Profile(new Phone(jsonAdaptedProfile.getPhone()), new Address(jsonAdaptedProfile.getAddress())); + } +} diff --git a/src/main/java/seedu/address/storage/JsonSerializableVendorManager.java b/src/main/java/seedu/address/storage/JsonSerializableVendorManager.java new file mode 100644 index 00000000000..7017ef71476 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonSerializableVendorManager.java @@ -0,0 +1,61 @@ +package seedu.address.storage; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.vendor.ReadOnlyVendorManager; +import seedu.address.model.vendor.Vendor; +import seedu.address.model.vendor.VendorManager; + + +/** + * An Immutable VendorManager that is serializable to JSON format. + */ +@JsonRootName(value = "vendorManager") +class JsonSerializableVendorManager { + + public static final String MESSAGE_DUPLICATE_VENDOR = "Vendors list contains duplicate vendor(s)."; + + private final List vendors = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableVendorManager} with the given vendors. + */ + @JsonCreator + public JsonSerializableVendorManager(@JsonProperty("vendors") List vendors) { + this.vendors.addAll(vendors); + } + + /** + * Converts a given {@code ReadOnlyVendorManager} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableVendorManager}. + */ + public JsonSerializableVendorManager(ReadOnlyVendorManager source) { + vendors.addAll(source.getVendorList().stream().map(JsonAdaptedVendor::new).collect(Collectors.toList())); + } + + /** + * Converts this address book into the model's {@code VendorManager} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public VendorManager toModelType() throws IllegalValueException { + VendorManager vendorManager = new VendorManager(); + for (JsonAdaptedVendor jsonAdaptedVendor : vendors) { + Vendor vendor = jsonAdaptedVendor.toModelType(); + if (vendorManager.hasVendor(vendor)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_VENDOR); + } + vendorManager.addVendor(vendor); + } + return vendorManager; + } + +} diff --git a/src/main/java/seedu/address/storage/JsonVendorManagerStorage.java b/src/main/java/seedu/address/storage/JsonVendorManagerStorage.java new file mode 100644 index 00000000000..bc111cc5390 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonVendorManagerStorage.java @@ -0,0 +1,80 @@ +package seedu.address.storage; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.commons.util.FileUtil; +import seedu.address.commons.util.JsonUtil; +import seedu.address.model.vendor.ReadOnlyVendorManager; + +/** + * A class to access VendorManager data stored as a json file on the hard disk. + */ +public class JsonVendorManagerStorage implements VendorManagerStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonVendorManagerStorage.class); + + private Path filePath; + + public JsonVendorManagerStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getVendorManagerFilePath() { + return filePath; + } + + @Override + public Optional readVendorManager() throws DataConversionException { + return readVendorManager(filePath); + } + + /** + * Similar to {@link #readVendorManager()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readVendorManager(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonVendorManager = JsonUtil.readJsonFile( + filePath, JsonSerializableVendorManager.class); + if (!jsonVendorManager.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonVendorManager.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveVendorManager(ReadOnlyVendorManager vendorManager) throws IOException { + saveVendorManager(vendorManager, filePath); + } + + /** + * Similar to {@link #saveVendorManager(ReadOnlyVendorManager)}. + * + * @param filePath location of the data. Cannot be null. + */ + public void saveVendorManager(ReadOnlyVendorManager vendorManager, Path filePath) throws IOException { + requireNonNull(vendorManager); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableVendorManager(vendorManager), filePath); + } + +} diff --git a/src/main/java/seedu/address/storage/MenuItemStorage.java b/src/main/java/seedu/address/storage/MenuItemStorage.java new file mode 100644 index 00000000000..d3c3673c1b4 --- /dev/null +++ b/src/main/java/seedu/address/storage/MenuItemStorage.java @@ -0,0 +1,30 @@ +package seedu.address.storage; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import javafx.collections.ObservableList; +import seedu.address.model.menu.MenuManager; +import seedu.address.model.menu.ReadOnlyMenuManager; +import seedu.address.model.vendor.Vendor; + +/** + * Stub to load in a sample menu to be used in MainApp class. + */ +public class MenuItemStorage { + /** + * Generates a List of ReadOnlyMenuManager wrapped in Optional class + */ + public List> readMenuManagers(ObservableList vendorObservableList) { + + List> menuManagers = new ArrayList<>(); + + for (Vendor vendor : vendorObservableList) { + MenuManager menuManager1 = new MenuManager(vendor.getMenu()); + menuManagers.add(Optional.of(menuManager1)); + } + + return menuManagers; + } +} diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/MenuManagerStorage.java similarity index 51% rename from src/main/java/seedu/address/storage/AddressBookStorage.java rename to src/main/java/seedu/address/storage/MenuManagerStorage.java index 4599182b3f9..216d1227671 100644 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ b/src/main/java/seedu/address/storage/MenuManagerStorage.java @@ -5,41 +5,41 @@ import java.util.Optional; import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.menu.ReadOnlyMenuManager; /** - * Represents a storage for {@link seedu.address.model.AddressBook}. + * Represents a storage for {@link seedu.address.model.menu.MenuManager}. */ -public interface AddressBookStorage { +public interface MenuManagerStorage { /** * Returns the file path of the data file. */ - Path getAddressBookFilePath(); + Path getMenuManagerFilePath(); /** - * Returns AddressBook data as a {@link ReadOnlyAddressBook}. + * Returns MenuManager data as a {@link ReadOnlyMenuManager}. * Returns {@code Optional.empty()} if storage file is not found. * @throws DataConversionException if the data in storage is not in the expected format. * @throws IOException if there was any problem when reading from the storage. */ - Optional readAddressBook() throws DataConversionException, IOException; + Optional readMenuManager() throws DataConversionException, IOException; /** - * @see #getAddressBookFilePath() + * @see #getMenuManagerFilePath() */ - Optional readAddressBook(Path filePath) throws DataConversionException, IOException; + Optional readMenuManager(Path filePath) throws DataConversionException, IOException; /** - * Saves the given {@link ReadOnlyAddressBook} to the storage. - * @param addressBook cannot be null. + * Saves the given {@link ReadOnlyMenuManager} to the storage. + * @param menuManager cannot be null. * @throws IOException if there was any problem writing to the file. */ - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; + void saveMenuManager(ReadOnlyMenuManager menuManager) throws IOException; /** - * @see #saveAddressBook(ReadOnlyAddressBook) + * @see #saveMenuManager(ReadOnlyMenuManager) */ - void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException; + void saveMenuManager(ReadOnlyMenuManager menuManager, Path filePath) throws IOException; } diff --git a/src/main/java/seedu/address/storage/PresetManagerStorage.java b/src/main/java/seedu/address/storage/PresetManagerStorage.java new file mode 100644 index 00000000000..fc81f9445c2 --- /dev/null +++ b/src/main/java/seedu/address/storage/PresetManagerStorage.java @@ -0,0 +1,32 @@ +package seedu.address.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.Optional; + +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.model.preset.Preset; + +public interface PresetManagerStorage { + /** + * Returns the file path of the data file. + */ + Path getPresetManagerFilePath(); + + /** + * Returns {@code Optional.empty()} if storage file is not found. + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional>> readPresetManager() throws DataConversionException, IOException; + + /** + * @see #getPresetManagerFilePath() + */ + Optional>> readPresetManager(Path filePath) throws DataConversionException, IOException; + + void savePresetManager(List> allPresets) throws IOException; + + void savePresetManager(List> allPresets, Path filePath) throws IOException; +} diff --git a/src/main/java/seedu/address/storage/ProfileManagerStorage.java b/src/main/java/seedu/address/storage/ProfileManagerStorage.java new file mode 100644 index 00000000000..20903d0c970 --- /dev/null +++ b/src/main/java/seedu/address/storage/ProfileManagerStorage.java @@ -0,0 +1,32 @@ +package seedu.address.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.model.profile.Profile; + +public interface ProfileManagerStorage { + /** + * Returns the file path of the data file. + */ + Path getProfileManagerFilePath(); + + /** + * Returns {@code Optional.empty()} if storage file is not found. + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional readProfileManager() throws DataConversionException; + + /** + * @see #getProfileManagerFilePath() + */ + Optional readProfileManager(Path filePath) throws DataConversionException; + + void saveProfileManager(Profile profile) throws IOException; + + void saveProfileManager(Profile profile, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java index beda8bd9f11..5453ae84fc8 100644 --- a/src/main/java/seedu/address/storage/Storage.java +++ b/src/main/java/seedu/address/storage/Storage.java @@ -2,17 +2,20 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.List; import java.util.Optional; import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.ReadOnlyUserPrefs; import seedu.address.model.UserPrefs; +import seedu.address.model.preset.Preset; +import seedu.address.model.profile.Profile; +import seedu.address.model.vendor.ReadOnlyVendorManager; /** * API of the Storage component */ -public interface Storage extends AddressBookStorage, UserPrefsStorage { +public interface Storage extends VendorManagerStorage, UserPrefsStorage, PresetManagerStorage, ProfileManagerStorage { @Override Optional readUserPrefs() throws DataConversionException, IOException; @@ -21,12 +24,30 @@ public interface Storage extends AddressBookStorage, UserPrefsStorage { void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; @Override - Path getAddressBookFilePath(); + Path getVendorManagerFilePath(); @Override - Optional readAddressBook() throws DataConversionException, IOException; + Optional readVendorManager() throws DataConversionException, IOException; @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; + void saveVendorManager(ReadOnlyVendorManager vendorManager) throws IOException; + + @Override + Path getPresetManagerFilePath(); + + @Override + Optional>> readPresetManager() throws DataConversionException, IOException; + + @Override + void savePresetManager(List> allPresets, Path filePath) throws IOException; + + @Override + Path getProfileManagerFilePath(); + + @Override + Optional readProfileManager() throws DataConversionException; + + @Override + void saveProfileManager(Profile profile, Path filepath) throws IOException; } diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java index 79868290974..e1d446ab744 100644 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ b/src/main/java/seedu/address/storage/StorageManager.java @@ -2,33 +2,45 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.List; import java.util.Optional; import java.util.logging.Logger; import seedu.address.commons.core.LogsCenter; import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.ReadOnlyUserPrefs; import seedu.address.model.UserPrefs; +import seedu.address.model.preset.Preset; +import seedu.address.model.profile.Profile; +import seedu.address.model.vendor.ReadOnlyVendorManager; /** - * Manages storage of AddressBook data in local storage. + * Manages storage of VendorManager data in local storage. */ public class StorageManager implements Storage { private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; + private VendorManagerStorage vendorManagerStorage; private UserPrefsStorage userPrefsStorage; + private PresetManagerStorage presetManagerStorage; + private ProfileManagerStorage profileManagerStorage; /** - * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}. + * Creates a {@code StorageManager} with the given {@code VendorManagerStorage} and {@code UserPrefStorage} + * and {@Code OrderManagerStorage}. */ - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { + public StorageManager(VendorManagerStorage vendorManagerStorage, UserPrefsStorage userPrefsStorage, + PresetManagerStorage presetManagerStorage, ProfileManagerStorage profileManagerStorage) { super(); - this.addressBookStorage = addressBookStorage; + this.vendorManagerStorage = vendorManagerStorage; this.userPrefsStorage = userPrefsStorage; + this.presetManagerStorage = presetManagerStorage; + this.profileManagerStorage = profileManagerStorage; } + public StorageManager() { + } // useless + // ================ UserPrefs methods ============================== @Override @@ -47,33 +59,92 @@ public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { } - // ================ AddressBook methods ============================== + // ================ VendorManager methods ============================== @Override - public Path getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); + public Path getVendorManagerFilePath() { + return vendorManagerStorage.getVendorManagerFilePath(); } @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); + public Optional readVendorManager() throws DataConversionException, IOException { + return readVendorManager(vendorManagerStorage.getVendorManagerFilePath()); } @Override - public Optional readAddressBook(Path filePath) throws DataConversionException, IOException { + public Optional readVendorManager(Path filePath) throws DataConversionException, + IOException { logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); + return vendorManagerStorage.readVendorManager(filePath); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); + public void saveVendorManager(ReadOnlyVendorManager vendorManager) throws IOException { + saveVendorManager(vendorManager, vendorManagerStorage.getVendorManagerFilePath()); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { + public void saveVendorManager(ReadOnlyVendorManager vendorManager, Path filePath) throws IOException { logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); + vendorManagerStorage.saveVendorManager(vendorManager, filePath); + } + + + // ================ PresetManager methods ============================== + + @Override + public Path getPresetManagerFilePath() { + return presetManagerStorage.getPresetManagerFilePath(); } + @Override + public Optional>> readPresetManager() throws DataConversionException, IOException { + return readPresetManager(presetManagerStorage.getPresetManagerFilePath()); + } + + @Override + public Optional>> readPresetManager(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return presetManagerStorage.readPresetManager(filePath); + } + + @Override + public void savePresetManager(List> allPresets) throws IOException { + savePresetManager(allPresets, presetManagerStorage.getPresetManagerFilePath()); + } + + @Override + public void savePresetManager(List> allPresets, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + presetManagerStorage.savePresetManager(allPresets, filePath); + } + + // ================ ProfileManager methods ============================== + + @Override + public Path getProfileManagerFilePath() { + return presetManagerStorage.getPresetManagerFilePath(); + } + + @Override + public Optional readProfileManager() throws DataConversionException { + return readProfileManager(profileManagerStorage.getProfileManagerFilePath()); + } + + @Override + public Optional readProfileManager(Path filePath) throws DataConversionException { + logger.fine("Attempting to read data from file: " + filePath); + return profileManagerStorage.readProfileManager(filePath); + } + + @Override + public void saveProfileManager(Profile profile) throws IOException { + saveProfileManager(profile, profileManagerStorage.getProfileManagerFilePath()); + } + + @Override + public void saveProfileManager(Profile profile, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + profileManagerStorage.saveProfileManager(profile, filePath); + } } diff --git a/src/main/java/seedu/address/storage/VendorManagerStorage.java b/src/main/java/seedu/address/storage/VendorManagerStorage.java new file mode 100644 index 00000000000..f29ba58426e --- /dev/null +++ b/src/main/java/seedu/address/storage/VendorManagerStorage.java @@ -0,0 +1,46 @@ +package seedu.address.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.model.vendor.ReadOnlyVendorManager; +import seedu.address.model.vendor.VendorManager; + +/** + * Represents a storage for {@link VendorManager}. + */ +public interface VendorManagerStorage { + + /** + * Returns the file path of the data file. + */ + Path getVendorManagerFilePath(); + + /** + * Returns VendorManager data as a {@link ReadOnlyVendorManager}. + * Returns {@code Optional.empty()} if storage file is not found. + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional readVendorManager() throws DataConversionException, IOException; + + /** + * @see #getVendorManagerFilePath() + */ + Optional readVendorManager(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyVendorManager} to the storage. + * @param vendorManager cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveVendorManager(ReadOnlyVendorManager vendorManager) throws IOException; + + /** + * @see #saveVendorManager(ReadOnlyVendorManager) + */ + void saveVendorManager(ReadOnlyVendorManager vendorManager, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/address/ui/FoodCard.java b/src/main/java/seedu/address/ui/FoodCard.java new file mode 100644 index 00000000000..6801ea99581 --- /dev/null +++ b/src/main/java/seedu/address/ui/FoodCard.java @@ -0,0 +1,89 @@ +package seedu.address.ui; + +import java.util.Comparator; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.model.food.MenuItem; + + +/** + * An UI component that displays information of a {@code Food}. + */ +public class FoodCard extends UiPart { + + private static final String FXML = "FoodListCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on VendorManager level 4 + */ + + public final MenuItem item; + + @FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label price; + @FXML + private FlowPane tags; + + @FXML + private ImageView imageView; + + /** + * Creates a {@code FoodCode} with the given {@code Food} and index to display. + */ + public FoodCard(MenuItem item, int displayedIndex) { + super(FXML); + this.item = item; + id.setText(displayedIndex + ". "); + name.setText(item.getName()); + price.setText(item.getPriceString()); + item.getTags().stream() + .sorted(Comparator.comparing(tag -> tag.tagName)) + .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + + try { + Image image = new Image(this.getClass().getResourceAsStream("/images/food/" + item.getFilePath())); + imageView.setImage(image); + } catch (NullPointerException e) { + Image defaultImage = new Image(this.getClass() + .getResourceAsStream("/images/food/default-menu-item.jpg")); + imageView.setImage(defaultImage); + } + + imageView.setFitHeight(120); + imageView.setFitWidth(120); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof FoodCard)) { + return false; + } + + // state check + FoodCard card = (FoodCard) other; + return id.getText().equals(card.id.getText()) + && item.equals(card.item); + } +} diff --git a/src/main/java/seedu/address/ui/FoodListPanel.java b/src/main/java/seedu/address/ui/FoodListPanel.java new file mode 100644 index 00000000000..d01a39e82bb --- /dev/null +++ b/src/main/java/seedu/address/ui/FoodListPanel.java @@ -0,0 +1,49 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.food.MenuItem; + +/** + * Panel containing the list of foods. + */ +public class FoodListPanel extends UiPart { + private static final String FXML = "FoodListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(FoodListPanel.class); + + @FXML + private ListView foodListView; + + /** + * Creates a {@code FoodListPanel} with the given {@code ObservableList}. + */ + public FoodListPanel(ObservableList menuItemList) { + super(FXML); + foodListView.setItems(menuItemList); + foodListView.setCellFactory(listView -> new MenuItemListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Food} using a {@code FoodCard}. + */ + class MenuItemListViewCell extends ListCell { + @Override + protected void updateItem(MenuItem item, boolean empty) { + super.updateItem(item, empty); + + if (empty || item == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new FoodCard(item, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java index 9a665915949..0a21efa61da 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/address/ui/HelpWindow.java @@ -15,7 +15,7 @@ */ public class HelpWindow extends UiPart { - public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html"; + public static final String USERGUIDE_URL = "https://ay2021s1-cs2103-t16-1.github.io/tp/UserGuide.html"; public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 9106c3aa6e5..64d28936ba3 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -9,6 +9,7 @@ import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; import javafx.stage.Stage; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; @@ -31,7 +32,9 @@ public class MainWindow extends UiPart { private Logic logic; // Independent Ui parts residing in this Ui container - private PersonListPanel personListPanel; + private VendorListPanel vendorListPanel; + private FoodListPanel foodListPanel; + private OrderItemListPanel orderItemListPanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; @@ -42,13 +45,22 @@ public class MainWindow extends UiPart { private MenuItem helpMenuItem; @FXML - private StackPane personListPanelPlaceholder; + private StackPane vendorListPanelPlaceholder; + + @FXML + private StackPane foodListPanelPlaceholder; + + @FXML + private StackPane orderItemListPanelPlaceholder; @FXML private StackPane resultDisplayPlaceholder; @FXML - private StackPane statusbarPlaceholder; + private VBox vendorBox; + + @FXML + private VBox menuBox; /** * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}. @@ -78,6 +90,7 @@ private void setAccelerators() { /** * Sets the accelerator of a MenuItem. + * * @param keyCombination the KeyCombination value of the accelerator */ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { @@ -110,19 +123,60 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { * Fills up all the placeholders of this window. */ void fillInnerParts() { - personListPanel = new PersonListPanel(logic.getFilteredPersonList()); - personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + foodListPanel = new FoodListPanel(logic.getFilteredMenuItemList()); + foodListPanelPlaceholder.getChildren().add(foodListPanel.getRoot()); + setFoodListDisplay(false); + + vendorListPanel = new VendorListPanel(logic.getObservableVendorList()); + vendorListPanelPlaceholder.getChildren().add(vendorListPanel.getRoot()); + + orderItemListPanel = new OrderItemListPanel(logic.getFilteredOrderItemList()); + orderItemListPanelPlaceholder.getChildren().add(orderItemListPanel.getRoot()); + resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); - StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); - statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); - CommandBox commandBox = new CommandBox(this::executeCommand); commandBoxPlaceholder.getChildren().add(commandBox.getRoot()); } + /** + * Sets visibility of vendor list display based on the boolean provided. + */ + void setVendorListDisplay(boolean bool) { + vendorBox.visibleProperty().setValue(bool); + vendorBox.managedProperty().setValue(bool); + } + + /** + * Sets visibility of food list display based on the boolean provided. + */ + void setFoodListDisplay(boolean bool) { + menuBox.visibleProperty().setValue(bool); + menuBox.managedProperty().setValue(bool); + + } + + void setOrderItemListDisplay(boolean bool) { + orderItemListPanel.getRoot().setVisible(bool); + orderItemListPanel.getRoot().setManaged(bool); + } + + /** + * Displays menu if vendor has been selected, otherwise display vendor list. + */ + void updateMode() { + boolean bool = logic.isSelected(); + + foodListPanel = new FoodListPanel(logic.getFilteredMenuItemList()); + foodListPanelPlaceholder.getChildren().add(foodListPanel.getRoot()); + + setVendorListDisplay(!bool); + setFoodListDisplay(bool); + setOrderItemListDisplay(true); + } + /** * Sets the default size based on {@code guiSettings}. */ @@ -163,8 +217,8 @@ private void handleExit() { primaryStage.hide(); } - public PersonListPanel getPersonListPanel() { - return personListPanel; + public VendorListPanel getVendorListPanel() { + return vendorListPanel; } /** @@ -186,6 +240,10 @@ private CommandResult executeCommand(String commandText) throws CommandException handleExit(); } + if (commandResult.isVendor()) { + updateMode(); + } + return commandResult; } catch (CommandException | ParseException e) { logger.info("Invalid command: " + commandText); @@ -193,4 +251,6 @@ private CommandResult executeCommand(String commandText) throws CommandException throw e; } } + + } diff --git a/src/main/java/seedu/address/ui/OrderItemCard.java b/src/main/java/seedu/address/ui/OrderItemCard.java new file mode 100644 index 00000000000..15aa8849427 --- /dev/null +++ b/src/main/java/seedu/address/ui/OrderItemCard.java @@ -0,0 +1,79 @@ +package seedu.address.ui; + +import java.util.Comparator; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.model.order.OrderItem; + +/** + * An UI component that displays information of a {@code Food}. + */ +public class OrderItemCard extends UiPart { + + private static final String FXML = "OrderItemListCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on VendorManager level 4 + */ + + public final OrderItem orderItem; + + @FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label price; + @FXML + private Label quantity; + @FXML + private FlowPane tags; + + /** + * Creates a {@code FoodCode} with the given {@code Food} and index to display. + */ + public OrderItemCard(OrderItem orderItem, int displayedIndex) { + super(FXML); + this.orderItem = orderItem; + id.setText(displayedIndex + ". "); + name.setText(orderItem.getName()); + price.setText(orderItem.getPriceString()); + quantity.setText("x " + Integer.toString(orderItem.getQuantity())); + orderItem.getTags().stream() + .sorted(Comparator.comparing(tag -> tag.tagName)) + .forEach(tag -> { + Label label = new Label(tag.tagName); + label.setEllipsisString("..."); + label.setMaxWidth(150); + tags.getChildren().add(label); + }); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof OrderItemCard)) { + return false; + } + + // state check + OrderItemCard card = (OrderItemCard) other; + return id.getText().equals(card.id.getText()) + && orderItem.equals(card.orderItem); + } +} diff --git a/src/main/java/seedu/address/ui/OrderItemListPanel.java b/src/main/java/seedu/address/ui/OrderItemListPanel.java new file mode 100644 index 00000000000..2370d64c2c9 --- /dev/null +++ b/src/main/java/seedu/address/ui/OrderItemListPanel.java @@ -0,0 +1,49 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.order.OrderItem; + +/** + * Panel containing the list of OrderItem. + */ +public class OrderItemListPanel extends UiPart { + private static final String FXML = "OrderItemListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(OrderItemListPanel.class); + + @FXML + private ListView orderItemListView; + + /** + * Creates a {@code OrderListPanel} with the given {@code ObservableList}. + */ + public OrderItemListPanel(ObservableList orderList) { + super(FXML); + orderItemListView.setItems(orderList); + orderItemListView.setCellFactory(listView -> new OrderItemListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code OrderItem} using a {@code OrderCard}. + */ + class OrderItemListViewCell extends ListCell { + @Override + protected void updateItem(OrderItem orderItem, boolean empty) { + super.updateItem(orderItem, empty); + + if (empty || orderItem == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new OrderItemCard(orderItem, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java deleted file mode 100644 index f4c501a897b..00000000000 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ /dev/null @@ -1,49 +0,0 @@ -package seedu.address.ui; - -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.layout.Region; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; - -/** - * Panel containing the list of persons. - */ -public class PersonListPanel extends UiPart { - private static final String FXML = "PersonListPanel.fxml"; - private final Logger logger = LogsCenter.getLogger(PersonListPanel.class); - - @FXML - private ListView personListView; - - /** - * Creates a {@code PersonListPanel} with the given {@code ObservableList}. - */ - public PersonListPanel(ObservableList personList) { - super(FXML); - personListView.setItems(personList); - personListView.setCellFactory(listView -> new PersonListViewCell()); - } - - /** - * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}. - */ - class PersonListViewCell extends ListCell { - @Override - protected void updateItem(Person person, boolean empty) { - super.updateItem(person, empty); - - if (empty || person == null) { - setGraphic(null); - setText(null); - } else { - setGraphic(new PersonCard(person, getIndex() + 1).getRoot()); - } - } - } - -} diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/address/ui/UiManager.java index 882027e4537..70211a2e700 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/address/ui/UiManager.java @@ -20,7 +20,7 @@ public class UiManager implements Ui { public static final String ALERT_DIALOG_PANE_FIELD_ID = "alertDialogPane"; private static final Logger logger = LogsCenter.getLogger(UiManager.class); - private static final String ICON_APPLICATION = "/images/address_book_32.png"; + private static final String ICON_APPLICATION = "/images/food/default-menu-item.jpg"; private Logic logic; private MainWindow mainWindow; diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/VendorCard.java similarity index 62% rename from src/main/java/seedu/address/ui/PersonCard.java rename to src/main/java/seedu/address/ui/VendorCard.java index 7fc927bc5d9..d8efc67ed91 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/seedu/address/ui/VendorCard.java @@ -7,24 +7,24 @@ import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; -import seedu.address.model.person.Person; +import seedu.address.model.vendor.Vendor; /** - * An UI component that displays information of a {@code Person}. + * An UI component that displays information of a {@code Vendor}. */ -public class PersonCard extends UiPart { +public class VendorCard extends UiPart { - private static final String FXML = "PersonListCard.fxml"; + private static final String FXML = "VendorListCard.fxml"; /** * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. * As a consequence, UI elements' variable names cannot be set to such keywords * or an exception will be thrown by JavaFX during runtime. * - * @see The issue on AddressBook level 4 + * @see The issue on VendorManager level 4 */ - public final Person person; + public final Vendor vendor; @FXML private HBox cardPane; @@ -42,17 +42,17 @@ public class PersonCard extends UiPart { private FlowPane tags; /** - * Creates a {@code PersonCode} with the given {@code Person} and index to display. + * Creates a {@code PersonCode} with the given {@code Vendor} and index to display. */ - public PersonCard(Person person, int displayedIndex) { + public VendorCard(Vendor vendor, int displayedIndex) { super(FXML); - this.person = person; + this.vendor = vendor; id.setText(displayedIndex + ". "); - name.setText(person.getName().fullName); - phone.setText(person.getPhone().value); - address.setText(person.getAddress().value); - email.setText(person.getEmail().value); - person.getTags().stream() + name.setText(vendor.getName().fullName); + phone.setText(vendor.getPhone().value); + address.setText(vendor.getAddress().value); + email.setText(vendor.getEmail().value); + vendor.getTags().stream() .sorted(Comparator.comparing(tag -> tag.tagName)) .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); } @@ -65,13 +65,13 @@ public boolean equals(Object other) { } // instanceof handles nulls - if (!(other instanceof PersonCard)) { + if (!(other instanceof VendorCard)) { return false; } // state check - PersonCard card = (PersonCard) other; + VendorCard card = (VendorCard) other; return id.getText().equals(card.id.getText()) - && person.equals(card.person); + && vendor.equals(card.vendor); } } diff --git a/src/main/java/seedu/address/ui/VendorListPanel.java b/src/main/java/seedu/address/ui/VendorListPanel.java new file mode 100644 index 00000000000..72446e4972d --- /dev/null +++ b/src/main/java/seedu/address/ui/VendorListPanel.java @@ -0,0 +1,49 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.vendor.Vendor; + +/** + * Panel containing the list of vendors. + */ +public class VendorListPanel extends UiPart { + private static final String FXML = "VendorListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(VendorListPanel.class); + + @FXML + private ListView vendorListView; + + /** + * Creates a {@code VendorListPanel} with the given {@code ObservableList}. + */ + public VendorListPanel(ObservableList vendorList) { + super(FXML); + vendorListView.setItems(vendorList); + vendorListView.setCellFactory(listView -> new VendorListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Vendor} using a {@code VendorCard}. + */ + class VendorListViewCell extends ListCell { + @Override + protected void updateItem(Vendor vendor, boolean empty) { + super.updateItem(vendor, empty); + + if (empty || vendor == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new VendorCard(vendor, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/resources/images/food/al_amaan/butter_chicken.png b/src/main/resources/images/food/al_amaan/butter_chicken.png new file mode 100644 index 00000000000..1b829853aee Binary files /dev/null and b/src/main/resources/images/food/al_amaan/butter_chicken.png differ diff --git a/src/main/resources/images/food/al_amaan/cheese_fries.png b/src/main/resources/images/food/al_amaan/cheese_fries.png new file mode 100644 index 00000000000..471682439fa Binary files /dev/null and b/src/main/resources/images/food/al_amaan/cheese_fries.png differ diff --git a/src/main/resources/images/food/al_amaan/kampong.png b/src/main/resources/images/food/al_amaan/kampong.png new file mode 100644 index 00000000000..12e4abd3913 Binary files /dev/null and b/src/main/resources/images/food/al_amaan/kampong.png differ diff --git a/src/main/resources/images/food/al_amaan/milo.png b/src/main/resources/images/food/al_amaan/milo.png new file mode 100644 index 00000000000..8314d7cc9b7 Binary files /dev/null and b/src/main/resources/images/food/al_amaan/milo.png differ diff --git a/src/main/resources/images/food/al_amaan/milo_dino.png b/src/main/resources/images/food/al_amaan/milo_dino.png new file mode 100644 index 00000000000..8cc9c70d5ff Binary files /dev/null and b/src/main/resources/images/food/al_amaan/milo_dino.png differ diff --git a/src/main/resources/images/food/al_amaan/milo_god.png b/src/main/resources/images/food/al_amaan/milo_god.png new file mode 100644 index 00000000000..a11a29dca54 Binary files /dev/null and b/src/main/resources/images/food/al_amaan/milo_god.png differ diff --git a/src/main/resources/images/food/al_amaan/pattaya.png b/src/main/resources/images/food/al_amaan/pattaya.png new file mode 100644 index 00000000000..3c938be5637 Binary files /dev/null and b/src/main/resources/images/food/al_amaan/pattaya.png differ diff --git a/src/main/resources/images/food/al_amaan/roti_john.png b/src/main/resources/images/food/al_amaan/roti_john.png new file mode 100644 index 00000000000..4622929e654 Binary files /dev/null and b/src/main/resources/images/food/al_amaan/roti_john.png differ diff --git a/src/main/resources/images/food/al_amaan/sambal_chicken.png b/src/main/resources/images/food/al_amaan/sambal_chicken.png new file mode 100644 index 00000000000..a6acf152362 Binary files /dev/null and b/src/main/resources/images/food/al_amaan/sambal_chicken.png differ diff --git a/src/main/resources/images/food/al_amaan/teh_ping.png b/src/main/resources/images/food/al_amaan/teh_ping.png new file mode 100644 index 00000000000..9a91f7d81c3 Binary files /dev/null and b/src/main/resources/images/food/al_amaan/teh_ping.png differ diff --git a/src/main/resources/images/food/al_amaan/veg_briyani.png b/src/main/resources/images/food/al_amaan/veg_briyani.png new file mode 100644 index 00000000000..857bda4f367 Binary files /dev/null and b/src/main/resources/images/food/al_amaan/veg_briyani.png differ diff --git a/src/main/resources/images/food/ayam_tali_wang/ayam_taliwang_dada.png b/src/main/resources/images/food/ayam_tali_wang/ayam_taliwang_dada.png new file mode 100644 index 00000000000..543807ee356 Binary files /dev/null and b/src/main/resources/images/food/ayam_tali_wang/ayam_taliwang_dada.png differ diff --git a/src/main/resources/images/food/ayam_tali_wang/ayam_taliwang_peha.png b/src/main/resources/images/food/ayam_tali_wang/ayam_taliwang_peha.png new file mode 100644 index 00000000000..af002f91230 Binary files /dev/null and b/src/main/resources/images/food/ayam_tali_wang/ayam_taliwang_peha.png differ diff --git a/src/main/resources/images/food/ayam_tali_wang/coke.png b/src/main/resources/images/food/ayam_tali_wang/coke.png new file mode 100644 index 00000000000..69d11c11bf0 Binary files /dev/null and b/src/main/resources/images/food/ayam_tali_wang/coke.png differ diff --git a/src/main/resources/images/food/ayam_tali_wang/keropok.png b/src/main/resources/images/food/ayam_tali_wang/keropok.png new file mode 100644 index 00000000000..bef2ae7a511 Binary files /dev/null and b/src/main/resources/images/food/ayam_tali_wang/keropok.png differ diff --git a/src/main/resources/images/food/ayam_tali_wang/lime_juice.png b/src/main/resources/images/food/ayam_tali_wang/lime_juice.png new file mode 100644 index 00000000000..d3571279dd1 Binary files /dev/null and b/src/main/resources/images/food/ayam_tali_wang/lime_juice.png differ diff --git a/src/main/resources/images/food/ayam_tali_wang/tahu_tempeh.png b/src/main/resources/images/food/ayam_tali_wang/tahu_tempeh.png new file mode 100644 index 00000000000..5039f57157d Binary files /dev/null and b/src/main/resources/images/food/ayam_tali_wang/tahu_tempeh.png differ diff --git a/src/main/resources/images/food/ayam_tali_wang/taliwang_dory.png b/src/main/resources/images/food/ayam_tali_wang/taliwang_dory.png new file mode 100644 index 00000000000..08041a7988d Binary files /dev/null and b/src/main/resources/images/food/ayam_tali_wang/taliwang_dory.png differ diff --git a/src/main/resources/images/food/de_frank/bacon_cheese_hotdog.png b/src/main/resources/images/food/de_frank/bacon_cheese_hotdog.png new file mode 100644 index 00000000000..0370a43b3a0 Binary files /dev/null and b/src/main/resources/images/food/de_frank/bacon_cheese_hotdog.png differ diff --git a/src/main/resources/images/food/de_frank/cheese_steak_slider.png b/src/main/resources/images/food/de_frank/cheese_steak_slider.png new file mode 100644 index 00000000000..3a49b7ce9e5 Binary files /dev/null and b/src/main/resources/images/food/de_frank/cheese_steak_slider.png differ diff --git a/src/main/resources/images/food/de_frank/chicken_burger.png b/src/main/resources/images/food/de_frank/chicken_burger.png new file mode 100644 index 00000000000..d90f9405ee9 Binary files /dev/null and b/src/main/resources/images/food/de_frank/chicken_burger.png differ diff --git a/src/main/resources/images/food/de_frank/corn_dog.png b/src/main/resources/images/food/de_frank/corn_dog.png new file mode 100644 index 00000000000..3856a7a72a0 Binary files /dev/null and b/src/main/resources/images/food/de_frank/corn_dog.png differ diff --git a/src/main/resources/images/food/de_frank/fish_burger.png b/src/main/resources/images/food/de_frank/fish_burger.png new file mode 100644 index 00000000000..1c68b7ad2b4 Binary files /dev/null and b/src/main/resources/images/food/de_frank/fish_burger.png differ diff --git a/src/main/resources/images/food/de_frank/foie_grass.png b/src/main/resources/images/food/de_frank/foie_grass.png new file mode 100644 index 00000000000..817e91d7a99 Binary files /dev/null and b/src/main/resources/images/food/de_frank/foie_grass.png differ diff --git a/src/main/resources/images/food/de_frank/ribeye_burger.png b/src/main/resources/images/food/de_frank/ribeye_burger.png new file mode 100644 index 00000000000..d743119ff17 Binary files /dev/null and b/src/main/resources/images/food/de_frank/ribeye_burger.png differ diff --git a/src/main/resources/images/food/de_frank/sauerkraut_hotdog.png b/src/main/resources/images/food/de_frank/sauerkraut_hotdog.png new file mode 100644 index 00000000000..c8129f44302 Binary files /dev/null and b/src/main/resources/images/food/de_frank/sauerkraut_hotdog.png differ diff --git a/src/main/resources/images/food/default-menu-item.jpg b/src/main/resources/images/food/default-menu-item.jpg new file mode 100644 index 00000000000..1f3fedfd25a Binary files /dev/null and b/src/main/resources/images/food/default-menu-item.jpg differ diff --git a/src/main/resources/images/food/makan_xpress/meatball_spagetti.png b/src/main/resources/images/food/makan_xpress/meatball_spagetti.png new file mode 100644 index 00000000000..12d6931a7e2 Binary files /dev/null and b/src/main/resources/images/food/makan_xpress/meatball_spagetti.png differ diff --git a/src/main/resources/images/food/makan_xpress/naan.png b/src/main/resources/images/food/makan_xpress/naan.png new file mode 100644 index 00000000000..83580b4f833 Binary files /dev/null and b/src/main/resources/images/food/makan_xpress/naan.png differ diff --git a/src/main/resources/images/food/makan_xpress/nasi_goreng.png b/src/main/resources/images/food/makan_xpress/nasi_goreng.png new file mode 100644 index 00000000000..dc6153bc40f Binary files /dev/null and b/src/main/resources/images/food/makan_xpress/nasi_goreng.png differ diff --git a/src/main/resources/images/food/makan_xpress/pattaya.png b/src/main/resources/images/food/makan_xpress/pattaya.png new file mode 100644 index 00000000000..3c938be5637 Binary files /dev/null and b/src/main/resources/images/food/makan_xpress/pattaya.png differ diff --git a/src/main/resources/images/food/makan_xpress/prata.png b/src/main/resources/images/food/makan_xpress/prata.png new file mode 100644 index 00000000000..1cc907e729d Binary files /dev/null and b/src/main/resources/images/food/makan_xpress/prata.png differ diff --git a/src/main/resources/images/food/makan_xpress/tandoori.png b/src/main/resources/images/food/makan_xpress/tandoori.png new file mode 100644 index 00000000000..7aab5bfda8f Binary files /dev/null and b/src/main/resources/images/food/makan_xpress/tandoori.png differ diff --git a/src/main/resources/images/food/makan_xpress/tomyam.png b/src/main/resources/images/food/makan_xpress/tomyam.png new file mode 100644 index 00000000000..35d50aaa1a9 Binary files /dev/null and b/src/main/resources/images/food/makan_xpress/tomyam.png differ diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 36e6b001cd8..5cc1a33dce3 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -1,6 +1,6 @@ .background { -fx-background-color: derive(#1d1d1d, 20%); - background-color: #383838; /* Used in the default.html file */ + background-color: #0e1621; /* Used in the default.html file */ } .label { @@ -90,39 +90,49 @@ .list-view { -fx-background-insets: 0; -fx-padding: 0; - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: #182533; } .list-cell { -fx-label-padding: 0 0 0 0; -fx-graphic-text-gap : 0; -fx-padding: 0 0 0 0; + -fx-background-radius: 10; + -fx-border-radius: 10; + -fx-background-color: #182533 } -.list-cell:filled:even { - -fx-background-color: #3c3e3f; -} - -.list-cell:filled:odd { - -fx-background-color: #515658; +.list-cell:filled:hover { + -fx-background-color: #2a5378; } -.list-cell:filled:selected { - -fx-background-color: #424d5f; -} - -.list-cell:filled:selected #cardPane { - -fx-border-color: #3e7b91; - -fx-border-width: 1; +.list-cell:filled:hover #cardPane { + -fx-background-radius: 10; + -fx-border-radius: 10; } .list-cell .label { -fx-text-fill: white; } +.menu_food_name { + -fx-font-size:24px; +} +.menu_food_price { + -fx-font-size:16px; +} +.order_food_name { + -fx-font-size:16px; +} +.order_food_quantity { + -fx-font-size:16px; +} +.order_food_price { + -fx-font-size:12px; +} .cell_big_label { -fx-font-family: "Segoe UI Semibold"; - -fx-font-size: 16px; + -fx-font-size: 20px; -fx-text-fill: #010504; } @@ -133,12 +143,12 @@ } .stack-pane { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#0e1621, 20%); } .pane-with-border { - -fx-background-color: derive(#1d1d1d, 20%); - -fx-border-color: derive(#1d1d1d, 10%); + -fx-background-color: derive(#0e1621, 20%); + -fx-border-color: derive(#0e1621, 10%); -fx-border-top-width: 1px; } @@ -150,7 +160,7 @@ -fx-background-color: transparent; -fx-font-family: "Segoe UI Light"; -fx-font-size: 13pt; - -fx-text-fill: white; + -fx-text-fill: #96c8e4; } .result-display .label { @@ -185,7 +195,7 @@ } .context-menu { - -fx-background-color: derive(#1d1d1d, 50%); + -fx-background-color: derive(#182533, 50%); } .context-menu .label { @@ -193,7 +203,7 @@ } .menu-bar { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#182533, 20%); } .menu-bar .label { @@ -282,11 +292,13 @@ } .scroll-bar { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#182533, 20%); + -fx-background-radius:3px; + -fx-border-radius:3px; } .scroll-bar .thumb { - -fx-background-color: derive(#1d1d1d, 50%); + -fx-background-color: derive(#182533, 50%); -fx-background-insets: 3; } @@ -320,7 +332,7 @@ #commandTextField { -fx-background-color: transparent #383838 transparent #383838; -fx-background-insets: 0; - -fx-border-color: #383838 #383838 #ffffff #383838; + -fx-border-color: #0e1621 #0e1621 #ffffff #0e1621; -fx-border-insets: 0; -fx-border-width: 1; -fx-font-family: "Segoe UI Light"; @@ -328,12 +340,12 @@ -fx-text-fill: white; } -#filterField, #personListPanel, #personWebpage { +#filterField, #vendorListPanel, #vendorWebpage { -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); } #resultDisplay .content { - -fx-background-color: transparent, #383838, transparent, #383838; + -fx-background-color: derive(#182533, 20%); -fx-background-radius: 0; } diff --git a/src/main/resources/view/FoodListCard.fxml b/src/main/resources/view/FoodListCard.fxml new file mode 100644 index 00000000000..89d70a4573a --- /dev/null +++ b/src/main/resources/view/FoodListCard.fxml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/FoodListPanel.fxml similarity index 77% rename from src/main/resources/view/PersonListPanel.fxml rename to src/main/resources/view/FoodListPanel.fxml index 8836d323cc5..415beed6662 100644 --- a/src/main/resources/view/PersonListPanel.fxml +++ b/src/main/resources/view/FoodListPanel.fxml @@ -4,5 +4,5 @@ - + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index a431648f6c0..24a67265fab 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -1,30 +1,26 @@ - - - - - - - - - - - - + + + + + + + + + - + - + - - + + @@ -33,27 +29,47 @@ - + - + + + - - - - - + + + + + + + - + + + + + + + + + + + + + + + - + - - - - + diff --git a/src/main/resources/view/OrderItemListCard.fxml b/src/main/resources/view/OrderItemListCard.fxml new file mode 100644 index 00000000000..d63d4e91a42 --- /dev/null +++ b/src/main/resources/view/OrderItemListCard.fxml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/OrderItemListPanel.fxml b/src/main/resources/view/OrderItemListPanel.fxml new file mode 100644 index 00000000000..133211bde6e --- /dev/null +++ b/src/main/resources/view/OrderItemListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml index 58d5ad3dc56..6e202429e51 100644 --- a/src/main/resources/view/ResultDisplay.fxml +++ b/src/main/resources/view/ResultDisplay.fxml @@ -1,9 +1,8 @@ - - + + - -