diff --git a/.gitignore b/.gitignore index 71c9194e8bd..c5c5a39bafc 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,7 @@ src/test/data/sandbox/ # MacOS custom attributes files created by Finder .DS_Store docs/_site/ + + +bin/ +docs/.Rhistory diff --git a/README.md b/README.md index 13f5c77403f..cc482b1ef92 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,25 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/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. +# Waddle 🦆 + +[![CI Status](https://github.com/AY2223S1-CS2103T-W11-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2223S1-CS2103-W11-4/tp/actions) +[![codecov](https://codecov.io/gh/AY2223S1-CS2103T-W11-4/tp/branch/master/graph/badge.svg?token=45LHH4UIGB)](https://codecov.io/gh/AY2223S1-CS2103T-W11-4/tp) + +![Ui](https://github.com/AY2223S1-CS2103T-W11-4/tp/blob/master/docs/images/Ui.png) + +### Introduction + +* **Waddle** is a simple, no-frills travel planning application catered to people who love doing everything on their keyboards. +* **Features :** + * You can plan your travels in 3 simple steps! + * Create a trip + * Add activities + * Schedule + * After you plan your trip, easily export your itinerary for easy reference during the trip itself. + +* Visit our product website for a detailed documentation, [**Waddle Product Website**](https://ay2223s1-cs2103t-w11-4.github.io/tp/UserGuide.html). + + +### Acknowledgements +* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). + + +* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5) diff --git a/build.gradle b/build.gradle index 108397716bd..4d436523890 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { id 'jacoco' } -mainClassName = 'seedu.address.Main' +mainClassName = 'seedu.waddle.Main' sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 @@ -20,6 +20,10 @@ checkstyle { toolVersion = '10.2' } +run { + enableAssertions = true +} + test { useJUnitPlatform() finalizedBy jacocoTestReport @@ -57,6 +61,9 @@ dependencies { implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac' implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.apache.pdfbox', name: 'pdfbox', version: '2.0.27' + implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.4' + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.7.0' implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.7.4' @@ -66,7 +73,7 @@ dependencies { } shadowJar { - archiveFileName = 'addressbook.jar' + archiveFileName = 'waddle.jar' } defaultTasks 'clean', 'test' diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..b578b2412f4 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -9,51 +9,51 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` ## Project team -### John Doe +### Clement Foo Shi Yu - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/CFSY)] +[[portfolio](team/cfsy.md)] -* Role: Project Advisor +* Role: Developer +* Responsibilities: Logic + UI -### Jane Doe +### Chen Shun - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/ciaoosuuu)] +[[portfolio](team/ciaoosuuu.md)] -* Role: Team Lead -* Responsibilities: UI +* Role: Developer +* Responsibilities: Logic -### Johnny Doe +### Hui Yi Lu - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](http://github.com/pewggls)] [[portfolio](team/pewggls.md)] * Role: Developer -* Responsibilities: Data +* Responsibilities: Storage -### Jean Doe +### Law Sean Meng - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/seox123)] +[[portfolio](team/seox123.md)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: Logic + Parser -### James Doe +### Tan Shao Ning - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/ningtan11)] +[[portfolio](team/ningtan11.md)] * Role: Developer -* Responsibilities: UI +* Responsibilities: Logic + Documentation diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..bb01ccb361b 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -9,7 +9,8 @@ title: Developer Guide ## **Acknowledgements** -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +* [Apache PDFBox®](https://pdfbox.apache.org/) +* * [Apache Commons](https://commons.apache.org/) -------------------------------------------------------------------------------------------------------------------- @@ -36,18 +37,19 @@ Given below is a quick overview of main components and how they interact with ea **Main components of the architecture** -**`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/AY2223S1-CS2103T-W11-4/tp/blob/master/src/main/java/seedu/waddle/Main.java) and [`MainApp`](https://github.com/AY2223S1-CS2103T-W11-4/tp/blob/master/src/main/java/seedu/waddle/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. [**`Commons`**](#common-classes) represents a collection of classes used by multiple other components. -The rest of the App consists of four components. +The rest of the App consists of five components. * [**`UI`**](#ui-component): The UI of the App. * [**`Logic`**](#logic-component): The command executor. * [**`Model`**](#model-component): Holds the data of the App in memory. * [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk. +* [**`StageManager`**](#stageManager-component): Keeps track of the App's current stage. **How the architecture components interact with each other** @@ -56,7 +58,9 @@ The *Sequence Diagram* below shows how the components interact with each other f -Each of the four main components (also shown in the diagram above), +StageManager (shown in the diagram above) uses the Singleton pattern and is accessible by the Logic component for checking and changing the app's current stage. + +The other four main components (also shown in the diagram above), * defines its *API* in an `interface` with the same name as the Component. * implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point. @@ -69,34 +73,34 @@ The sections below give more details of each component. ### UI component -The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java) +The **API** of this component is specified in [`Ui.java`](https://github.com/AY2223S1-CS2103T-W11-4/tp/blob/master/src/main/java/seedu/waddle/ui/Ui.java) ![Structure of the UI Component](images/UiClassDiagram.png) -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `ItineraryListPanel`, `ItemGroupListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. -The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml) +The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/AY2223S1-CS2103T-W11-4/tp/blob/master/src/main/java/seedu/waddle/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/AY2223S1-CS2103T-W11-4/tp/blob/master/src/main/resources/view/MainWindow.fxml) The `UI` component, * executes user commands using the `Logic` component. * listens for changes to `Model` data so that the UI can be updated with the modified data. * keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands. -* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`. +* depends on some classes in the `Model` component, as it displays `Itinerary` object residing in the `Model`. ### Logic component -**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java) +**API** : [`Logic.java`](https://github.com/AY2223S1-CS2103T-W11-4/tp/blob/master/src/main/java/seedu/waddle/logic/Logic.java) Here's a (partial) class diagram of the `Logic` component: How the `Logic` component works: -1. When `Logic` is called upon to execute a command, it uses the `AddressBookParser` class to parse the user command. -1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to add a person). -1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. +1. When `Logic` is called upon to execute a command, it uses the `WaddleParser` class to parse the user command. +2. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. +3. The command can communicate with the `Model` when it is executed (e.g. to add an item). +4. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call. @@ -110,43 +114,36 @@ Here are the other classes in `Logic` (omitted from the class diagram above) tha How the parsing works: -* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object. +* When called upon to parse a user command, the `WaddleParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `WaddleParser` returns back as a `Command` object. * All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. ### Model component -**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) +**API** : [`Model.java`](https://github.com/AY2223S1-CS2103T-W11-4/tp/blob/master/src/main/java/seedu/waddle/model/Model.java) The `Model` component, -* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object). -* stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. +* stores the app data i.e., all `Itinerary` objects (which are contained in a `UniqueItineraryList` object). +* stores the currently 'selected' `Itinerary` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. * stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects. -* does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components) - -
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
- - - -
- +* does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components). ### Storage component -**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/AY2223S1-CS2103T-W11-4/tp/blob/master/src/main/java/seedu/waddle/storage/Storage.java) The `Storage` component, -* can save both address book data and user preference data in json format, and read them back into corresponding objects. -* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). +* can save both Waddle data and user preference data in json format, and read them back into corresponding objects. +* inherits from both `WaddleStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). * depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`) ### Common classes -Classes used by multiple components are in the `seedu.addressbook.commons` package. +Classes used by multiple components are in the `seedu.waddle.commons` package. -------------------------------------------------------------------------------------------------------------------- @@ -154,90 +151,119 @@ 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 +### Plan/Unplan Feature -#### Proposed Implementation +The Plan/Unplan feature allows users to allocate an item to a particular time slot within a day. -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: +#### Implementation + The Plan/Unplan mechanism is facilitated mainly by the `Itinerary` and `Day` classes. First, let us take a look at class structure of the `Itinerary` ,`Day` and `item` classes. -* `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. +![ItineraryClassDiagram](images/ItineraryClassDiagram.png) -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +The diagram above is a partial class diagram containing the all the fields and relevant methods needed to understand the Plan/Unplan implementation. -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +The fields of particular concern are explained below. +* `unscheduledItemList` — A UniqueItemList of unscheduled items in an Itinerary. +* `days` — A List of Days in an Itinerary. +* `itemList` — A UniqueItemList of scheduled items in a Day. -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. +Additionally, we will explain the methods of particular concern below. -![UndoRedoState0](images/UndoRedoState0.png) +* `Itinerary#planItem(Index, dayNumber, LocalTime)` — Transfers the selected item from the itinerary's unscheduledItemList to the itemList of the selected day. Sets the startTime of the itinerary to the specified startTime. Returns the item. +* `Itinerary#unplanItem(MultiIndex)` — Transfers the selected item from the selected day's itemList to the itinerary's unscheduledItemList. Resets the startTime field of the item. Re-sort the items in `unscheduledItemList` by order of priority. Returns the item. +* `Day#additem(Item)` — Adds the provided item into the itemList if there are no time conflicts. Re-sort the items in itemList by order of startTime. +* `Day#removeitem(Index)` — Removes the item at the specified index from itemList. Returns the item. +* `Day#getConflictingItems(Item)` — Returns a list of items, in the day's itemList, that have time conflicts with the provided item. +* `Item#getStartTime()` — Returns the start time of the item. +* `Item#getEndTime()` — Returns the end time of the item. -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. +Given below is an example usage scenario and how the Plan/Unplan mechanism behaves at each step. The sequence diagram is a partial diagram showing omitting the details of how the `PlanCommand` is executed. This detail will be shown in the next sequence diagram. -![UndoRedoState1](images/UndoRedoState1.png) +![PlanSequenceDiagram](images/PlanSequenceDiagram.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`. +Step 1. The user executes `plan 1 d/1 t/12:00` command to plan the first item in the unscheduledItemList to the first day at time 12:00. The `plan` command is then parsed into a `PlanCommand` object and executed by the LogicManger. This is similar to what was shown in the Architecture Sequence Diagram under the Design section. -![UndoRedoState2](images/UndoRedoState2.png) +![PlanSequenceDiagram2](images/PlanSequenceDiagram2.png) -
: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 `PlanCommand` object's `execute()` method is called. `PlanCommand` gets the singleton instance of StageManager through `StageManager#getInstance()` followed by the current selected `Itinerary` object through `StageManager#getSelectedItinerary()`. -
- -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) +Step 3. `PlanCommand` calls `Itinerary#planItem(itemIndex, dayNumber, startTime)` with the parsed values from the `plan 1 d/1 t/12:00` 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. +Step 4. `Itinerary` gets the item from its unscheduledItemList at the specified `itemIndex`. It sets the startTime of the item and then calls `Day#addItem(item)` of the `Day` at index `dayNumber` with the item. -
- -The following sequence diagram shows how the undo operation works: +Step 5. `Day`self invokes `Day#getConflictingItems(item)`. If there are no conflicting items, the incoming item is added into the day's `itemList`. If there are conflicting items, a CommandException is thrown with a time conflict message. -![UndoSequenceDiagram](images/UndoSequenceDiagram.png) +Step 6. If the item is successfully added, a `CommandResult` object is created with the success message. -
: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. +
:information_source: **Note:** The Unplan command works in a similar manner. Instead of `Itinerary#planItem(itemIndex, dayNumber, startTime)` and `Day#addItem(item)`, `Itinerary#unplanItem(MultiIndex)` and `Day#removeitem(Index)` are called instead.
-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. +### Edit an item -
: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. +Refer above for the class structure of the `Itinerary` ,`Day` and `item` classes. -
+Items can be either scheduled or unscheduled. Unscheduled items are stored in an itinerary's unscheduledItemList while scheduled items are stored in the respective days' itemLists. as such, both are handled in slightly different manners. -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 a walk-through of the edit item mechanism. We will skip to where the `EditItemCommand#execute()` method is called since the preceding portion is similar to what we have laid out in previous sections. -![UndoRedoState4](images/UndoRedoState4.png) +Step 1. The `EditItemCommand` object's `execute()` method is called. -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 multiIndex in `EditItemCommand` is checked for a presence of a dayNumber. If dayNumber is absent, follow the steps under Unscheduled Items, else follow the steps under Scheduled Items. -![UndoRedoState5](images/UndoRedoState5.png) +* **Unscheduled Items** + * Step 3. The index is checked to be within bounds of the itinerary's unscheduledItemList. If it is not, a `CommandException` is thrown. + * Step 4. A new `Item` object, `editedItem` is created with the edited inputs. + * Step 5. A check for duplicates in the unscheduledItemList is done. If there is a duplicate, a `CommandException` is thrown. + * Step 6. The original item is replaced with `editedItem`. + * Step 7. The unscheduledItemList is re-sorted in order of priority. +* **Scheduled Items** + * Step 3. The dayNumber and index are checked to be within bounds of the itinerary's duration and unscheduledItemList respectively. If any is not, a `CommandException` is thrown. + * Step 4. A new `Item` object, `editedItem` is created with the edited inputs. + * Step 5. A check for duplicates in the day's itemList is done. If there is a duplicate, a `CommandException` is thrown. + * Step 6. The original item is removed from the day's itemList + * Step 7. Attempt to add the `editedItem` into the itemList. If a time conflict is detected, the original item is added back into the itemList and a `CommandException` is thrown. + * Step 8. If the `editedItem` is added successfully, the itemList is re-sorted in order of startTime. -The following activity diagram summarizes what happens when a user executes a new command: +### Edit an itinerary - +An itinerary's details (description, start date, duration, and budget) can be edited by changing the fields of an Itinerary object. -#### Design considerations: +Given below are some example usage scenarios and how the editing mechanism is carried out. As per the examples above, we will skip to where the `EditCommand#execute()` method is called. -**Aspect: How undo & redo executes:** +Step 1. The `EditCommand` object's `execute()` method is called. -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. +* **Editing an itinerary's description, budget, and start date** + * Step 2. A new `Itinerary` object, `editedItinerary` is created with the edited inputs. + * Step 3. A check for duplicates in model is done. If there is a duplicate, a `CommandException` is thrown. + * Step 4. The original itinerary is replaced with `editedItinerary`. +* **Editing an itinerary's duration** + * The steps are similar but with the additional checks below. + * If the duration is extended, add more Day objects to the Itinerary's list. + * If the duration is reduced, remove the extra Day objects from the Itinerary's list starting from the back (i.e., the last Day is removed first). + * The Items that were scheduled in the deleted Days would be unscheduled. + * Update the duration field. -* **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. +### Export to PDF -_{more aspects and alternatives to be added}_ +The exporting feature is facilitated by the Apache PDFBox Java library. -### \[Proposed\] Data archiving +PDFBox converts a fill-able PDF template into an Acroform. Each fill-able field of the Acroform (PDField) can be identified with +a field name and PDField can be filled by calling `PDField#setValue(value)`. -_{Explain here how the data archiving feature will be implemented}_ +Given below is a walk-through of the exporting to pdf mechanism. +* Step 1. The `PdfCommand` object's `execute()` method is called. +* Step 2. A new `PdfFiller` object is created with the current itinerary and a path to the export template. +* Step 3. `PdfFiller#fillItinerary()` method is called. +* Step 4. `PdfFiller#fillDay()` method is called with each Day object in the itinerary. +* Step 5. In each `PdfFiller#fillDay()`, the list of scheduled items will be converted to a list of PDFieldInfo which contains +the description of the item as value and a name which identifies the PDField the item is supposed to fill. +* Step 6. `PdfFiller#fillForm()` method is called. +* Step 7. An acroform is generated using the template pdf and each PDField in the acroform is filled up according using +the list of PDFieldInfo which specifies the PDField to fill and the value to fill with. +* Step 8. The filled up forms for the different days are then appended together and export as one pdf. +PDF will be stored in the user's Documents folder with the naming being ".pdf". -------------------------------------------------------------------------------------------------------------------- @@ -257,71 +283,143 @@ _{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 plan itineraries for trips * 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**: Provides a distraction free and fast way to plan itineraries ### User stories Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` -| Priority | As a …​ | I want to …​ | So that I can…​ | -| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- | -| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App | -| `* * *` | user | add a new 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 | +| Priority | As a …​ | I want to …​ | So that I can…​ | +|----------|--------------------------------------|-------------------------------------------------|----------------------------------------------------------------------| +| `* * *` | user | see usage instructions | refer to instructions when I forget how to use the App | +| `* * *` | user | add a new itinerary | | +| `* * *` | user | delete an itinerary | remove itineraries that I no longer need | +| `* * *` | user | find an itinerary by keywords | access an itinerary without having to scroll through the entire list | +| `* * *` | user | edit the details of an itinerary | | +| `*` | user with many itineraries in Waddle | sort itineraries by date | see which trips are coming first | +| `* * *` | user | add activities to an itinerary | | +| `* * *` | user | edit activities in an itinerary | | +| `* * *` | user | schedule activities in an itinerary | | +| `* * *` | user | select an itinerary for planning | | +| `* * *` | user | see the activities i have added in an itinerary | schedule better | +| `*` | resourceful user | import others' itineraries | to reference | +| `*` | user who travels in groups | create a split schedule | different groups can do different things simultaneously | +| `*` | user | share the itinerary to non Waddle users | travel buddies can all use it | +| `*` | user | mark done activities | | +| `*` | meticulous | categorise each activity by activity type | | +| `*` | unique user | create custom activity categories | | +| `*` | fast user | use shortcuts | be even faster | +| `*` | fast user | add custom shortcuts for commands | | + -*{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 user begins at the starting page, the **System** is `Waddle` and the **Actor** is the `user`, unless specified otherwise) -**Use case: Delete a person** +**Use case: Add an itinerary** **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 add itinerary. +2. User enters the description of the itinerary. +3. Waddle creates the itinerary and provides a confirmation to the user.
+Use case ends. + +**Extensions** + +* 2a. The itinerary description is not entered. + * Waddle shows an error message.
+ Use case resumes at step 2. + - Use case ends. +**Use case: Edit a specific itinerary’s details** + +**MSS** + +1. User selects a specific itinerary. +2. User enters new details of the itinerary. +3. Waddle updates the itinerary details and provides a confirmation to the user.
+Use case ends. **Extensions** -* 2a. The list is empty. +* 2a. The itinerary description is incomplete. + * Waddle shows an error message.
+ Use case resumes at step 2. + +**Use case: Delete an itinerary** + +**MSS** + +1. User selects a specific itinerary. +2. User requests to delete the itinerary. +3. Waddle requests for user confirmation. +4. User confirms intention to delete itinerary. +5. Waddle deletes the itinerary.
+Use case ends. + +**Extensions** +* 4a. User decides against deleting the itinerary.
Use case ends. -* 3a. The given index is invalid. - * 3a1. AddressBook shows an error message. +**Use case: Add an item to an itinerary** + +**MSS** + +1. User selects a specific itinerary. +2. User requests to add an item. +3. User enters details of the item. +4. Waddle adds the item and provides confirmation to the user.
+ Use case ends. + +**Extensions** +* 3a. The item details are incomplete. + * Waddle shows an error message.
+ Use case resumes at step 3. + + +**Use case: Delete an item from an itinerary** + +**MSS** + +1. User selects a specific itinerary. +2. User requests to delete a chosen item. +3. Waddle requests for user confirmation. +4. User confirms intention to delete chosen item. +5. Waddle deletes the item from the itinerary.
+Use case ends. + +**Extensions** +* 2a. User does not provide index of item. + * Waddle shows an error message.
+ Use case resumes from step 2. +* 4a. User decides against deleting the item.
+ Use case ends. - Use case resumes at step 2. -*{More to be added}* ### Non-Functional Requirements -1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. -2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. -3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. +2. Should be able to hold up to 500 itineraries 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. +4. The GUI should be intuitive and easy to understand. +5. The system should respond in at most 1 second. -*{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 -------------------------------------------------------------------------------------------------------------------- @@ -346,32 +444,19 @@ testers are expected to do more *exploratory* testing. 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 double-clicking the jar file.
Expected: The most recent window size and location is retained. -1. _{ more test cases …​ }_ - -### Deleting a person -1. Deleting a person while all persons are being shown +### Deleting an itinerary - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. +1. Deleting an itinerary while all itineraries are being shown 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. + Expected: First itinerary is deleted from the list. Details of the deleted itinerary shown in the status message. 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. + Expected: No itinerary is deleted. Error details shown in the status message. 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
Expected: Similar to previous. - -1. _{ more test cases …​ }_ - -### Saving data - -1. Dealing with missing/corrupted data files - - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ - -1. _{ more test cases …​ }_ diff --git a/docs/SettingUp.md b/docs/SettingUp.md index 275445bd551..c9fd8663d3d 100644 --- a/docs/SettingUp.md +++ b/docs/SettingUp.md @@ -45,7 +45,7 @@ If you plan to use Intellij IDEA (highly recommended): 1. **Learn the design** - When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [AddressBook’s architecture](DeveloperGuide.md#architecture). + When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [Waddle’s architecture](DeveloperGuide.md#architecture). 1. **Do the tutorials** These tutorials will help you get acquainted with the codebase. diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3716f3ca8a4..9c57ee35096 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -2,41 +2,220 @@ layout: page title: User Guide --- +# Waddle User Guide 🦆 +Waddle is a **simple, no-frills travel planning application** aligned with your travel itinerary planning needs. +Whether you are a seasoned traveler or a beginner at planning your own itinerary, Waddle provides you with a structured, focused and intuitive way to plan your trips. -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. +With Waddle, you can plan your travel itinerary in **3 simple steps**: +1. Create a trip itinerary +2. Add activities to your itinerary wishlist +3. Make a schedule for your trip -* Table of Contents -{:toc} +**It's that simple**. + +If you wish to learn more about Waddle and its features, then this comprehensive user guide is for you! +For tips on how to navigate our user guide, check out our [User Guide Tips](#user-guide-tips) section. +For instructions on how to set up Waddle, visit our [Quick start](#quick-start) section. +For basic information on how to make the best of Waddle, check out our [Tutorial for beginners](#tutorial-for-beginners). + +-------------------------------------------------------------------------------------------------------------------- +
+ +## Table of Contents +1. [**User Guide Tips**](#user-guide-tips) +2. [**Quick start**](#quick-start) +3. [**Tutorial for beginners**](#tutorial-for-beginners) +4. [**Features**](#features) + 1. [**Universal commands**](#universal-commands) + 1. [`help` Viewing help](#viewing-help--help) + 2. [`exit` Exiting Waddle](#exiting-waddle--exit) + 2. [**The home page**](#the-home-page) + 3. [**Commands on the home page**](#commands-on-the-home-page) + 1. [`add` Creating a new itinerary](#creating-a-new-itinerary--add) + 2. [`list` Listing all itineraries](#listing-all-itineraries--list) + 3. [`find` Locating itineraries by description](#locating-itineraries-by-description--find) + 4. [`edit` Editing the details of an itinerary](#editing-the-details-of-an-itinerary--edit) + 5. [`delete` Deleting an itinerary](#deleting-an-itinerary--delete) + 6. [`clear` Clearing itineraries](#clearing-itineraries--clear) + 7. [`select` Selecting an itinerary](#selecting-an-itinerary--select) + 4. [**The activity planning page**](#the-activity-planning-page) + 5. [**Commands on the activity planning page**](#commands-on-the-activity-planning-page) + 1. [`add` Adding an activity](#adding-an-activity--add) + 2. [`edit` Editing the details of an activity](#editing-the-details-of-an-activity--edit) + 3. [`delete` Deleting an activity](#deleting-an-activity--delete) + 4. [`free` Viewing vacant timeslots](#viewing-vacant-timeslots--free) + 5. [`plan` Scheduling an activity](#scheduling-an-activity--plan) + 6. [`unplan` Unscheduling an activity](#unscheduling-an-activity--unplan) + 7. [`copy` Copying to clipboard](#copying-to-clipboard--copy) + 8. [`pdf` Exporting as PDF file](#exporting-as-pdf-file--pdf) + 9. [`home` Returning to home page](#returning-to-home-page--home) + 6. [**Advanced**](#advanced) + 1. [Saving the data](#saving-the-data) + 2. [Editing the data file](#editing-the-data-file) +5. [**FAQ**](#faq) +6. [**Command summary**](#command-summary) + 1. [**Home page commands**](#home-page-commands) + 2. [**Activity-planning page commands**](#activity-planning-page-commands) +7. [**Glossary**](#glossary) -------------------------------------------------------------------------------------------------------------------- +
+ +## User Guide Tips + +### Finding what you need + +1. [Quick start](#quick-start) will help you set up Waddle +2. [Tutorial for Beginners](#tutorial-for-beginners) walks you through our graphical interface and also guides you on planning your very first itinerary on Waddle +3. [Features](#features) helps you understand how our features can be used in your planning +4. [FAQ](#faq) answers some of the most common questions we have gotten from our users. If you have questions for us, this section might just be the one you need! +5. [Command Summary](#command-summary) provides a summarised list of our features' commands for your easy reference +6. [Glossary](#glossary) explains some of the terms we used. If you do not understand a term in this guide, this section might be of help! +7. You can use the buttons at the bottom right of each page to return to the [Table of Contents](#table-of-contents) + +### Understanding the symbols and syntax + +| Symbol/Syntax | Meaning | +|----------------------|-------------------------------------------------------------------------------------------------------------------| +| `command` | When a word or phrase is highlighted with this markup, it simply means that it is related to a command or keyword | +| :exclamation: | The exclamation indicates a warning and contains information that is important | +| :bulb: | When you see this, you can expect some tips from us! | +| :information_source: | Wee will use this icon to provide you with information you should take note of | + +-------------------------------------------------------------------------------------------------------------------- +
+ +## Quick Start + +To begin planning your travels with Waddle, simply set it up as follows: + +1. Ensure you have Java `11` or above installed in your Computer. If you do not, you may install it [here](https://www.oracle.com/java/technologies/downloads). + +2. Download the latest `waddle.jar` from [here](https://github.com/AY2223S1-CS2103T-W11-4/tp/releases/). + +3. Copy the file to the folder you want to use as the _home folder_ for Waddle. + +4. Double-click the file to start the app. This will bring you to the Waddle [home page](#the-home-page). A graphical user interface (GUI) similar to the below should appear in a few seconds. + Note how the app contains some sample data.
+ ![Home Page](images/mainPage.png) + +And you're ready to waddle! To execute any command, type it command in the command box and press Enter. e.g. typing **`help`** and pressing Enter will open the help window. For more information on how to begin waddling, carry on to the [Tutorial for beginners](#tutorial-for-beginners). + +Alternatively, you may wish to dive straight to the full list of commands and their details provided in the [Features](#features) section below. + +-------------------------------------------------------------------------------------------------------------------- +
+ +## Tutorial for Beginners + +If you are a beginner to Waddle, this tutorial will walk you through creating and planning your very first Waddle itinerary. + + +This is the [home page](#the-home-page), which you will encounter when you open Waddle. +It displays all your current itineraries. +The various sections of the interface have been labeled in blue for your reference. +If this is your first time launching Waddle, you will see the following page with two sample itineraries:

+![home-page-sample-ui](images/ug-beg-tut/main-page-sample-ui.png) + +If you wish to, you can [clear](#clearing-itineraries--clear) the sample itineraries by typing `clear` into the command box and pressing Enter. +You will then see the below message:

+![clear-command](images/clear.png) -## Quick start +Now you can add your first itinerary. To do so, use the [`add` command](#creating-a-new-itinerary--add) with the relevant itinerary details. +Try executing the command `add d/Summer Trip sd/2023-06-01 du/5`. +It will add an itinerary named Summer Trip, starting from 1 June 2023 with a duration of 5 days. +You should now see the message below and your itinerary added on the home page as follows:

+![add-itinerary](images/ug-beg-tut/add-itinerary.png) -1. Ensure you have Java `11` or above installed in your Computer. +
+ +**:information_source: Notes:**
-1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +* You can replace the `add` command inputs with the relevant details of your itinerary. +* You can include optional details such as the country, number of people, and budget. + - e.g. Enter `add d/Summer Trip c/Japan sd/2023-06-01 du/5 p/4 b/2000` instead to specify the country as Japan, number of people as 4, and your budget as $2000. +* For additional information, refer to the [`add` command](#creating-a-new-itinerary--add) in the [Features](#features) section under home page commands. -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +
-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.
- ![Ui](images/Ui.png) +Select the itinerary which you have just created by typing in `select` along with the index of the itinerary on the list, then press Enter. +For example, if "Summer Trip" is shown as the first itinerary on the list, enter `select 1`. +You will now see the planning page for the itinerary as follows:

+![planning-page-ui](images/ug-beg-tut/planning-page-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: +You can return to the main page anytime you wish by executing `home`. - * **`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. +**:information_source: Note about commands:**
+ +* Note that the commands for the main page are different from those for an itinerary planning page. + Some commands only apply to one type of page while some are universally applicable (i.e. they can be executed on any page). +* To find out more about which commands apply to your current page, visit the user guide's [Features](#features) section. + +
+
- * **`delete`**`3` : Deletes the 3rd contact shown in the current list. - * **`clear`** : Deletes all contacts. +To add an activity to your Wishlist, use the [`add` command](#adding-an-activity--add). +Try adding a new activity by typing in `add d/Visit Meiji Shrine du/120` to the command box and press Enter. +This will add a new activity with the description of "Visit Meiji Shrine" and a duration of 120 minutes. +You should now see the activity being displayed in the Wishlist.

+![add-item](images/ug-beg-tut/add-item.png) + +
+ +**:information_source: Notes:**
+ +* You can replace the `add` command inputs with the relevant details of your activities. +* You can include optional details such as the priority and cost. + - e.g. Enter `add d/Visit Meiji Shrine du/120 p/5 c/20` instead to specify the priority as 5 and the cost as $20. +* For additional information, refer to the [`add` command](#adding-an-activity--add) explanation in the [Features](#features) section under itinerary planning page commands. + +
- * **`exit`** : Exits the app. +Try adding a few more activities! You should see the activities listed in the Wishlist. -1. Refer to the [Features](#features) below for details of each command. +To schedule an item from your Wishlist on a specific day in your itinerary, use the [`plan` command](#scheduling-an-activity--plan). +For example, type `plan 1 d/1 st/10:00` into the command box and Enter. +Waddle will schedule the first item on your Wishlist for Day 1 of your itinerary starting at 10am. +You should see the item being moved from the Wishlist section to the Day 1 item list, as follows:

+![plan-item](images/ug-beg-tut/plan-item.png) + +
+ +**:information_source: Notes:**
+ +* For additional information, refer to the [`plan` command](#scheduling-an-activity--plan) explanation in the [Features](#features) section under itinerary planning page commands. + +
+ +Note that the index of the activity is now shown as 1.1, meaning it is the first activity on Day 1. + +Now you can try scheduling all the activities in your Wishlist! + +Once you are satisfied with your schedule, you can export your schedule as a PDF document using the [`pdf` command](#exporting-as-pdf-file--pdf). +Type `pdf` into the command box and Enter. +You should now see the below:

+![pdf](images/ug-beg-tut/pdf.png) + +Navigate to your computer's Documents folder, where you should see a folder named Waddle. +Open up the folder to see a PDF document with the same name as your exported itinerary. +You can open the PDF document to view your itinerary schedule. + +You can easily send your itinerary details to others using the [`copy` command](#copying-to-clipboard--copy). +Type `copy` into the command box and Enter. +This will copy your itinerary to your clipboard, and you can now paste it in another location such as a messaging or email application. +You should also see the below displayed in Waddle:

+![copy](images/ug-beg-tut/copy.png) + +
+Congratulations! You have now created and planned your very first travel itinerary using Waddle. +We hope this tutorial has helped you kickstart your journey! +You can expand your Waddle knowledge further by reading the other sections of this User Guide, starting with the [Features](#features) that are available to you. -------------------------------------------------------------------------------------------------------------------- +
## Features @@ -45,148 +224,468 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo **:information_source: 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 `add d/DESCRIPTION ...`, `DESCRIPTION` is a parameter which can be used as `add d/My Japan Trip`. + * 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. `d/DESCRIPTION [c/COUNTRY] sd/START_DATE du/DURATION` can be used as `d/My Japan Trip c/Japan sd/2023-04-01 du/14` or as `d/My Japan Trip sd/2023-04-01 du/14`. * Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. + - e.g. if the command specifies `c/COUNTRY d/DESCRIPTION`, `d/DESCRIPTION c/COUNTRY` is also acceptable. -* If a parameter is expected only once in the command but you specified it multiple times, only the last occurrence of the parameter will be taken.
- e.g. if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken. +* If a parameter is expected only once in the command, but you specified it multiple times, only the **last occurrence** of the parameter will be taken.
+ - e.g. if you specify `d/Eat Ramen d/Aquarium`, only `d/Aquarium` will be taken. * Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
- e.g. if the command specifies `help 123`, it will be interpreted as `help`. + - e.g. if the command specifies `help 123`, it will be interpreted as `help`.
+
+ +### Universal commands + +Most commands in Waddle can only be used on the [main page](#the-home-page) or the [activity planning page](#the-activity-planning-page). However, the commands in this section may be used on either page at any time. ### Viewing help : `help` -Shows a message explaning how to access the help page. +Brings up the help message, which contains a link to this guide. ![help message](images/helpMessage.png) Format: `help` +### Exiting Waddle : `exit` + +Exits the Waddle program. + +Format: `exit` + +
+ +### The home page + +The home page, or home page, of Waddle displays the list of itineraries you have created and stored in the app. + +![Home Page](images/mainPage.png) +[Commands exclusive to the home page](#commands-on-the-home-page) can help you: +* [add](#creating-a-new-itinerary--add) new itineraries +* [list](#listing-all-itineraries--list) or [find](#locating-itineraries-by-description--find) existing itineraries +* [edit](#editing-the-details-of-an-itinerary--edit) or [delete](#deleting-an-itinerary--delete) existing itineraries +* [clear](#clearing-itineraries--clear) all existing itineraries + +Using the [`select` command](#selecting-an-itinerary--select) will bring you to the [planning page](#the-activity-planning-page) of the selected itinerary. + +
+ +### Commands on the home page -### Adding a person: `add` +### Creating a new itinerary : `add` -Adds a person to the address book. +Adds an itinerary to Waddle. -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +Format: `add d/DESCRIPTION sd/START_DATE du/DURATION [c/COUNTRY] [p/NUMBER_OF_WADDLERS] [b/BUDGET]` + +* Adds a new itinerary named `DESCRIPTION` to the itinerary list. It cannot be blank and must only contain +alphanumeric characters, spaces and these following special characters: `()&!':.,-`. +* `START_DATE` is the date of the first day in the itinerary. It must be given in the format `yyyy-mm-dd` and is a valid future date. +* `DURATION` will determine the number of days in the itinerary, and must be between 1 and 365 days. + - e.g. `sd/2022-12-10 du/3` would mean that the trip is from 10 Dec 2022 to 12 Dec 2022. +* `BUDGET` is the budget for the itinerary in dollars, or dollars and cents, and must be between 0 and 1,000,000. + - e.g. `b/1000` is $1,000. + - e.g. `b/1000.50` is $1,000.50. + +
+ +**:information_source: Notes:**
+ +* You cannot add an itinerary with the same description as an existing itinerary. +* Waddle only accepts english letters and spaces for `COUNTRY`, special characters like `'`, `&`, `!` are not allowed.
+ - Example of invalid input: `c/Côte d'Ivoire`, `c/中国` +* The budget input should only contain numbers and one decimal point.
+ - Example of invalid input: `b/1,000,000` +* If more than 2 decimal places are provided for the budget, Waddle rounds it up to 2 decimal places.
+ - e.g. `b/1000.505` will be reflected as $1,000.51. -
:bulb: **Tip:** -A person can have any number of tags (including 0)
Examples: -* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` -* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal` +* `add d/Summer Trip c/Singapore sd/2025-10-28 du/15 p/4 b/1000` + +![Add Command](images/add-itinerary.png) -### Listing all persons : `list` +### Listing all itineraries : `list` -Shows a list of all persons in the address book. +Shows a list of all itineraries in Waddle. Format: `list` -### Editing a person : `edit` +![List Command](images/list.png) -Edits an existing person in the address book. +### Locating itineraries by description : `find` -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +Finds itineraries with names containing any of the given keywords. -* 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. +Format: `find KEYWORD [MORE_KEYWORDS]` + +* The search is case-insensitive. e.g. `india` will match `India` +* The order of the keywords does not matter. e.g. `Trip Japan My` will match `My Japan Trip` +* The search is based on itinerary descriptions only. +* Only full words will be matched e.g. `Jap` will not match `Japan` +* Itineraries matching at least one of the provided keywords will be returned (i.e. `OR` search). + - e.g. `find Japan Trip` will return `My Germany Trip`, since there is a match for the keyword `Trip`. +* Use the [`list`](#listing-all-itineraries--list) command to see all itineraries again. 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. +* `find India` returns `My India Trip` and `India Expedition` +* `find India Trip` returns `My Japan Trip`, `My India Trip`, `India Expedition` +* `find trip` returns the following result:

+ ![result for 'find trip'](images/find.png) -### Locating persons by name: `find` +### Editing the details of an itinerary : `edit` -Finds persons whose names contain any of the given keywords. +Edits an existing itinerary in Waddle. -Format: `find KEYWORD [MORE_KEYWORDS]` +Format: `edit INDEX [d/DESCRIPTION] [c/COUNTRY] [sd/START_DATE] [du/DURATION] [p/NUMBER_OF_WADDLERS] [b/BUDGET]` + +* Edits the itinerary at the specified `INDEX`. The index refers to the index number shown in the displayed itinerary 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. + +
+ +**:information_source: Notes:**
-* 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` +* If you are editing the budget, please ensure that it is sufficient to cover the cost of all the planned activities. An error would be shown otherwise.
+* If you reduce the duration of an itinerary, days will be removed from the back, and any activities that were scheduled on a removed day would be returned to the wishlist.
+ +
Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +* `edit 1 du/15 sd/2023-11-03` Edits the duration and start date of the first itinerary to be `15` and `2023-11-03` respectively. +* `edit 2 c/India` Edits the country of the second itinerary to be `India`. + +Running `edit 1 du/15 sd/2023-11-03` -### Deleting a person : `delete` +![Edit Command](images/edit-itinerary.png) -Deletes the specified person from the address book. +### Deleting an itinerary : `delete` + +Deletes the specified itinerary from Waddle. Format: `delete INDEX` -* Deletes the person at the specified `INDEX`. -* The index refers to the index number shown in the displayed person list. +* Deletes the itinerary at the specified `INDEX`. The index refers to the index number shown in the displayed list of itineraries. * The index **must be a positive integer** 1, 2, 3, …​ Examples: -* `list` followed by `delete 2` deletes the 2nd person in the address book. -* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command. +* `list` followed by `delete 2` deletes the 2nd itinerary in Waddle. +* `find Japan` followed by `delete 1` deletes the 1st itinerary in the results of the `find` command. + +Running `delete 2` + +![Delete Command](images/delete-itinerary.png) -### Clearing all entries : `clear` +### Clearing itineraries : `clear` -Clears all entries from the address book. +Deletes all itineraries in Waddle. Format: `clear` -### Exiting the program : `exit` +![Clear command](images/clear.png) +### Selecting an itinerary : `select` -Exits the program. +Enters the [activity planning page](#the-activity-planning-page) for the selected itinerary. -Format: `exit` +Format: `select INDEX` -### Saving the data +* Selects the itinerary at the specified `INDEX`. The index refers to the index number shown in the displayed list of itineraries. +* The index **must be a positive integer** 1, 2, 3, ...​ -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +Examples: +* `select 1` -### Editing the data file +
+ +### The activity-planning page + +Once you have created your itinerary, you can now start adding and planning the activities that you want to complete in +your time there! The activity-planning page of an itinerary displays the list of activities that you have added to your +itinerary. Activities on the wishlist that you have not given a start time and date yet will appear at the very top, +in order of _priority_ (as indicated by the number of stars). Activities that you have scheduled will instead appear in +the list of days below the wishlist, sorted by _start time_. + +The index of scheduled activities are in the format `DAY.ACTIVITY_NUMBER`. Some examples: +* The _first_ activity of the _first_ day will have index `1.1` +* The _fifth_ activity of the _third_ day will have index `3.5` +* The _second_ activity of the wishlist will have index `2` + +Here's an example of how your activity-planning page might look like: +![activity-planning page](images/ug-beg-tut/planning-page-ui.png) + +[Commands exclusive to the activity-planning page](#commands-on-the-activity-planning-page) can help you: +* [Add](#adding-an-activity--add) new activities +* [Edit](#editing-the-details-of-an-activity--edit) or [Delete](#deleting-an-activity--delete) existing activities +* [View](#viewing-vacant-timeslots--free) the vacant time slots on your itinerary +* [Schedule](#scheduling-an-activity--plan) or [Unschedule](#unscheduling-an-activity--unplan) activities +* [Copy](#copying-to-clipboard--copy) your itinerary to your clipboard. +* [Export](#exporting-as-pdf-file--pdf) your itinerary as a pdf file + +Using the [`home` command](#returning-to-home-page--home) will bring you to the [home page](#the-home-page) of the selected itinerary. + +
+ +### Commands on the activity-planning page + +### Adding an activity : `add` + +Adds an activity to your wishlist (without a scheduled day and time). + +Format: `add d/DESCRIPTION du/DURATION [p/PRIORITY] [c/COST] ` -AddressBook data are saved as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file. +* `DESCRIPTION` cannot be blank and must only contain alphanumeric characters, spaces and these following special characters: `()&!':.,-`. +* `DURATION` is the time taken for the activity in _minutes_. The duration must be more than 0 minutes and shorter than 1440 minutes (1 day). + - e.g. `du/100` is 100 minutes (or 1 hour and 40 minutes). + +* `PRIORITY` is used to rank the importance of an activity. It must be a number from 1 to 5, with 1 being the highest priority. + +* `COST` is the cost of the activity in dollars, or dollars and cents, and must be between 0 and 1,000,000. + - e.g. `c/100.20` is $100.20. + +* You cannot add activities with the same description as an existing activity in the activity list. + +
+ +**:information_source: Note:**
+* If no `PRIORITY` or `COST` is provided, Waddle assigns them a default value as follows: + * The default `PRIORITY` is 1.
+ * The default `COST` is $0.
+* The cost input should only contain numbers and one decimal point.
+ - Examples of invalid input: `c/1,000,000` +* If more than 2 decimal places are provided for the cost, Waddle rounds it up to 2 decimal places.
+ - e.g. `b/1000.505` will be reflected as $1,000.51. + +
+ +Examples: +* `add d/Go to the Louvre p/2 du/1` +* `add d/Skiing du/14 c/100` + +Running `add d/Go to the Louvre p/2 du/1` + +![Add Activity](images/add-item.png) + +### Editing the details of an activity : `edit` + +Edits an existing activity in your activity list. + +Format: `edit INDEX [d/DESCRIPTION] [p/PRIORITY] [c/COST] [du/DURATION]` + +* Edits the activity at the specified `INDEX`. The index refers to the index number displayed in either the wishlist, or the scheduled activity in the list of days. +* The index of a scheduled activity refers to the index number displayed in the list of days, the format being `DAY_NUMBER`.`ACTIVITY_INDEX`. +* At least one of the optional fields must be provided. +* Existing values will be updated to the input values. + +
+ +**:information_source: Note:**
+ +* If you are editing the cost, please ensure that the new cost stays within the budget of the itinerary. An error would be shown otherwise.
+ +
+ +Examples: +* `edit 1 d/Go skiing` would edit the description of the 1st activity in the unscheduled activity list to be `Go skiing`. +* `edit 2.2 p/3 c/100` would edit the priority and cost of the 2nd activity in the Day 2 list to be `3` and `100` respectively. + +Running `edit 1 d/Go skiing` + +![Edit Activity](images/edit-item.png) + + +### Deleting an activity : `delete` + +Deletes an existing activity in your activity list. + +Format: `delete INDEX` + +* Deletes the activity at the specified `INDEX`. The index refers to the index number displayed in either the unscheduled activity list, or the scheduled activity in the list of days. + +Examples: +* `delete 1` would delete the 1st activity in the unscheduled activity list. +* `delete 2.1` would delete the 1st activity in the Day 2 activity list. + +Running `delete 1` + +![Delete Activity](images/delete-item.png) + +### Viewing vacant timeslots : `free` + +Displays the vacant timeslots available for you to schedule activities. + +Format: `free` + +### Scheduling an activity : `plan` + +Schedules an activity from your wishlist. + +Format: `plan INDEX d/DAY_NUMBER st/START_TIME` + +* Schedules the activity at the specified `INDEX`. The index refers to the index number displayed in the wishlist. +* When an activity is scheduled, the cost of the activity is automatically deducted from the budget of the itinerary. +* `DAY_NUMBER` must be an integer from 1 to the duration (in days) of the trip. +* `START_TIME` should be given in the format `hh:mm`, or `hh:mm:ss` where `hh` is the hour in 24-hour format, `mm` is the minute, and `ss` is the seconds. +* The end time of the activity is automatically calculated by adding the `DURATION` of the activity to the `START_TIME`. +* You can only add an activity if there is no clash in timing between the start and end time of the new activity, and the start and end time of any existing scheduled activity. + +
+ +**:information_source: Note:**
+ +* When scheduling an activity, please ensure that the activity stays within the budget of the itinerary. An error would be shown otherwise.
+ +
+ +Examples: +* `plan 2 d/3 st/12:00` would add the 2nd activity in the wishlist to Day 3, starting at 12pm. +* `plan 1 d/1 st/14:50:10` would add the 1st activity in the wishlist to Day 1, starting at 14:50pm, 10 seconds in. + +Running `plan 1 d/1 st/11:00` + +![Plan Activity](images/plan-item.png) + +### Unscheduling an activity : `unplan` + +Takes an activity from the list of days and puts it back into the wishlist. + +Format: `unplan INDEX` + +* Unschedules the activity at the specified `INDEX` as displayed on the day lists. +* When an activity is unscheduled, its cost is automatically added back to the budget of the itinerary. + +Examples: +* `unplan 2.1` would unschedule the 1st activity in the Day 2 activity list. +* `unplan 4.5` would unschedule the 5th activity in the Day 4 activity list. + +Running `unplan 1.1` + +![Unplan Activity](images/unplan-item.png) + +### Copying to clipboard : `copy` + +Copies your itinerary in a text format onto your device's clipboard so that you can paste it anywhere. + +Format: `copy` + +Here's an example of how the copied text would look like:
+![exportCopy](images/exportCopy.png) + +
+ +**:information_source: Note:**
+ +* The generated text includes all days within the itinerary, even if there are no activities planned for the day. +* The generated text does not include the activities in the wishlist. For activities to be reflected in the generated text, they must be planned.
+ +
+ +### Exporting as PDF file : `pdf` + +Exports your itinerary as a PDF file. The file can be found under the "Waddle" folder in your "Documents" folder. + +[//]: # (TODO: include screenshots of where to find it for windows and mac, maybe linux but idk how) + +Format: `pdf` + +Here's an example of how the generated PDF would look like:
+![exportPDF](images/exportPDF.png) + +
+ +**:information_source: Note:**
+ +* The generated PDF file does not contain the activities in your wishlist. For these activities to be reflected in the generated PDF file, you must plan them.
+* PDF can only display up to 35 characters for itinerary description and 50 characters for activity description. -
:exclamation: **Caution:** -If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run.
-### Archiving data files `[coming in v2.0]` +### Returning to home page : `home` + +Returns you to the [home page](#the-home-page). + +Format: `home` + +
+ +### Advanced +This section contains miscellaneous additional information about Waddle's user data. + +### Saving the data -_Details coming soon ..._ +Waddle data is saved in your hard disk automatically upon any change in the data. There is no need for you to save manually. + +### Editing the data file + +Waddle data is saved as a JSON file `[JAR file location]/data/waddle.json`. If you know how JSON works, you are welcome to update data directly by editing that data file. + +
:exclamation: Caution: +If your changes to the data file makes its format invalid, Waddle will discard all data and start with an empty data file at the next run. Please perform a backup before manually editing data. +
-------------------------------------------------------------------------------------------------------------------- +
+ ## 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**: 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 Waddle home folder. -------------------------------------------------------------------------------------------------------------------- +
+ ## Command summary -Action | Format, Examples ---------|------------------ -**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague` -**Clear** | `clear` -**Delete** | `delete INDEX`
e.g., `delete 3` -**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…​`
e.g.,`edit 2 n/James Lee e/jameslee@example.com` -**Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` -**List** | `list` -**Help** | `help` +### Home page commands + +| Action | Format, Examples | +|------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [**Help**](#viewing-help--help) | `help` | +| [**Add Itinerary**](#creating-a-new-itinerary--add) | `add d/DESCRIPTION sd/START DATE du/DURATION [c/COUNTRY] [p/NUMBER OF WADDLERS] [b/BUDGET]`
e.g., `add d/Germanyyyy sd/2025-05-10 du/14 c/Germany p/4 b/7500` | +| [**List Itineraries**](#listing-all-itineraries--list) | `list` | +| [**Find Itinerary**](#locating-itineraries-by-description--find) | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find India Trip` | +| [**Edit Itinerary**](#editing-the-details-of-an-itinerary--edit) | `edit INDEX [n/NAME] [c/COUNTRY] [du/DURATION] [s/START DATE] [p/NUMBER OF WADDLERS] [b/BUDGET]`
e.g.,`edit 1 du/15 sd/2025-04-10` | +| [**Delete Itinerary**](#deleting-an-itinerary--delete) | `delete INDEX`
e.g., `delete 3` | +| [**Clear Itineraries**](#clearing-itineraries--clear) | `clear` | +| [**Select Itinerary**](#selecting-an-itinerary--select) | `select INDEX`
e.g., `select 3` | +| [**Exit**](#exiting-waddle--exit) | `exit` | + +
+ +### Activity-planning page commands + +| Action | Format, Examples | +|----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------| +| [**Help**](#viewing-help--help) | `help` | +| [**Add Activity**](#adding-an-activity--add) | `add d/DESCRIPTION [p/PRIORITY] [c/COST] [du/DURATION]`
e.g.,`add d/Visit Taj Mahal p/5 c/40 du/180` | +| [**Edit Activity**](#editing-the-details-of-an-activity--edit) | `edit INDEX [d/DESCRIPTION] [p/PRIORITY] [c/COST] [du/DURATION]`
e.g., `edit 4.1 c/50` | +| [**Delete Activity**](#deleting-an-activity--delete) | `delete INDEX`
e.g., `delete 3` | +| [**View Vacant Timeslots**](#viewing-vacant-timeslots--free) | `free` | +| [**Schedule Activity**](#scheduling-an-activity--plan) | `plan INDEX d/DAY NUMBER st/START TIME`
e.g., `plan 1 d/4 st/12:00` | +| [**Unschedule Activity**](#unscheduling-an-activity--unplan) | `unplan INDEX`
e.g., `unplan 3.2` | +| [**Copy to clipboard**](#copying-to-clipboard--copy) | `copy` | +| [**Export to PDF**](#exporting-as-pdf-file--pdf) | `pdf` | +| [**Return to Home Page**](#returning-to-home-page--home) | `home` | +| [**Exit**](#exiting-waddle--exit) | `exit` | + +-------------------------------------------------------------------------------------------------------------------- + +
+ +## Glossary + +| Term | Meaning | +|------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Command Line Interface (CLI)** | A command line interface is a text-based user interface that allows users to input commands in the form of text | +| **Graphical User Interface (GUI)** | A graphical user interface is a graphics-based user interface that allows users to interact with visual elements like buttons and lists | +| **Parameter** | The details that you have to provide waddle which differs for each command. For example, the `plan` command requires the `day` and `start time` parameters | +| **Prefix** | The characters that precede the parameters you enter. For example, `n/` for the name parameter | diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..80d1bfd57f9 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "Waddle" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2223S1-CS2103T-W11-4/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..e680bdd55c7 100644 --- a/docs/_sass/minima/_base.scss +++ b/docs/_sass/minima/_base.scss @@ -288,7 +288,7 @@ table { text-align: center; } .site-header:before { - content: "AB-3"; + content: "Waddle"; font-size: 32px; } } diff --git a/docs/diagrams/ArchitectureDiagram.puml b/docs/diagrams/ArchitectureDiagram.puml index 4c5cf58212e..465a0007f7b 100644 --- a/docs/diagrams/ArchitectureDiagram.puml +++ b/docs/diagrams/ArchitectureDiagram.puml @@ -9,6 +9,7 @@ Package " "<>{ Class Logic LOGIC_COLOR Class Storage STORAGE_COLOR Class Model MODEL_COLOR + Class StageManager STAGE_COLOR Class Main #grey Class Commons LOGIC_COLOR_T2 } diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index ef81d18c337..35f7766d479 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -4,6 +4,7 @@ Actor User as user USER_COLOR Participant ":UI" as ui UI_COLOR Participant ":Logic" as logic LOGIC_COLOR +Participant ":Stage" as stage STAGE_COLOR Participant ":Model" as model MODEL_COLOR Participant ":Storage" as storage STORAGE_COLOR @@ -13,13 +14,33 @@ activate ui UI_COLOR ui -[UI_COLOR]> logic : execute("delete 1") activate logic LOGIC_COLOR -logic -[LOGIC_COLOR]> model : deletePerson(p) -activate model MODEL_COLOR +logic -[LOGIC_COLOR]> stage : getInstance() +activate stage STAGE_COLOR -model -[MODEL_COLOR]-> logic -deactivate model +stage -[STAGE_COLOR]-> logic +deactivate stage -logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook) +logic -[LOGIC_COLOR]> stage : instance.getCurrentStage() +activate stage STAGE_COLOR + +stage -[STAGE_COLOR]-> logic +deactivate stage + +alt Stages.HOME + logic -[LOGIC_COLOR]> model : deleteItinerary(i) + activate model MODEL_COLOR + + model -[MODEL_COLOR]-> logic + deactivate model +else Stages.PLAN + logic -[LOGIC_COLOR]> model : deleteItem(i) + activate model MODEL_COLOR + + model -[MODEL_COLOR]-> logic + deactivate model +end + +logic -[LOGIC_COLOR]> storage : saveWaddle(waddle) activate storage STORAGE_COLOR storage -[STORAGE_COLOR]> storage : Save to file diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 1dc2311b245..7cfbf0e2c90 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -3,7 +3,7 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":WaddleParser" as WaddleParser LOGIC_COLOR participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR participant ":CommandResult" as CommandResult LOGIC_COLOR @@ -16,17 +16,17 @@ end box [-> LogicManager : execute("delete 1") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") -activate AddressBookParser +LogicManager -> WaddleParser : parseCommand("delete 1") +activate WaddleParser create DeleteCommandParser -AddressBookParser -> DeleteCommandParser +WaddleParser -> DeleteCommandParser activate DeleteCommandParser -DeleteCommandParser --> AddressBookParser +DeleteCommandParser --> WaddleParser deactivate DeleteCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") +WaddleParser -> DeleteCommandParser : parse("1") activate DeleteCommandParser create DeleteCommand @@ -36,19 +36,19 @@ activate DeleteCommand DeleteCommand --> DeleteCommandParser : d deactivate DeleteCommand -DeleteCommandParser --> AddressBookParser : d +DeleteCommandParser --> WaddleParser : d deactivate DeleteCommandParser 'Hidden arrow to position the destroy marker below the end of the activation bar. -DeleteCommandParser -[hidden]-> AddressBookParser +DeleteCommandParser -[hidden]-> WaddleParser destroy DeleteCommandParser -AddressBookParser --> LogicManager : d -deactivate AddressBookParser +WaddleParser --> LogicManager : d +deactivate WaddleParser LogicManager -> DeleteCommand : execute() activate DeleteCommand -DeleteCommand -> Model : deletePerson(1) +DeleteCommand -> Model : deleteItinerary(1) activate Model Model --> DeleteCommand diff --git a/docs/diagrams/ItineraryClassDiagram.puml b/docs/diagrams/ItineraryClassDiagram.puml new file mode 100644 index 00000000000..6a6e2aaa423 --- /dev/null +++ b/docs/diagrams/ItineraryClassDiagram.puml @@ -0,0 +1,54 @@ +@startuml +!include style.puml +skinparam classAttributeIconSize 0 +skinparam arrowThickness 1.1 +skinparam arrowColor black +skinparam classBackgroundColor white +skinparam Class { + FontColor black + BorderThickness 1 + BorderColor black + StereotypeFontColor black + FontName Arial +} +show footbox +show members + + +Package Itinerary <>{ +Class Itinerary { + -name: Name + -country: Country + -startDate: Date + -duration: Duration + -people: People + -budget: Budget + -priorityComparator: Comparator + + +planItem(Index, DayNumber, LocalTime): Item + +unplanItem(MultiIndex): Item +} +Class Item { + -description: String + -priority: Priority + -cost: Cost + -duration: Duration + -startTime: LocalTime + -endTime: LocalTime + + +getStartTime(): LocalTime + +getEndTime(): LocalTime +} +Class Day { + -dayNumber: int + -startTimeComparator: Comparator + + +addItem(Item) + +removeItem(Index): Item + -getConflictingItems(Item): ArrayList +} + +Itinerary *-down-> "~* unscheduledItemList"Item +Itinerary *-down-> "~* days" Day +Day -left-> "~* " Item :itemList +@enduml diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index d4193173e18..8ac8ae1f85e 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR package Logic { -Class AddressBookParser +Class WaddleParser Class XYZCommand Class CommandResult Class "{abstract}\nCommand" as Command @@ -27,8 +27,8 @@ Class HiddenOutside #FFFFFF HiddenOutside ..> Logic LogicManager .right.|> Logic -LogicManager -right->"1" AddressBookParser -AddressBookParser ..> XYZCommand : creates > +LogicManager -right->"1" WaddleParser +WaddleParser ..> XYZCommand : creates > XYZCommand -up-|> Command LogicManager .left.> Command : executes > diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 4439108973a..7235fd68923 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -5,19 +5,19 @@ skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR Package Model <>{ -Class "<>\nReadOnlyAddressBook" as ReadOnlyAddressBook +Class "<>\nReadOnlyWaddle" as ReadOnlyWaddle Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs Class "<>\nModel" as Model -Class AddressBook +Class Waddle Class ModelManager Class UserPrefs -Class UniquePersonList -Class Person -Class Address -Class Email -Class Name -Class Phone +Class UniqueItineraryList +Class Itinerary +Class People +Class Duration +Class Description +Class Country Class Tag } @@ -25,26 +25,25 @@ Class Tag Class HiddenOutside #FFFFFF HiddenOutside ..> Model -AddressBook .up.|> ReadOnlyAddressBook +Waddle .up.|> ReadOnlyWaddle ModelManager .up.|> Model Model .right.> ReadOnlyUserPrefs -Model .left.> ReadOnlyAddressBook -ModelManager -left-> "1" AddressBook +Model .left.> ReadOnlyWaddle +ModelManager -left-> "1" Waddle ModelManager -right-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs -AddressBook *--> "1" UniquePersonList -UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag +Waddle *--> "1" UniqueItineraryList +UniqueItineraryList --> "~* all" Itinerary +Itinerary *--> Description +Itinerary *--> Country +Itinerary *--> Duration +Itinerary *--> People -Name -[hidden]right-> Phone -Phone -[hidden]right-> Address -Address -[hidden]right-> Email +Description -[hidden]right-> Country +Country -[hidden]right-> People +People -[hidden]right-> Duration -ModelManager -->"~* filtered" Person +ModelManager -->"~* filtered" Itinerary @enduml diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml index 0c7424de6e0..c2b507e2860 100644 --- a/docs/diagrams/ParserClasses.puml +++ b/docs/diagrams/ParserClasses.puml @@ -9,7 +9,7 @@ Class XYZCommand package "Parser classes"{ Class "<>\nParser" as Parser -Class AddressBookParser +Class WaddleParser Class XYZCommandParser Class CliSyntax Class ParserUtil @@ -19,12 +19,12 @@ Class Prefix } Class HiddenOutside #FFFFFF -HiddenOutside ..> AddressBookParser +HiddenOutside ..> WaddleParser -AddressBookParser .down.> XYZCommandParser: creates > +WaddleParser .down.> XYZCommandParser: creates > XYZCommandParser ..> XYZCommand : creates > -AddressBookParser ..> Command : returns > +WaddleParser ..> Command : returns > XYZCommandParser .up.|> Parser XYZCommandParser ..> ArgumentMultimap XYZCommandParser ..> ArgumentTokenizer diff --git a/docs/diagrams/PlanSequenceDiagram.puml b/docs/diagrams/PlanSequenceDiagram.puml new file mode 100644 index 00000000000..1dbe2dfecfd --- /dev/null +++ b/docs/diagrams/PlanSequenceDiagram.puml @@ -0,0 +1,51 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":WaddleParser" as WaddleParser LOGIC_COLOR +participant ":PlanCommandParser" as PlanCommandParser LOGIC_COLOR +participant "d:PlanCommand" as PlanCommand LOGIC_COLOR +end box + +[-> LogicManager : execute("plan 1 d/1 t/12:00") +activate LogicManager + +LogicManager -> WaddleParser : parseCommand("plan 1 d/1 t/12:00") +activate WaddleParser + +create PlanCommandParser +WaddleParser -> PlanCommandParser +activate PlanCommandParser + +PlanCommandParser --> WaddleParser +deactivate PlanCommandParser + +WaddleParser -> PlanCommandParser : parse("1 d/1 t/12:00") +activate PlanCommandParser + +create PlanCommand +PlanCommandParser -> PlanCommand +activate PlanCommand + +PlanCommand --> PlanCommandParser : d +deactivate PlanCommand + +PlanCommandParser --> WaddleParser : d +deactivate PlanCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +PlanCommandParser -[hidden]-> WaddleParser +destroy PlanCommandParser + +WaddleParser --> LogicManager : d +deactivate WaddleParser + +LogicManager -> PlanCommand : execute() +activate PlanCommand + +PlanCommand --> LogicManager : result +deactivate PlanCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/PlanSequenceDiagram2.puml b/docs/diagrams/PlanSequenceDiagram2.puml new file mode 100644 index 00000000000..d2e7dd60b8d --- /dev/null +++ b/docs/diagrams/PlanSequenceDiagram2.puml @@ -0,0 +1,57 @@ +@startuml +!include style.puml + +box LOGIC_COLOR_T1 +participant "d:PlanCommand" as PlanCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box StageManger STAGE_COLOR_T1 +participant ":StageManager" as StageManager STAGE_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Itinerary" as Itinerary MODEL_COLOR +participant ":Day" as Day MODEL_COLOR +end box + + +activate PlanCommand + +PlanCommand -> StageManager : getInstance() +activate StageManager + +StageManager --> PlanCommand +deactivate StageManager + +PlanCommand -> StageManager : instance.getSelectedItinerary() +activate StageManager + +StageManager --> PlanCommand +deactivate StageManager + +PlanCommand -> Itinerary : planItem(itemIndex, dayNumber, startTime) +activate Itinerary + +Itinerary -> Day : addItem(item) +activate Day + +Day -> Day : getConflictingItems(item) +activate Day + +Day --> Day +deactivate Day + +Day --> Itinerary +deactivate Day + +Itinerary --> PlanCommand +deactivate Itinerary + +create CommandResult +PlanCommand -> CommandResult +activate CommandResult + +CommandResult --> PlanCommand +deactivate CommandResult +@enduml diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 760305e0e58..eee71556fc7 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -15,10 +15,10 @@ Class "<>\nStorage" as Storage Class StorageManager package "AddressBook Storage" #F4F6F6{ -Class "<>\nAddressBookStorage" as AddressBookStorage -Class JsonAddressBookStorage -Class JsonSerializableAddressBook -Class JsonAdaptedPerson +Class "<>\nWaddleStorage" as WaddleStorage +Class JsonWaddleStorage +Class JsonSerializableWaddle +Class JsonAdaptedItinerary Class JsonAdaptedTag } @@ -29,15 +29,15 @@ HiddenOutside ..> Storage StorageManager .up.|> Storage StorageManager -up-> "1" UserPrefsStorage -StorageManager -up-> "1" AddressBookStorage +StorageManager -up-> "1" WaddleStorage Storage -left-|> UserPrefsStorage -Storage -right-|> AddressBookStorage +Storage -right-|> WaddleStorage JsonUserPrefsStorage .up.|> UserPrefsStorage -JsonAddressBookStorage .up.|> AddressBookStorage -JsonAddressBookStorage ..> JsonSerializableAddressBook -JsonSerializableAddressBook --> "*" JsonAdaptedPerson -JsonAdaptedPerson --> "*" JsonAdaptedTag +JsonWaddleStorage .up.|> WaddleStorage +JsonWaddleStorage ..> JsonSerializableWaddle +JsonSerializableWaddle --> "*" JsonAdaptedItinerary +JsonAdaptedItinerary --> "*" JsonAdaptedTag @enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 95473d5aa19..cba7dd37335 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -11,8 +11,10 @@ Class UiManager Class MainWindow Class HelpWindow Class ResultDisplay -Class PersonListPanel -Class PersonCard +Class ItineraryListPanel +Class ItemGroupListPanel +Class ItineraryCard +Class ItemGroupCard Class StatusBarFooter Class CommandBox } @@ -32,26 +34,31 @@ UiManager .left.|> Ui UiManager -down-> "1" MainWindow MainWindow *-down-> "1" CommandBox MainWindow *-down-> "1" ResultDisplay -MainWindow *-down-> "1" PersonListPanel +MainWindow *-down-> "0..1" ItineraryListPanel +MainWindow *-down-> "0..1" ItemGroupListPanel MainWindow *-down-> "1" StatusBarFooter MainWindow --> "0..1" HelpWindow -PersonListPanel -down-> "*" PersonCard +ItineraryListPanel -down-> "*" ItineraryCard +ItemGroupListPanel -down-> "*" ItemGroupCard MainWindow -left-|> UiPart ResultDisplay --|> UiPart CommandBox --|> UiPart -PersonListPanel --|> UiPart -PersonCard --|> UiPart +ItineraryListPanel --|> UiPart +ItineraryCard --|> UiPart +ItemGroupListPanel --|> UiPart +ItemGroupCard --|> UiPart StatusBarFooter --|> UiPart HelpWindow --|> UiPart -PersonCard ..> Model +ItineraryCard ..> Model +ItemGroupCard ..> Model UiManager -right-> Logic MainWindow -left-> Logic -PersonListPanel -[hidden]left- HelpWindow +ItineraryListPanel -[hidden]left- HelpWindow HelpWindow -[hidden]left- CommandBox CommandBox -[hidden]left- ResultDisplay ResultDisplay -[hidden]left- StatusBarFooter diff --git a/docs/diagrams/style.puml b/docs/diagrams/style.puml index fad8b0adeaa..c463b858a13 100644 --- a/docs/diagrams/style.puml +++ b/docs/diagrams/style.puml @@ -31,6 +31,9 @@ !define STORAGE_COLOR_T3 #806600 !define STORAGE_COLOR_T2 #544400 +!define STAGE_COLOR #ff9000 +!define STAGE_COLOR_T1 #ffc37a + !define USER_COLOR #000000 skinparam BackgroundColor #FFFFFFF diff --git a/docs/diagrams/tracing/LogicSequenceDiagram.puml b/docs/diagrams/tracing/LogicSequenceDiagram.puml index fdcbe1c0ccc..e5f85a93e7c 100644 --- a/docs/diagrams/tracing/LogicSequenceDiagram.puml +++ b/docs/diagrams/tracing/LogicSequenceDiagram.puml @@ -13,7 +13,7 @@ create ecp abp -> ecp abp -> ecp ++: parse(arguments) create ec -ecp -> ec ++: index, editPersonDescriptor +ecp -> ec ++: index, editItineraryDescriptor ec --> ecp -- ecp --> abp --: command abp --> logic --: command diff --git a/docs/images/ArchitectureDiagram.png b/docs/images/ArchitectureDiagram.png index 86c60246ccb..40281fad976 100644 Binary files a/docs/images/ArchitectureDiagram.png and b/docs/images/ArchitectureDiagram.png differ diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 2f1346869d0..a438cc26278 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index fa327b39618..74da1091327 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/ItineraryClassDiagram.png b/docs/images/ItineraryClassDiagram.png new file mode 100644 index 00000000000..852f1f4ed3a Binary files /dev/null and b/docs/images/ItineraryClassDiagram.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index 9e9ba9f79e5..374ed35f695 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 04070af60d8..1c689bd8930 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png index e7b4c8880cd..0a57aefbb21 100644 Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ diff --git a/docs/images/PlanSequenceDiagram.png b/docs/images/PlanSequenceDiagram.png new file mode 100644 index 00000000000..0cd8c295ecf Binary files /dev/null and b/docs/images/PlanSequenceDiagram.png differ diff --git a/docs/images/PlanSequenceDiagram2.png b/docs/images/PlanSequenceDiagram2.png new file mode 100644 index 00000000000..38df28e9f4a Binary files /dev/null and b/docs/images/PlanSequenceDiagram2.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 2533a5c1af0..7d178193d63 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png deleted file mode 100644 index 5bd77847aa2..00000000000 Binary files a/docs/images/Ui.png and /dev/null differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png index 785e04dbab4..c06439c1baf 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/add-item.png b/docs/images/add-item.png new file mode 100644 index 00000000000..092ba227b61 Binary files /dev/null and b/docs/images/add-item.png differ diff --git a/docs/images/add-itinerary.png b/docs/images/add-itinerary.png new file mode 100644 index 00000000000..f39e79eb964 Binary files /dev/null and b/docs/images/add-itinerary.png differ diff --git a/docs/images/addActivityCommand.png b/docs/images/addActivityCommand.png new file mode 100644 index 00000000000..404b235070c Binary files /dev/null and b/docs/images/addActivityCommand.png differ diff --git a/docs/images/addCommand.png b/docs/images/addCommand.png new file mode 100644 index 00000000000..60b4f047831 Binary files /dev/null and b/docs/images/addCommand.png differ diff --git a/docs/images/cfsy.png b/docs/images/cfsy.png new file mode 100644 index 00000000000..a7607771d45 Binary files /dev/null and b/docs/images/cfsy.png differ diff --git a/docs/images/ciaoosuuu.png b/docs/images/ciaoosuuu.png new file mode 100644 index 00000000000..a2c4d507959 Binary files /dev/null and b/docs/images/ciaoosuuu.png differ diff --git a/docs/images/clear.png b/docs/images/clear.png new file mode 100644 index 00000000000..4a971b03cfe Binary files /dev/null and b/docs/images/clear.png differ diff --git a/docs/images/clearCommand.png b/docs/images/clearCommand.png new file mode 100644 index 00000000000..765d399fb85 Binary files /dev/null and b/docs/images/clearCommand.png differ diff --git a/docs/images/delete-item.png b/docs/images/delete-item.png new file mode 100644 index 00000000000..72ef906bbce Binary files /dev/null and b/docs/images/delete-item.png differ diff --git a/docs/images/delete-itinerary.png b/docs/images/delete-itinerary.png new file mode 100644 index 00000000000..b7db5241184 Binary files /dev/null and b/docs/images/delete-itinerary.png differ diff --git a/docs/images/deleteActivityCommand.png b/docs/images/deleteActivityCommand.png new file mode 100644 index 00000000000..036d78725ca Binary files /dev/null and b/docs/images/deleteActivityCommand.png differ diff --git a/docs/images/deleteCommand.png b/docs/images/deleteCommand.png new file mode 100644 index 00000000000..649dbadaab0 Binary files /dev/null and b/docs/images/deleteCommand.png differ diff --git a/docs/images/edit-item.png b/docs/images/edit-item.png new file mode 100644 index 00000000000..4c9491a474e Binary files /dev/null and b/docs/images/edit-item.png differ diff --git a/docs/images/edit-itinerary.png b/docs/images/edit-itinerary.png new file mode 100644 index 00000000000..ba4edd7013a Binary files /dev/null and b/docs/images/edit-itinerary.png differ diff --git a/docs/images/editActivityCommand.png b/docs/images/editActivityCommand.png new file mode 100644 index 00000000000..3a52f6a1c84 Binary files /dev/null and b/docs/images/editActivityCommand.png differ diff --git a/docs/images/editCommand.png b/docs/images/editCommand.png new file mode 100644 index 00000000000..ea76a4f88d2 Binary files /dev/null and b/docs/images/editCommand.png differ diff --git a/docs/images/exportCopy.png b/docs/images/exportCopy.png new file mode 100644 index 00000000000..66f5a52fae9 Binary files /dev/null and b/docs/images/exportCopy.png differ diff --git a/docs/images/exportPDF.png b/docs/images/exportPDF.png new file mode 100644 index 00000000000..31ce3cad4d0 Binary files /dev/null and b/docs/images/exportPDF.png differ diff --git a/docs/images/find.png b/docs/images/find.png new file mode 100644 index 00000000000..63c263c3130 Binary files /dev/null and b/docs/images/find.png differ diff --git a/docs/images/findAlexDavidResult.png b/docs/images/findAlexDavidResult.png deleted file mode 100644 index 235da1c273e..00000000000 Binary files a/docs/images/findAlexDavidResult.png and /dev/null differ diff --git a/docs/images/findCommand.png b/docs/images/findCommand.png new file mode 100644 index 00000000000..0c5657a6bae Binary files /dev/null and b/docs/images/findCommand.png differ diff --git a/docs/images/findTripResult.png b/docs/images/findTripResult.png new file mode 100644 index 00000000000..ebb6d433641 Binary files /dev/null and b/docs/images/findTripResult.png differ diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png index b1f70470137..cd9ca2636d5 100644 Binary files a/docs/images/helpMessage.png and b/docs/images/helpMessage.png differ diff --git a/docs/images/itemPlanningUi.png b/docs/images/itemPlanningUi.png new file mode 100644 index 00000000000..3bb26dd8e70 Binary files /dev/null and b/docs/images/itemPlanningUi.png differ diff --git a/docs/images/list.png b/docs/images/list.png new file mode 100644 index 00000000000..dbd0540f7b1 Binary files /dev/null and b/docs/images/list.png differ diff --git a/docs/images/listCommand.png b/docs/images/listCommand.png new file mode 100644 index 00000000000..d70e4ba55be Binary files /dev/null and b/docs/images/listCommand.png differ diff --git a/docs/images/mainPage.png b/docs/images/mainPage.png new file mode 100644 index 00000000000..631563e83ce Binary files /dev/null and b/docs/images/mainPage.png differ diff --git a/docs/images/ningtan11.png b/docs/images/ningtan11.png new file mode 100644 index 00000000000..1ce7ce16dc8 Binary files /dev/null and b/docs/images/ningtan11.png differ diff --git a/docs/images/pewggls.png b/docs/images/pewggls.png new file mode 100644 index 00000000000..fd33f32f1be Binary files /dev/null and b/docs/images/pewggls.png differ diff --git a/docs/images/plan-item.png b/docs/images/plan-item.png new file mode 100644 index 00000000000..cc86338b746 Binary files /dev/null and b/docs/images/plan-item.png differ diff --git a/docs/images/planCommand.png b/docs/images/planCommand.png new file mode 100644 index 00000000000..51cac7ac075 Binary files /dev/null and b/docs/images/planCommand.png differ diff --git a/docs/images/select.png b/docs/images/select.png new file mode 100644 index 00000000000..70db425b806 Binary files /dev/null and b/docs/images/select.png differ diff --git a/docs/images/seox123.png b/docs/images/seox123.png new file mode 100644 index 00000000000..5b45ef4a47e Binary files /dev/null and b/docs/images/seox123.png differ diff --git a/docs/images/ug-beg-tut/add-item.png b/docs/images/ug-beg-tut/add-item.png new file mode 100644 index 00000000000..f1ff638977f Binary files /dev/null and b/docs/images/ug-beg-tut/add-item.png differ diff --git a/docs/images/ug-beg-tut/add-itinerary.png b/docs/images/ug-beg-tut/add-itinerary.png new file mode 100644 index 00000000000..3f2f3b9906b Binary files /dev/null and b/docs/images/ug-beg-tut/add-itinerary.png differ diff --git a/docs/images/ug-beg-tut/copy.png b/docs/images/ug-beg-tut/copy.png new file mode 100644 index 00000000000..bd31b7a317b Binary files /dev/null and b/docs/images/ug-beg-tut/copy.png differ diff --git a/docs/images/ug-beg-tut/main-page-sample-ui.png b/docs/images/ug-beg-tut/main-page-sample-ui.png new file mode 100644 index 00000000000..5780e8295dc Binary files /dev/null and b/docs/images/ug-beg-tut/main-page-sample-ui.png differ diff --git a/docs/images/ug-beg-tut/pdf.png b/docs/images/ug-beg-tut/pdf.png new file mode 100644 index 00000000000..eb7b1ffb53a Binary files /dev/null and b/docs/images/ug-beg-tut/pdf.png differ diff --git a/docs/images/ug-beg-tut/plan-item.png b/docs/images/ug-beg-tut/plan-item.png new file mode 100644 index 00000000000..4aa07075b84 Binary files /dev/null and b/docs/images/ug-beg-tut/plan-item.png differ diff --git a/docs/images/ug-beg-tut/planning-page-ui.png b/docs/images/ug-beg-tut/planning-page-ui.png new file mode 100644 index 00000000000..bde03c6eaa7 Binary files /dev/null and b/docs/images/ug-beg-tut/planning-page-ui.png differ diff --git a/docs/images/unplan-item.png b/docs/images/unplan-item.png new file mode 100644 index 00000000000..9f2a362087a Binary files /dev/null and b/docs/images/unplan-item.png differ diff --git a/docs/images/unplanCommand.png b/docs/images/unplanCommand.png new file mode 100644 index 00000000000..f5a504d5395 Binary files /dev/null and b/docs/images/unplanCommand.png differ diff --git a/docs/images/waddle-launch.png b/docs/images/waddle-launch.png new file mode 100644 index 00000000000..b91148fb2bc Binary files /dev/null and b/docs/images/waddle-launch.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..64149825cae 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,19 +1,31 @@ --- layout: page -title: AddressBook Level-3 +title: Waddle --- -[![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) +# Waddle 🦆 -![Ui](images/Ui.png) +[![CI Status](https://github.com/AY2223S1-CS2103T-W11-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2223S1-CS2103-W11-4/tp/actions) +[![codecov](https://codecov.io/gh/AY2223S1-CS2103T-W11-4/tp/branch/master/graph/badge.svg?token=45LHH4UIGB)](https://codecov.io/gh/AY2223S1-CS2103T-W11-4/tp) -**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). +![Ui](images/mainPage.png) + +**Introduction** + +* **Waddle** is a simple, no-frills travel planning application catered to people who love doing everything on their keyboards. +* Features: + * You can plan your travels in 3 simple steps! + * Create a trip + * Add activities + * Schedule + * After you plan your trip, easily export your itinerary for easy reference during the trip itself. + +* Visit our product website for a detailed documentation, **[Waddle Product Website](https://ay2223s1-cs2103-W11-4.github.io/tp/UserGuide.html)**. -* 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. **Acknowledgements** +* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). + * Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5) diff --git a/docs/team/cfsy.md b/docs/team/cfsy.md new file mode 100644 index 00000000000..879bc25b8eb --- /dev/null +++ b/docs/team/cfsy.md @@ -0,0 +1,81 @@ +--- +layout: page +title: Foo Shi Yu's Project Portfolio Page +--- + +### Project: Waddle + +Waddle is an app for easy-to-use travel itinerary creation and management. + +Given below are my contributions to the project. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=cfsy&breakdown=true) + + +* **Enhancements Implemented**: + * Implemented the following features: + * New UI — Brand new UI for Waddle + * Created new classes for UI elements + * Implemented UI page change for `select` and `home` commands + * (Pull requests [\#45](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/45), [\#83](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/83), [\#94](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/94), [\#96](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/96), [\#98](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/98), [\#124](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/124), [\#132](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/132)) + * `select` command — Allows the user to select an itinerary and enter the itinerary planning page + * Created `SelectCommand` and `SelectCommandParser` classes + * (Pull requests [\#32](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/32), [\#80](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/80)) + * `home` command — Allows the user to return to the home page + * Created `HomeCommand` class + * (Pull request [\#32](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/32)) + * `free` command — Lists the vacant time slots in an itinerary + * Created `FreeCommand` class + * (Pull request [\#103](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/103)) + * `copy` command — Copies the itinerary in a text format into the user's clipboard + * Created `CopyCommand` class + * (Pull request [\#127](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/127)) + * Created the following supporting classes: + * `StageManager` — class to keep track of the current page and selected itinerary (Pull request [\#32](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/32)) + * `Day` — class to encapsulate a day in an itinerary (Pull requests [\#75](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/75), [\#78](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/78)) + * `StartTime` — class to encapsulate a starting time (Pull request [\#79](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/79)) + * `Text` — class to abstract text formatting related tasks (Pull request [\#127](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/127)) + * Created the following UI related classes: + * `ItemCard` — class for an item card + * `ItemListPanel` — class for a list of item cards + * `ItemGroupCard` — class for an item group which includes a label and list of item cards + * `ItemGroupListPanel` — class for a list of item groups + * (Pull requests [\#83](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/83), [\#94](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/94), [\#96](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/96)) + * Completed the following miscellaneous tasks: + * Implement logic for time conflict checks when planning items and editing itineraries/items (Pull request [\#134](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/134)) + * Implement 2 decimal place representation of floats for budget and cost (Pull request [\#195](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/195)) + * PED bug fixes (Pull request [\#194](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/194)) + * Wrote tests in the following pull requests + * Pull requests [\#203](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/203), [\#220](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/220), [\#224](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/224), [\#233](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/233), [\#236](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/236), [\#243](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/243) + + +* **Documentation**: + * User Guide: + * Wrote the content for the following commands + * `add (itinerary)`, `add (item)` + * Wrote the Notes section for the following commands + * `add (itinerary)`, `add (item)`, `edit (itinerary)`, `edit (item)`, `plan`, `copy` + * (Pull requests [\#16](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/16), [\#132](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/132)) + * Developer Guide: + * Updated the original AB3 diagrams to correctly reflect our application + * Created the following diagrams + * `ItineraryClassDiagram`, `PlanSequenceDiagram`, `PlanSequenceDiagram2` + * Wrote the content for the following sections + * `StageManager` under the Architecture section + * Implementation of the `Plan/Unplan`, `Edit (item)`, and `Edit (itinerary)` features + * (Pull requests [\#17](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/17), [\#62](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/62), [\#107](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/107)) + + +* **Contributions to team-based tasks**: + * Initiate weekly and ad-hoc team meetings + * Plan the agenda + * Lead discussions in implementation and design + * Allocate work and set internal deadlines + * Keep track of deadlines and deliverables + * Managed releases `v1.3.1` and `v1.3.2` (2 releases) on GitHub + + +* **Tools**: + * Used Java AWT Clipboard for copying itineraries to the user's clipboard (Pull request [\#127](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/127)) + * Integrated a third party library (commons-lang3) to the project to check for OS type before saving the PDF file (Pull request [\#131](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/131)) + diff --git a/docs/team/ciaoosuuu.md b/docs/team/ciaoosuuu.md new file mode 100644 index 00000000000..d47f73a6d85 --- /dev/null +++ b/docs/team/ciaoosuuu.md @@ -0,0 +1,74 @@ +--- +layout: page +title: Chen Shun's Project Portfolio Page +--- + +### Project: Waddle + +Waddle is a simple, no-frills travel planning application catered to people who love doing everything on their keyboards. + +Given below are my contributions to the project. + +* **New Feature**: Add an export to PDF command. + * What it does: This command allows users to export the itinerary they have planned into an offline PDF document. + * Justification: This feature improves the product significantly as users usually travel with other people, hence +there is most likely a need to share itinerary with others. This feature allows users to have an offline PDF document +to send to their travel buddies. + * Highlights: The implementation was challenging as it involved incorporating external libraries to our project, and +it was necessary to look through the heavy documentation to implement this feature. + * Credits: This feature heavily relies on [Apache PDFBox](https://pdfbox.apache.org/) + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=ciaoosuuu&breakdown=true&sort=groupTitle&sortWithin=title&since=2022-09-16&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + +* **Project management**: + * Managed releases `Waddle v1.2` and `v1.2.1` (2 releases) on GitHub + +* **Enhancements to existing features**: + * Added `Itinerary` and relevant classes ([#28](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/28)) + * `Country`, `People`, `Description`, `Date` + * Implemented essential helper methods in `Itinerary` for `plan` and `unplan` commands + * `Itinerary#removeItem`, `Itinerary#setItem`, `Itinerary#planItem`, `Itinerary#unplanItem`, + * Added the calculation logic for Budget + * Edited various command's parser to allow optional fields + * Constantly improving existing commands and fixing bugs + ([#77](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/77), + [#81](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/81), + [#82](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/82), + [#88](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/88), + [#89](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/89), + [#90](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/90), + [#93](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/93), + [#113](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/113), + [#126](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/126), + [#190](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/190), + [#192](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/192), + [#210](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/210), + [#234](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/234), + [#245](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/245),) + +* **Documentation**: + * User Guide: + * Edited documentation for the features + * Vetted the documentation for any broken links + * Developer Guide: + * Added design details of the Model class + ([#56](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/56), + [#57](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/57)) + * Added Model class UML diagram ([#59](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/59)) + * Added Logic and Parser class UML diagram ([#257](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/257)) + * Added implementation details for export to PDF command ([#245](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/245)) + +* **Team-based tasks**: + * Enabled assertion in gradle ([#89](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/89)) + * Created milestone v1.4 + * Made use of issue tracker to create issues and assign issues + * Provided pull request reviews for teammates + +* **Community**: + * Assisted other teams by reporting more than average number of bugs for PE Dry run + +* **Tools**: + * Integrated a third party library (Apache PDFBox) to the project + ([#97](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/97), + [#118](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/118), + [#126](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/126)) diff --git a/docs/team/ningtan11.md b/docs/team/ningtan11.md new file mode 100644 index 00000000000..706944275ee --- /dev/null +++ b/docs/team/ningtan11.md @@ -0,0 +1,42 @@ +--- +layout: page +title: Shao Ning's Project Portfolio Page +--- + +### Project: Waddle + +Waddle is an app for easy-to-use travel itinerary creation and management. + +Given below are my contributions to the project. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=ningtan11&breakdown=true&sort=groupTitle&sortWithin=title&since=2022-09-16&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + +* **Enhancements implemented** + * Created the following supporting classes: + * `Cost` and `Duration` fields of the `Item` object + * Implemented the following classes: + * `ItemCard` and `ItemListPanel` + * Involved in the implementation of the following classes: + * `Priority` field of the `Item` object + * Edited various help messages + * Fixed various bugs + +* **Project management**: + * Nothing + +* **Documentation**: + * Wrote and edited help messages for various commands + * User Guide: + * Wrote the content for the following sections: + * Universal commands + * The main page + * The planning page + * Various bug fixes throughout the document + * Developer Guide: + * TBC + * JavaDocs + * Wrote and edited various JavaDocs comments + +* **Community**: + * Reported bugs and suggestions for other teams in the class + * Reviewed UG and DG for other teams in the class diff --git a/docs/team/pewggls.md b/docs/team/pewggls.md new file mode 100644 index 00000000000..76ac5efb040 --- /dev/null +++ b/docs/team/pewggls.md @@ -0,0 +1,42 @@ +--- +layout: page +title: Hui Yi Lu's Project Portfolio Page +--- + +### Project: Waddle + +Waddle is a simple, no-frills travel itinerary planning CLI application catered to people who love doing everything on their keyboards. + +Given below are my contributions to the project. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=pewggls&breakdown=true) + +* **Project management**: + * NIL + +* **Enhancements implemented** + * Created and was involved in implementing: + * The classes `UniqueItemList`, `ItineraryDuration`, `Budget`, `AddItemCommand`, and `AddItemCommandParser` + * Storage functionality for Itinerary, and Item: `JsonAdaptedItinerary` and `JsonAdaptedItem` + * Wrote tests for the classes `Budget`, `DeleteItemCommand`, `AddItemCommand`, `AddCommandParser`, + `JsonAdaptedItem`, and `JsonAdaptedItinerary` + * Fixed minor bugs for: + * Invalid itinerary details + + +* **Documentation**: + * User Guide: + * Command summary section + * Introduction section + * Developer Guide: + * Edit an itinerary section + * Use case for Add an item to an itinerary + +* **Community**: + * Reported bugs for other teams + * Made UG and DG suggestions for other teams + + +* **Tools**: + * NIL + diff --git a/docs/team/seox123.md b/docs/team/seox123.md new file mode 100644 index 00000000000..3819e13d3c2 --- /dev/null +++ b/docs/team/seox123.md @@ -0,0 +1,57 @@ +--- +layout: page +title: Law Sean Meng's Project Portfolio Page +--- + +### Project: Waddle + +Waddle is a simple, no-frills travel planning application catered to people who love doing everything on their keyboards. + +Given below are my contributions to the project. + +* **New Feature**: Add an edit item command. [#49](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/49) + * What it does: This command allows users to edit an item in their unscheduled item list. + * Justification: This feature improves the product significantly as users can easily edit minor details in the + items in their item list without having to delete and re-add the entire item. + * Highlights: Writing the `EditItemDescriptor` class allowed me to learn how to use a defensive copy to prevent + the user from making any unwanted changes to the original copy of the item. +* **New Feature**: Add a multi index class. [#76](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/76) + * What it does: Allows for access of items inside the day list, using a more complex version of an `Index`. + * Justification: There is a need for a different indexing of items that are in the day list compared to the + unscheduled item list. This is to allow users to easily access items in both lists from the same item-planning + page. + * Highlights: It was challenging coming up with a way to represent the different indexing that would satisfy our + needs. +* **New Feature**: Add a plan and unplan command. [#85](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/85), [#87](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/87) + * What it does: Allows for scheduling and unscheduling of items in the wishlist, to a specific day and time. + * Justification: As a travel planning application, users will need to schedule their activities in order to create a + cohesive itinerary using our app. + * Highlights: There was plenty of collaboration and discussion with team members on where to handle each aspect of + the plan and unplan commands, such as shifting the inner workings to the `Itinerary` and `Day` classes. + + + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=seox123&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2022-09-16&tabOpen=true&tabType=zoom&zFR=false&zA=seox123&zR=AY2223S1-CS2103T-W11-4%2Ftp%5Bmaster%5D&zACS=153.1875&zS=2022-09-16&zFS=seox123&zU=2022-11-07&zMG=false&zFTF=commit&zFGS=groupByRepos) + +* **Project management**: + * Managed releases `Waddle v1.4.1` and `1.4.2` (2 releases) on GitHub + +* **Enhancements to existing features**: + * Add a `Priority` field to `Item` class. [#61](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/61) + * Fix bugs and close issues. [#100](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/100), [#200](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/200), [#201](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/201), [#202](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/202) + * Write test cases, focussing mainly on `Command` and `Parser` classes. [#53](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/53), [#228](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/228), [#230](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/230), [#241](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/241) + +* **Documentation**: + * User Guide: + * Updated user guide with the newly-added commands like `plan`, `unplan`, `free`, and more. [#117](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/117) + * Updated Ui snapshot of the application. [#120](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/120) + * Developer Guide: + * Added multiple use cases to the developer guide. [#51](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/51) + * Added new proposal to unplan items. [#67](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/67) + +* **Team-based tasks**: + * Conducted testing of app and opened issues. [#101](https://github.com/AY2223S1-CS2103T-W11-4/tp/issues/101), [#102](https://github.com/AY2223S1-CS2103T-W11-4/tp/issues/102) + * Made necessary changes and renaming of classes to be more suitable with our application. [#53](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/53), [#85](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/85), [#248](https://github.com/AY2223S1-CS2103T-W11-4/tp/pull/248) + +* **Community**: + * Assisted other teams in detection and reporting of bugs during the PE dry run. diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md index 4fb62a83ef6..7bc119d5510 100644 --- a/docs/tutorials/TracingCode.md +++ b/docs/tutorials/TracingCode.md @@ -190,7 +190,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [ public CommandResult execute(Model model) throws CommandException { ... Person personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); + Person editedPerson = createEditedPerson(personToEdit, editItineraryDescriptor); if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { throw new CommandException(MESSAGE_DUPLICATE_PERSON); } diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java deleted file mode 100644 index 1deb3a1e469..00000000000 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ /dev/null @@ -1,13 +0,0 @@ -package seedu.address.commons.core; - -/** - * 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!"; - -} diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java deleted file mode 100644 index 71656d7c5c8..00000000000 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ /dev/null @@ -1,67 +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 seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; - -/** - * Adds a person to the address book. - */ -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; - - /** - * Creates an AddCommand to add the specified {@code Person} - */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.addPerson(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); - } - - @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)); - } -} 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/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/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java deleted file mode 100644 index 3b8bfa035e8..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ /dev/null @@ -1,60 +0,0 @@ -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.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 - */ -public class AddCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the AddCommand - * and returns an AddCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - 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)); - } - - 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); - } - - /** - * 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/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/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/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java deleted file mode 100644 index b117acb9c55..00000000000 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ /dev/null @@ -1,124 +0,0 @@ -package seedu.address.logic.parser; - -import static java.util.Objects.requireNonNull; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * 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 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 { - String trimmedIndex = oneBasedIndex.trim(); - if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { - throw new ParseException(MESSAGE_INVALID_INDEX); - } - return Index.fromOneBased(Integer.parseInt(trimmedIndex)); - } - - /** - * Parses a {@code String name} into a {@code Name}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code name} is invalid. - */ - public static Name parseName(String name) throws ParseException { - requireNonNull(name); - String trimmedName = name.trim(); - if (!Name.isValidName(trimmedName)) { - throw new ParseException(Name.MESSAGE_CONSTRAINTS); - } - return new Name(trimmedName); - } - - /** - * Parses a {@code String phone} into a {@code Phone}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code phone} is invalid. - */ - public static Phone parsePhone(String phone) throws ParseException { - requireNonNull(phone); - String trimmedPhone = phone.trim(); - if (!Phone.isValidPhone(trimmedPhone)) { - throw new ParseException(Phone.MESSAGE_CONSTRAINTS); - } - return new Phone(trimmedPhone); - } - - /** - * Parses a {@code String address} into an {@code Address}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code address} is invalid. - */ - public static Address parseAddress(String address) throws ParseException { - requireNonNull(address); - String trimmedAddress = address.trim(); - if (!Address.isValidAddress(trimmedAddress)) { - throw new ParseException(Address.MESSAGE_CONSTRAINTS); - } - return new Address(trimmedAddress); - } - - /** - * Parses a {@code String email} into an {@code Email}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code email} is invalid. - */ - public static Email parseEmail(String email) throws ParseException { - requireNonNull(email); - String trimmedEmail = email.trim(); - if (!Email.isValidEmail(trimmedEmail)) { - throw new ParseException(Email.MESSAGE_CONSTRAINTS); - } - return new Email(trimmedEmail); - } - - /** - * Parses a {@code String tag} into a {@code Tag}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code tag} is invalid. - */ - public static Tag parseTag(String tag) throws ParseException { - requireNonNull(tag); - String trimmedTag = tag.trim(); - if (!Tag.isValidTagName(trimmedTag)) { - throw new ParseException(Tag.MESSAGE_CONSTRAINTS); - } - return new Tag(trimmedTag); - } - - /** - * Parses {@code Collection tags} into a {@code Set}. - */ - public static Set parseTags(Collection tags) throws ParseException { - requireNonNull(tags); - final Set tagSet = new HashSet<>(); - for (String tagName : tags) { - tagSet.add(parseTag(tagName)); - } - return tagSet; - } -} diff --git a/src/main/java/seedu/address/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 deleted file mode 100644 index d54df471c1f..00000000000 --- a/src/main/java/seedu/address/model/Model.java +++ /dev/null @@ -1,87 +0,0 @@ -package seedu.address.model; - -import java.nio.file.Path; -import java.util.function.Predicate; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; - -/** - * 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}. - */ - void setUserPrefs(ReadOnlyUserPrefs userPrefs); - - /** - * Returns the user prefs. - */ - ReadOnlyUserPrefs getUserPrefs(); - - /** - * Returns the user prefs' GUI settings. - */ - GuiSettings getGuiSettings(); - - /** - * Sets the user prefs' GUI settings. - */ - void setGuiSettings(GuiSettings guiSettings); - - /** - * Returns the user prefs' address book file path. - */ - Path getAddressBookFilePath(); - - /** - * Sets the user prefs' address book file path. - */ - void setAddressBookFilePath(Path addressBookFilePath); - - /** - * Replaces address book data with the data in {@code addressBook}. - */ - void setAddressBook(ReadOnlyAddressBook addressBook); - - /** Returns the AddressBook */ - ReadOnlyAddressBook getAddressBook(); - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - boolean hasPerson(Person person); - - /** - * Deletes the given person. - * The person must exist in the address book. - */ - void deletePerson(Person target); - - /** - * Adds the given person. - * {@code person} must not already exist in the address book. - */ - void addPerson(Person person); - - /** - * 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. - */ - void setPerson(Person target, Person editedPerson); - - /** Returns an unmodifiable view of the filtered person list */ - ObservableList getFilteredPersonList(); - - /** - * Updates the filter of the filtered person list to filter by the given {@code predicate}. - * @throws NullPointerException if {@code predicate} is null. - */ - void updateFilteredPersonList(Predicate predicate); -} diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java deleted file mode 100644 index 86c1df298d7..00000000000 --- a/src/main/java/seedu/address/model/ModelManager.java +++ /dev/null @@ -1,150 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.nio.file.Path; -import java.util.function.Predicate; -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import javafx.collections.transformation.FilteredList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; - -/** - * Represents the in-memory model of the address book data. - */ -public class ModelManager implements Model { - private static final Logger logger = LogsCenter.getLogger(ModelManager.class); - - private final AddressBook addressBook; - private final UserPrefs userPrefs; - private final FilteredList filteredPersons; - - /** - * Initializes a ModelManager with the given addressBook and userPrefs. - */ - public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { - requireAllNonNull(addressBook, userPrefs); - - logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); - - this.addressBook = new AddressBook(addressBook); - this.userPrefs = new UserPrefs(userPrefs); - filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); - } - - public ModelManager() { - this(new AddressBook(), new UserPrefs()); - } - - //=========== UserPrefs ================================================================================== - - @Override - public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { - requireNonNull(userPrefs); - this.userPrefs.resetData(userPrefs); - } - - @Override - public ReadOnlyUserPrefs getUserPrefs() { - return userPrefs; - } - - @Override - public GuiSettings getGuiSettings() { - return userPrefs.getGuiSettings(); - } - - @Override - public void setGuiSettings(GuiSettings guiSettings) { - requireNonNull(guiSettings); - userPrefs.setGuiSettings(guiSettings); - } - - @Override - public Path getAddressBookFilePath() { - return userPrefs.getAddressBookFilePath(); - } - - @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - userPrefs.setAddressBookFilePath(addressBookFilePath); - } - - //=========== AddressBook ================================================================================ - - @Override - public void setAddressBook(ReadOnlyAddressBook addressBook) { - this.addressBook.resetData(addressBook); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return addressBook; - } - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return addressBook.hasPerson(person); - } - - @Override - public void deletePerson(Person target) { - addressBook.removePerson(target); - } - - @Override - public void addPerson(Person person) { - addressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - } - - @Override - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - addressBook.setPerson(target, editedPerson); - } - - //=========== Filtered Person List Accessors ============================================================= - - /** - * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of - * {@code versionedAddressBook} - */ - @Override - public ObservableList getFilteredPersonList() { - return filteredPersons; - } - - @Override - public void updateFilteredPersonList(Predicate predicate) { - requireNonNull(predicate); - filteredPersons.setPredicate(predicate); - } - - @Override - public boolean equals(Object obj) { - // short circuit if same object - if (obj == this) { - return true; - } - - // instanceof handles nulls - if (!(obj instanceof ModelManager)) { - return false; - } - - // state check - ModelManager other = (ModelManager) obj; - return addressBook.equals(other.addressBook) - && userPrefs.equals(other.userPrefs) - && filteredPersons.equals(other.filteredPersons); - } - -} 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/person/Address.java b/src/main/java/seedu/address/model/person/Address.java deleted file mode 100644 index 60472ca22a0..00000000000 --- a/src/main/java/seedu/address/model/person/Address.java +++ /dev/null @@ -1,57 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's address in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} - */ -public class Address { - - public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[^\\s].*"; - - public final String value; - - /** - * Constructs an {@code Address}. - * - * @param address A valid address. - */ - public Address(String address) { - requireNonNull(address); - checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS); - value = address; - } - - /** - * Returns true if a given string is a valid email. - */ - public static boolean isValidAddress(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Address // instanceof handles nulls - && value.equals(((Address) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java deleted file mode 100644 index f866e7133de..00000000000 --- a/src/main/java/seedu/address/model/person/Email.java +++ /dev/null @@ -1,71 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's email in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} - */ -public class Email { - - private static final String SPECIAL_CHARACTERS = "+_.-"; - public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain " - + "and adhere to the following constraints:\n" - + "1. The local-part should only contain alphanumeric characters and these special characters, excluding " - + "the parentheses, (" + SPECIAL_CHARACTERS + "). The local-part may not start or end with any special " - + "characters.\n" - + "2. This is followed by a '@' and then a domain name. The domain name is made up of domain labels " - + "separated by periods.\n" - + "The domain name must:\n" - + " - end with a domain label at least 2 characters long\n" - + " - have each domain label start and end with alphanumeric characters\n" - + " - have each domain label consist of alphanumeric characters, separated only by hyphens, if any."; - // alphanumeric and special characters - private static final String ALPHANUMERIC_NO_UNDERSCORE = "[^\\W_]+"; // alphanumeric characters except underscore - private static final String LOCAL_PART_REGEX = "^" + ALPHANUMERIC_NO_UNDERSCORE + "([" + SPECIAL_CHARACTERS + "]" - + ALPHANUMERIC_NO_UNDERSCORE + ")*"; - private static final String DOMAIN_PART_REGEX = ALPHANUMERIC_NO_UNDERSCORE - + "(-" + ALPHANUMERIC_NO_UNDERSCORE + ")*"; - private static final String DOMAIN_LAST_PART_REGEX = "(" + DOMAIN_PART_REGEX + "){2,}$"; // At least two chars - private static final String DOMAIN_REGEX = "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX; - public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX; - - public final String value; - - /** - * Constructs an {@code Email}. - * - * @param email A valid email address. - */ - public Email(String email) { - requireNonNull(email); - checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS); - value = email; - } - - /** - * Returns if a given string is a valid email. - */ - public static boolean isValidEmail(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Email // instanceof handles nulls - && value.equals(((Email) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/person/Name.java deleted file mode 100644 index 79244d71cf7..00000000000 --- a/src/main/java/seedu/address/model/person/Name.java +++ /dev/null @@ -1,59 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's name in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} - */ -public class Name { - - public static final String MESSAGE_CONSTRAINTS = - "Names should only contain alphanumeric characters and spaces, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; - - public final String fullName; - - /** - * Constructs a {@code Name}. - * - * @param name A valid name. - */ - public Name(String name) { - requireNonNull(name); - checkArgument(isValidName(name), MESSAGE_CONSTRAINTS); - fullName = name; - } - - /** - * Returns true if a given string is a valid name. - */ - public static boolean isValidName(String test) { - return test.matches(VALIDATION_REGEX); - } - - - @Override - public String toString() { - return fullName; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Name // instanceof handles nulls - && fullName.equals(((Name) other).fullName)); // state check - } - - @Override - public int hashCode() { - return fullName.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java deleted file mode 100644 index 8ff1d83fe89..00000000000 --- a/src/main/java/seedu/address/model/person/Person.java +++ /dev/null @@ -1,123 +0,0 @@ -package seedu.address.model.person; - -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import seedu.address.model.tag.Tag; - -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. - */ -public class Person { - - // Identity fields - private final Name name; - private final Phone phone; - private final Email email; - - // Data fields - private final Address address; - private final Set tags = new HashSet<>(); - - /** - * Every field must be present and not null. - */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - requireAllNonNull(name, phone, email, address, tags); - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - this.tags.addAll(tags); - } - - public Name getName() { - return name; - } - - public Phone getPhone() { - return phone; - } - - public Email getEmail() { - return email; - } - - public Address getAddress() { - return address; - } - - /** - * Returns an immutable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - */ - public Set getTags() { - return Collections.unmodifiableSet(tags); - } - - /** - * Returns true if both persons have the same name. - * This defines a weaker notion of equality between two persons. - */ - public boolean isSamePerson(Person otherPerson) { - if (otherPerson == this) { - return true; - } - - return otherPerson != null - && otherPerson.getName().equals(getName()); - } - - /** - * Returns true if both persons have the same identity and data fields. - * This defines a stronger notion of equality between two persons. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof Person)) { - 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()); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append(getName()) - .append("; Phone: ") - .append(getPhone()) - .append("; Email: ") - .append(getEmail()) - .append("; Address: ") - .append(getAddress()); - - Set tags = getTags(); - if (!tags.isEmpty()) { - builder.append("; Tags: "); - tags.forEach(builder::append); - } - return builder.toString(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/person/Phone.java deleted file mode 100644 index 872c76b382f..00000000000 --- a/src/main/java/seedu/address/model/person/Phone.java +++ /dev/null @@ -1,53 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's phone number in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} - */ -public class Phone { - - - public static final String MESSAGE_CONSTRAINTS = - "Phone numbers should only contain numbers, and it should be at least 3 digits long"; - public static final String VALIDATION_REGEX = "\\d{3,}"; - public final String value; - - /** - * Constructs a {@code Phone}. - * - * @param phone A valid phone number. - */ - public Phone(String phone) { - requireNonNull(phone); - checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS); - value = phone; - } - - /** - * Returns true if a given string is a valid phone number. - */ - public static boolean isValidPhone(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Phone // instanceof handles nulls - && value.equals(((Phone) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java deleted file mode 100644 index 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/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java deleted file mode 100644 index b0ea7e7dad7..00000000000 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ /dev/null @@ -1,54 +0,0 @@ -package seedu.address.model.tag; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Tag in the address book. - * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} - */ -public class Tag { - - public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric"; - public static final String VALIDATION_REGEX = "\\p{Alnum}+"; - - public final String tagName; - - /** - * Constructs a {@code Tag}. - * - * @param tagName A valid tag name. - */ - public Tag(String tagName) { - requireNonNull(tagName); - checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS); - this.tagName = tagName; - } - - /** - * Returns true if a given string is a valid tag name. - */ - public static boolean isValidTagName(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Tag // instanceof handles nulls - && tagName.equals(((Tag) other).tagName)); // state check - } - - @Override - public int hashCode() { - return tagName.hashCode(); - } - - /** - * Format state as text for viewing. - */ - public String toString() { - return '[' + tagName + ']'; - } - -} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java deleted file mode 100644 index 1806da4facf..00000000000 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.model.util; - -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; - -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Contains utility methods for populating {@code AddressBook} with sample data. - */ -public class SampleDataUtil { - public static Person[] getSamplePersons() { - return new Person[] { - new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), - new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), - new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), - new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), - new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), - new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) - }; - } - - public static ReadOnlyAddressBook getSampleAddressBook() { - AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); - } - return sampleAb; - } - - /** - * Returns a tag set containing the list of strings given. - */ - public static Set getTagSet(String... strings) { - return Arrays.stream(strings) - .map(Tag::new) - .collect(Collectors.toSet()); - } - -} diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/AddressBookStorage.java deleted file mode 100644 index 4599182b3f9..00000000000 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ /dev/null @@ -1,45 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; - -/** - * Represents a storage for {@link seedu.address.model.AddressBook}. - */ -public interface AddressBookStorage { - - /** - * Returns the file path of the data file. - */ - Path getAddressBookFilePath(); - - /** - * Returns AddressBook data as a {@link ReadOnlyAddressBook}. - * Returns {@code Optional.empty()} if storage file is not found. - * @throws 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; - - /** - * @see #getAddressBookFilePath() - */ - Optional readAddressBook(Path filePath) throws DataConversionException, IOException; - - /** - * Saves the given {@link ReadOnlyAddressBook} to the storage. - * @param addressBook cannot be null. - * @throws IOException if there was any problem writing to the file. - */ - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - - /** - * @see #saveAddressBook(ReadOnlyAddressBook) - */ - void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java deleted file mode 100644 index a6321cec2ea..00000000000 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ /dev/null @@ -1,109 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Jackson-friendly version of {@link Person}. - */ -class JsonAdaptedPerson { - - public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; - - private final String name; - private final String phone; - private final String email; - private final String address; - private final List tagged = new ArrayList<>(); - - /** - * Constructs a {@code JsonAdaptedPerson} with the given person details. - */ - @JsonCreator - public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone, - @JsonProperty("email") String email, @JsonProperty("address") String address, - @JsonProperty("tagged") List tagged) { - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - if (tagged != null) { - this.tagged.addAll(tagged); - } - } - - /** - * Converts a given {@code Person} into this class for Jackson use. - */ - public JsonAdaptedPerson(Person source) { - name = source.getName().fullName; - phone = source.getPhone().value; - email = source.getEmail().value; - address = source.getAddress().value; - tagged.addAll(source.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList())); - } - - /** - * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted person. - */ - public Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); - for (JsonAdaptedTag tag : tagged) { - personTags.add(tag.toModelType()); - } - - if (name == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); - } - if (!Name.isValidName(name)) { - throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); - } - final Name modelName = new Name(name); - - if (phone == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); - } - if (!Phone.isValidPhone(phone)) { - throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); - } - final Phone modelPhone = new Phone(phone); - - if (email == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); - } - if (!Email.isValidEmail(email)) { - throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); - } - final Email modelEmail = new Email(email); - - if (address == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); - } - if (!Address.isValidAddress(address)) { - throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); - } - final Address modelAddress = new Address(address); - - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/address/storage/JsonAdaptedTag.java deleted file mode 100644 index 0df22bdb754..00000000000 --- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java +++ /dev/null @@ -1,48 +0,0 @@ -package seedu.address.storage; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; - -/** - * Jackson-friendly version of {@link Tag}. - */ -class JsonAdaptedTag { - - private final String tagName; - - /** - * Constructs a {@code JsonAdaptedTag} with the given {@code tagName}. - */ - @JsonCreator - public JsonAdaptedTag(String tagName) { - this.tagName = tagName; - } - - /** - * Converts a given {@code Tag} into this class for Jackson use. - */ - public JsonAdaptedTag(Tag source) { - tagName = source.tagName; - } - - @JsonValue - public String getTagName() { - return tagName; - } - - /** - * Converts this Jackson-friendly adapted tag object into the model's {@code Tag} object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted tag. - */ - public Tag toModelType() throws IllegalValueException { - if (!Tag.isValidTagName(tagName)) { - throw new IllegalValueException(Tag.MESSAGE_CONSTRAINTS); - } - return new Tag(tagName); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java deleted file mode 100644 index dfab9daaa0d..00000000000 --- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java +++ /dev/null @@ -1,80 +0,0 @@ -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.ReadOnlyAddressBook; - -/** - * A class to access AddressBook data stored as a json file on the hard disk. - */ -public class JsonAddressBookStorage implements AddressBookStorage { - - private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class); - - private Path filePath; - - public JsonAddressBookStorage(Path filePath) { - this.filePath = filePath; - } - - public Path getAddressBookFilePath() { - return filePath; - } - - @Override - public Optional readAddressBook() throws DataConversionException { - return readAddressBook(filePath); - } - - /** - * Similar to {@link #readAddressBook()}. - * - * @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 { - requireNonNull(filePath); - - Optional jsonAddressBook = JsonUtil.readJsonFile( - filePath, JsonSerializableAddressBook.class); - if (!jsonAddressBook.isPresent()) { - return Optional.empty(); - } - - try { - return Optional.of(jsonAddressBook.get().toModelType()); - } catch (IllegalValueException ive) { - logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); - throw new DataConversionException(ive); - } - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, filePath); - } - - /** - * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}. - * - * @param filePath location of the data. Cannot be null. - */ - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - requireNonNull(addressBook); - requireNonNull(filePath); - - FileUtil.createIfMissing(filePath); - JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), 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/Storage.java b/src/main/java/seedu/address/storage/Storage.java deleted file mode 100644 index beda8bd9f11..00000000000 --- a/src/main/java/seedu/address/storage/Storage.java +++ /dev/null @@ -1,32 +0,0 @@ -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.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; - -/** - * API of the Storage component - */ -public interface Storage extends AddressBookStorage, UserPrefsStorage { - - @Override - Optional readUserPrefs() throws DataConversionException, IOException; - - @Override - void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; - - @Override - Path getAddressBookFilePath(); - - @Override - Optional readAddressBook() throws DataConversionException, IOException; - - @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java deleted file mode 100644 index 6cfa0162164..00000000000 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ /dev/null @@ -1,78 +0,0 @@ -package seedu.address.storage; - -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.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; - -/** - * Manages storage of AddressBook data in local storage. - */ -public class StorageManager implements Storage { - - private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; - private UserPrefsStorage userPrefsStorage; - - /** - * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}. - */ - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { - this.addressBookStorage = addressBookStorage; - this.userPrefsStorage = userPrefsStorage; - } - - // ================ UserPrefs methods ============================== - - @Override - public Path getUserPrefsFilePath() { - return userPrefsStorage.getUserPrefsFilePath(); - } - - @Override - public Optional readUserPrefs() throws DataConversionException, IOException { - return userPrefsStorage.readUserPrefs(); - } - - @Override - public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { - userPrefsStorage.saveUserPrefs(userPrefs); - } - - - // ================ AddressBook methods ============================== - - @Override - public Path getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); - } - - @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); - } - - @Override - public Optional readAddressBook(Path filePath) throws DataConversionException, IOException { - logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); - } - -} diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java deleted file mode 100644 index 7fc927bc5d9..00000000000 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ /dev/null @@ -1,77 +0,0 @@ -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.person.Person; - -/** - * An UI component that displays information of a {@code Person}. - */ -public class PersonCard extends UiPart { - - private static final String FXML = "PersonListCard.fxml"; - - /** - * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. - * As a consequence, UI elements' variable names cannot be set to such keywords - * or an exception will be thrown by JavaFX during runtime. - * - * @see The issue on AddressBook level 4 - */ - - public final Person person; - - @FXML - private HBox cardPane; - @FXML - private Label name; - @FXML - private Label id; - @FXML - private Label phone; - @FXML - private Label address; - @FXML - private Label email; - @FXML - private FlowPane tags; - - /** - * Creates a {@code PersonCode} with the given {@code Person} and index to display. - */ - public PersonCard(Person person, int displayedIndex) { - super(FXML); - this.person = person; - 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() - .sorted(Comparator.comparing(tag -> tag.tagName)) - .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof PersonCard)) { - return false; - } - - // state check - PersonCard card = (PersonCard) other; - return id.getText().equals(card.id.getText()) - && person.equals(card.person); - } -} 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/AppParameters.java b/src/main/java/seedu/waddle/AppParameters.java similarity index 93% rename from src/main/java/seedu/address/AppParameters.java rename to src/main/java/seedu/waddle/AppParameters.java index ab552c398f3..82e2bade8f0 100644 --- a/src/main/java/seedu/address/AppParameters.java +++ b/src/main/java/seedu/waddle/AppParameters.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.waddle; import java.nio.file.Path; import java.nio.file.Paths; @@ -7,8 +7,8 @@ import java.util.logging.Logger; import javafx.application.Application; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.FileUtil; +import seedu.waddle.commons.core.LogsCenter; +import seedu.waddle.commons.util.FileUtil; /** * Represents the parsed command-line parameters given to the application. diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/waddle/Main.java similarity index 97% rename from src/main/java/seedu/address/Main.java rename to src/main/java/seedu/waddle/Main.java index 052a5068631..0c159e85e0f 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/seedu/waddle/Main.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.waddle; import javafx.application.Application; diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/waddle/MainApp.java similarity index 67% rename from src/main/java/seedu/address/MainApp.java rename to src/main/java/seedu/waddle/MainApp.java index 4133aaa0151..e4dd20138ac 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/waddle/MainApp.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.waddle; import java.io.IOException; import java.nio.file.Path; @@ -7,36 +7,36 @@ import javafx.application.Application; import javafx.stage.Stage; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.core.Version; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.ConfigUtil; -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.util.SampleDataUtil; -import seedu.address.storage.AddressBookStorage; -import seedu.address.storage.JsonAddressBookStorage; -import seedu.address.storage.JsonUserPrefsStorage; -import seedu.address.storage.Storage; -import seedu.address.storage.StorageManager; -import seedu.address.storage.UserPrefsStorage; -import seedu.address.ui.Ui; -import seedu.address.ui.UiManager; +import seedu.waddle.commons.core.Config; +import seedu.waddle.commons.core.LogsCenter; +import seedu.waddle.commons.core.Version; +import seedu.waddle.commons.exceptions.DataConversionException; +import seedu.waddle.commons.util.ConfigUtil; +import seedu.waddle.commons.util.StringUtil; +import seedu.waddle.logic.Logic; +import seedu.waddle.logic.LogicManager; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.ReadOnlyUserPrefs; +import seedu.waddle.model.ReadOnlyWaddle; +import seedu.waddle.model.UserPrefs; +import seedu.waddle.model.Waddle; +import seedu.waddle.model.util.SampleDataUtil; +import seedu.waddle.storage.JsonUserPrefsStorage; +import seedu.waddle.storage.JsonWaddleStorage; +import seedu.waddle.storage.Storage; +import seedu.waddle.storage.StorageManager; +import seedu.waddle.storage.UserPrefsStorage; +import seedu.waddle.storage.WaddleStorage; +import seedu.waddle.ui.Ui; +import seedu.waddle.ui.UiManager; /** * Runs the application. */ public class MainApp extends Application { - public static final Version VERSION = new Version(0, 2, 0, true); + public static final Version VERSION = new Version(1, 4, 2, true); private static final Logger logger = LogsCenter.getLogger(MainApp.class); @@ -48,7 +48,7 @@ public class MainApp extends Application { @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing Waddle ]==========================="); super.init(); AppParameters appParameters = AppParameters.parse(getParameters()); @@ -56,8 +56,8 @@ 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); + WaddleStorage waddleStorage = new JsonWaddleStorage(userPrefs.getWaddleFilePath()); + storage = new StorageManager(waddleStorage, userPrefsStorage); initLogging(config); @@ -69,25 +69,25 @@ 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 waddle and {@code userPrefs}.
+ * The data from the sample waddle will be used instead if {@code storage}'s waddle is not found, + * or an empty waddle will be used instead if errors occur when reading {@code storage}'s waddle. */ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional waddleOptional; + ReadOnlyWaddle initialData; try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Data file not found. Will be starting with a sample AddressBook"); + waddleOptional = storage.readWaddle(); + if (!waddleOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample Waddle"); } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + initialData = waddleOptional.orElseGet(SampleDataUtil::getSampleWaddle); } 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 Waddle"); + initialData = new Waddle(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Problem while reading from the file. Will be starting with an empty Waddle"); + initialData = new Waddle(); } return new ModelManager(initialData, userPrefs); @@ -151,7 +151,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 Waddle"); initializedPrefs = new UserPrefs(); } @@ -167,13 +167,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting Waddle " + MainApp.VERSION); ui.start(primaryStage); } @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("============================ [ Stopping Waddle ] ============================="); try { storage.saveUserPrefs(model.getUserPrefs()); } catch (IOException e) { diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/waddle/commons/core/Config.java similarity index 97% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/seedu/waddle/commons/core/Config.java index 91145745521..0982b516169 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/seedu/waddle/commons/core/Config.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.waddle.commons.core; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/waddle/commons/core/GuiSettings.java similarity index 98% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/seedu/waddle/commons/core/GuiSettings.java index ba33653be67..fb8bbb0f356 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/waddle/commons/core/GuiSettings.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.waddle.commons.core; import java.awt.Point; import java.io.Serializable; diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/waddle/commons/core/LogsCenter.java similarity index 97% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/seedu/waddle/commons/core/LogsCenter.java index 431e7185e76..09f4fb4665d 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/seedu/waddle/commons/core/LogsCenter.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.waddle.commons.core; import java.io.IOException; import java.util.Arrays; @@ -18,7 +18,7 @@ public class LogsCenter { private static final int MAX_FILE_COUNT = 5; private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB - private static final String LOG_FILE = "addressbook.log"; + private static final String LOG_FILE = "waddle.log"; private static Level currentLogLevel = Level.INFO; private static final Logger logger = LogsCenter.getLogger(LogsCenter.class); private static FileHandler fileHandler; diff --git a/src/main/java/seedu/waddle/commons/core/Messages.java b/src/main/java/seedu/waddle/commons/core/Messages.java new file mode 100644 index 00000000000..50d6c13565d --- /dev/null +++ b/src/main/java/seedu/waddle/commons/core/Messages.java @@ -0,0 +1,30 @@ +package seedu.waddle.commons.core; + +/** + * Container for user visible messages. + */ +public class Messages { + + public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command."; + public static final String MESSAGE_COPY_ERROR = "Unable to copy to clipboard."; + public static final String MESSAGE_OVER_BUDGET = "The provided cost exceeds the budget." + + " Please increase the budget or lower the cost."; + public static final String MESSAGE_ITINERARY_OVER_BUDGET = "The provided budget is insufficient to cover the cost." + + " Please increase the budget or lower the cost."; + public static final String MESSAGE_UNAVAILABLE_COMMAND_HOME = "Command is unavailable in the home page."; + public static final String MESSAGE_UNAVAILABLE_COMMAND_ITINERARY = "Command is unavailable in the itinerary page."; + public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; + public static final String MESSAGE_INVALID_ITINERARY_DISPLAYED_INDEX = "The itinerary index provided is invalid."; + public static final String MESSAGE_INVALID_ITEM_DISPLAYED_INDEX = "The item index provided is invalid."; + public static final String MESSAGE_INVALID_DAY_INDEX = "The day you have selected does not exist"; + public static final String MESSAGE_ITINERARIES_LISTED_OVERVIEW = "%1$d itineraries listed!"; + public static final String MESSAGE_INVALID_STAGE = "The stage you provided is invalid! \n%1$s"; + public static final String MESSAGE_CONFLICTING_ITEMS = "Quack, there is a time clash!" + + "\nThe provided time clashes with:\n%1$sPlease change the start time and/or the duration."; + public static final String MESSAGE_ITEM_PAST_MIDNIGHT = + "%1$s extends past midnight which is not currently supported.\n" + + "Please split %1$s into 2 parts and plan the second part at the start of the next day."; + // not meant for users to see + public static final String MESSAGE_UNKNOWN_STAGE = "Unknown stage, something went wrong with the StateManager."; +} + diff --git a/src/main/java/seedu/waddle/commons/core/Text.java b/src/main/java/seedu/waddle/commons/core/Text.java new file mode 100644 index 00000000000..7aeaebb75b4 --- /dev/null +++ b/src/main/java/seedu/waddle/commons/core/Text.java @@ -0,0 +1,29 @@ +package seedu.waddle.commons.core; + +import java.text.DecimalFormat; + +/** + * This class contains methods for text related operations. + */ +public class Text { + public static final int INDENT_NONE = 0; + public static final int INDENT_TWO = 2; + public static final int INDENT_FOUR = 4; + public static final DecimalFormat MONEY_PRINT_FORMATTER = new DecimalFormat("#,##0.00"); + public static final DecimalFormat MONEY_SAVE_FORMATTER = new DecimalFormat("0.00"); + + /** + * Indents the input text by specified amount of spaces. + * + * @param text Text to indent. + * @param indents Number of spaces to indent. + * @return The indented text. + */ + public static String indent(String text, int indents) { + String indentText = " ".repeat(indents); + if (indents == 0 || text.equals("")) { + return text; + } + return indentText + text.replaceAll(System.lineSeparator(), System.lineSeparator() + indentText); + } +} diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/waddle/commons/core/Version.java similarity index 98% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/seedu/waddle/commons/core/Version.java index 12142ec1e32..136c338a943 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/seedu/waddle/commons/core/Version.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.waddle.commons.core; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/seedu/waddle/commons/core/index/Index.java similarity index 97% rename from src/main/java/seedu/address/commons/core/index/Index.java rename to src/main/java/seedu/waddle/commons/core/index/Index.java index 19536439c09..ecdeab691be 100644 --- a/src/main/java/seedu/address/commons/core/index/Index.java +++ b/src/main/java/seedu/waddle/commons/core/index/Index.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core.index; +package seedu.waddle.commons.core.index; /** * Represents a zero-based or one-based index. diff --git a/src/main/java/seedu/waddle/commons/core/index/MultiIndex.java b/src/main/java/seedu/waddle/commons/core/index/MultiIndex.java new file mode 100644 index 00000000000..99fb529245a --- /dev/null +++ b/src/main/java/seedu/waddle/commons/core/index/MultiIndex.java @@ -0,0 +1,106 @@ +package seedu.waddle.commons.core.index; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a series of Index objects. + */ +public class MultiIndex { + public static final String MESSAGE_CONSTRAINTS = + "Index should only contain positive integers separated by a decimal point"; + public static final String VALIDATION_REGEX = "\\d+\\.?\\d*"; + private final List indices; + + public MultiIndex() { + indices = new ArrayList<>(); + } + + /** + * Returns true if a given string is a valid multi index. + */ + public static boolean isValidMultiIndex(String test) { + return test.matches(VALIDATION_REGEX); + } + + public void appendZeroBasedIndex(int index) { + indices.add(Index.fromZeroBased(index)); + } + + public void appendOneBasedIndex(int index) { + indices.add(Index.fromOneBased(index)); + } + + /** + * Adds {@code Index} to {@code MultiIndex} and returns the {@code MultiIndex}. + * + * @param index The {@code Index} to be added + * @return This {@code MultiIndex} object + */ + public MultiIndex addIndex(Index index) { + indices.add(index); + return this; + } + + /** + * Removes indices more than or equal to specified position. + * + * @param pos Specified position to remove from + */ + public void removeIndex(int pos) { + if (!isValidPos(pos)) { + throw new IndexOutOfBoundsException(); + } + for (int i = pos - 1; i < indices.size(); i++) { + indices.remove(i); + } + } + + public Index getDayIndex() { + if (this.indices.size() == 1) { + return null; + } + return getIndex(1); + } + + public Index getTaskIndex() { + if (this.indices.size() == 1) { + return getIndex(1); + } + return getIndex(2); + } + + private Index getIndex(int pos) { + if (!isValidPos(pos)) { + throw new IndexOutOfBoundsException(); + } + return indices.get(pos - 1); + } + + public boolean containsMultiIndex() { + return this.indices.size() >= 2; + } + + private boolean isValidPos(int pos) { + return pos >= 1 && pos <= indices.size(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < indices.size(); i++) { + sb.append(indices.get(i).getOneBased()); + if (i != indices.size() - 1) { + sb.append('.'); + } + } + return sb.toString(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof MultiIndex // instanceof handles nulls + && indices.equals(((MultiIndex) other).indices)); + } +} diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/seedu/waddle/commons/exceptions/DataConversionException.java similarity index 84% rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java rename to src/main/java/seedu/waddle/commons/exceptions/DataConversionException.java index 1f689bd8e3f..eec0b970120 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/seedu/waddle/commons/exceptions/DataConversionException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.waddle.commons.exceptions; /** * Represents an error during conversion of data from one format to another diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/waddle/commons/exceptions/IllegalValueException.java similarity index 93% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/seedu/waddle/commons/exceptions/IllegalValueException.java index 19124db485c..322a57816be 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/seedu/waddle/commons/exceptions/IllegalValueException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.waddle.commons.exceptions; /** * Signals that some given data does not fulfill some constraints. diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/seedu/waddle/commons/util/AppUtil.java similarity index 94% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/seedu/waddle/commons/util/AppUtil.java index 87aa89c0326..cb3ee5ce712 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/seedu/waddle/commons/util/AppUtil.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package seedu.waddle.commons.util; import static java.util.Objects.requireNonNull; import javafx.scene.image.Image; -import seedu.address.MainApp; +import seedu.waddle.MainApp; /** * A container for App specific utility functions diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/seedu/waddle/commons/util/CollectionUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/seedu/waddle/commons/util/CollectionUtil.java index eafe4dfd681..655ae0c97b4 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/seedu/waddle/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.waddle.commons.util; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/seedu/waddle/commons/util/ConfigUtil.java similarity index 77% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/seedu/waddle/commons/util/ConfigUtil.java index f7f8a2bd44c..b31ec451f35 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/seedu/waddle/commons/util/ConfigUtil.java @@ -1,11 +1,11 @@ -package seedu.address.commons.util; +package seedu.waddle.commons.util; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.waddle.commons.core.Config; +import seedu.waddle.commons.exceptions.DataConversionException; /** * A class for accessing the Config File. diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/waddle/commons/util/FileUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/seedu/waddle/commons/util/FileUtil.java index b1e2767cdd9..6591821bdf9 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/seedu/waddle/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.waddle.commons.util; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/seedu/waddle/commons/util/JsonUtil.java similarity index 97% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/seedu/waddle/commons/util/JsonUtil.java index 8ef609f055d..80165756063 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/seedu/waddle/commons/util/JsonUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.waddle.commons.util; import static java.util.Objects.requireNonNull; @@ -20,8 +20,8 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.waddle.commons.core.LogsCenter; +import seedu.waddle.commons.exceptions.DataConversionException; /** * Converts a Java object instance to JSON and vice versa diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/waddle/commons/util/StringUtil.java similarity index 95% rename from src/main/java/seedu/address/commons/util/StringUtil.java rename to src/main/java/seedu/waddle/commons/util/StringUtil.java index 61cc8c9a1cb..ee9346259a2 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/waddle/commons/util/StringUtil.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package seedu.waddle.commons.util; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.waddle.commons.util.AppUtil.checkArgument; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/waddle/logic/Logic.java similarity index 64% rename from src/main/java/seedu/address/logic/Logic.java rename to src/main/java/seedu/waddle/logic/Logic.java index 92cd8fa605a..3cd7ea4b69b 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/waddle/logic/Logic.java @@ -1,14 +1,14 @@ -package seedu.address.logic; +package seedu.waddle.logic; import java.nio.file.Path; import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -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.waddle.commons.core.GuiSettings; +import seedu.waddle.logic.commands.CommandResult; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.logic.parser.exceptions.ParseException; +import seedu.waddle.model.ReadOnlyWaddle; +import seedu.waddle.model.itinerary.Itinerary; /** * API of the Logic component @@ -26,17 +26,17 @@ public interface Logic { /** * Returns the AddressBook. * - * @see seedu.address.model.Model#getAddressBook() + * @see seedu.waddle.model.Model#getWaddle() */ - ReadOnlyAddressBook getAddressBook(); + ReadOnlyWaddle getWaddle(); /** Returns an unmodifiable view of the filtered list of persons */ - ObservableList getFilteredPersonList(); + ObservableList getFilteredItineraryList(); /** * Returns the user prefs' address book file path. */ - Path getAddressBookFilePath(); + Path getWaddleFilePath(); /** * Returns the user prefs' GUI settings. diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/waddle/logic/LogicManager.java similarity index 56% rename from src/main/java/seedu/address/logic/LogicManager.java rename to src/main/java/seedu/waddle/logic/LogicManager.java index 9d9c6d15bdc..3b5aa2d1025 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/waddle/logic/LogicManager.java @@ -1,21 +1,21 @@ -package seedu.address.logic; +package seedu.waddle.logic; import java.io.IOException; import java.nio.file.Path; import java.util.logging.Logger; import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -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.exceptions.ParseException; -import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; -import seedu.address.storage.Storage; +import seedu.waddle.commons.core.GuiSettings; +import seedu.waddle.commons.core.LogsCenter; +import seedu.waddle.logic.commands.Command; +import seedu.waddle.logic.commands.CommandResult; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.logic.parser.WaddleParser; +import seedu.waddle.logic.parser.exceptions.ParseException; +import seedu.waddle.model.Model; +import seedu.waddle.model.ReadOnlyWaddle; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.storage.Storage; /** * The main LogicManager of the app. @@ -26,7 +26,7 @@ public class LogicManager implements Logic { private final Model model; private final Storage storage; - private final AddressBookParser addressBookParser; + private final WaddleParser waddleParser; /** * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. @@ -34,7 +34,7 @@ public class LogicManager implements Logic { public LogicManager(Model model, Storage storage) { this.model = model; this.storage = storage; - addressBookParser = new AddressBookParser(); + waddleParser = new WaddleParser(); } @Override @@ -42,11 +42,11 @@ public CommandResult execute(String commandText) throws CommandException, ParseE logger.info("----------------[USER COMMAND][" + commandText + "]"); CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); + Command command = waddleParser.parseCommand(commandText); commandResult = command.execute(model); try { - storage.saveAddressBook(model.getAddressBook()); + storage.saveWaddle(model.getWaddle()); } catch (IOException ioe) { throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); } @@ -55,18 +55,18 @@ public CommandResult execute(String commandText) throws CommandException, ParseE } @Override - public ReadOnlyAddressBook getAddressBook() { - return model.getAddressBook(); + public ReadOnlyWaddle getWaddle() { + return model.getWaddle(); } @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); + public ObservableList getFilteredItineraryList() { + return model.getFilteredItineraryList(); } @Override - public Path getAddressBookFilePath() { - return model.getAddressBookFilePath(); + public Path getWaddleFilePath() { + return model.getWaddleFilePath(); } @Override diff --git a/src/main/java/seedu/waddle/logic/PdfFieldInfo.java b/src/main/java/seedu/waddle/logic/PdfFieldInfo.java new file mode 100644 index 00000000000..e39ce99b117 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/PdfFieldInfo.java @@ -0,0 +1,32 @@ +package seedu.waddle.logic; + +/** + * A Class to store information about a Pdf field. + */ +public class PdfFieldInfo { + private String name; + private String value; + + /** + * Constructor for a PdfFieldInfo. + * @param name Name of the field. + * @param value Value to insert into the field. + */ + public PdfFieldInfo(String name, String value) { + this.name = name; + this.value = value; + } + + public String getName() { + return this.name; + } + + public String getValue() { + return this.value; + } + + @Override + public String toString() { + return "Field name: " + this.name + " Value: " + this.value; + } +} diff --git a/src/main/java/seedu/waddle/logic/PdfFiller.java b/src/main/java/seedu/waddle/logic/PdfFiller.java new file mode 100644 index 00000000000..a307bc34f07 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/PdfFiller.java @@ -0,0 +1,150 @@ +package seedu.waddle.logic; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import javax.swing.filechooser.FileSystemView; + +import org.apache.commons.lang3.SystemUtils; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; +import org.apache.pdfbox.pdmodel.interactive.form.PDField; + +import seedu.waddle.commons.core.Text; +import seedu.waddle.model.item.Day; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.item.UniqueItemList; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * Class to fill pdf acroform with itinerary details. + */ +public class PdfFiller { + public static final int MAX_DISPLAY = 15; + private final Itinerary itinerary; + private final String pdfTemplate; + private final List pdfList; + private final PDDocument finalPdf; + + + /** + * Constructor for a PdfFiller + * + * @param itinerary Itinerary to export. + * @param pdfTemplate Default template for export. + * @throws IOException When fail to export itinerary information. + */ + public PdfFiller(Itinerary itinerary, String pdfTemplate) throws IOException { + this.itinerary = itinerary; + this.pdfTemplate = pdfTemplate; + this.pdfList = new ArrayList<>(); + this.finalPdf = new PDDocument(); + } + + private void fillField(PdfFieldInfo info, PDAcroForm form, List fieldList) throws IOException { + PDField field = form.getField(info.getName()); + field.setValue(info.getValue()); + fieldList.add(field); + } + + private void fillForm(Day day, List infoList) throws IOException { + + InputStream exportTemplate = getClass().getResourceAsStream(pdfTemplate); + PDDocument pdf = PDDocument.load(exportTemplate); + PDAcroForm form = pdf.getDocumentCatalog().getAcroForm(); + List fieldList = new ArrayList<>(); + ArrayList infoToFill = new ArrayList<>(infoList); + form.setXFA(null); + infoToFill.add(new PdfFieldInfo("itinerary_name", this.itinerary.getDescriptionString(Text.INDENT_NONE))); + infoToFill.add(new PdfFieldInfo("day", "Day " + (day.getDayNumber() + 1))); + + for (PdfFieldInfo info : infoToFill) { + fillField(info, form, fieldList); + } + + form.flatten(fieldList, true); + pdf.getDocumentCatalog().setAcroForm(form); + this.pdfList.add(pdf); + } + + /** + * Export a day into PDF + * + * @param day The day containing items to export. + * @throws IOException When export fails. + */ + private void fillDay(Day day) throws IOException { + UniqueItemList itemList = day.getItemList(); + int itemListSize = itemList.getSize(); + int numOfPages = (int) Math.ceil((double) itemListSize / MAX_DISPLAY); + if (numOfPages == 0) { + List fieldList = new ArrayList<>(); + for (int i = 0; i < MAX_DISPLAY; i++) { + PdfFieldInfo time = new PdfFieldInfo("time" + i, ""); + PdfFieldInfo activity = new PdfFieldInfo("item" + i, ""); + fieldList.add(time); + fieldList.add(activity); + } + fillForm(day, fieldList); + } + for (int i = 0; i < numOfPages; i++) { + List fieldList = new ArrayList<>(); + for (int j = 0; j < MAX_DISPLAY; j++) { + int targetIndex = i * MAX_DISPLAY + j; + if (targetIndex < itemListSize) { + Item item = itemList.get(targetIndex); + PdfFieldInfo time = new PdfFieldInfo("time" + j, + item.getTimeString(Text.INDENT_NONE).replace("Time: ", "")); + PdfFieldInfo activity = new PdfFieldInfo("item" + j, item.getDescription().toString()); + fieldList.add(time); + fieldList.add(activity); + } else { + PdfFieldInfo time = new PdfFieldInfo("time" + j, ""); + PdfFieldInfo activity = new PdfFieldInfo("item" + j, ""); + fieldList.add(time); + fieldList.add(activity); + } + } + fillForm(day, fieldList); + } + } + + /** + * Export an itinerary into PDF. + * + * @throws IOException When export fails. + */ + public void fillItinerary() throws IOException { + for (Day day : this.itinerary.getDays()) { + fillDay(day); + } + for (PDDocument pdf : this.pdfList) { + PDPage page = pdf.getPage(0); + this.finalPdf.addPage(page); + } + + // create a waddle directory and get the path + String defaultPath = FileSystemView.getFileSystemView().getDefaultDirectory().getPath(); + File waddleFolder; + if (SystemUtils.IS_OS_MAC) { + waddleFolder = new File(defaultPath + "/Documents/Waddle"); + + } else { + waddleFolder = new File(defaultPath + "/Waddle"); + } + if (!waddleFolder.exists()) { + waddleFolder.mkdirs(); + } + + finalPdf.save(waddleFolder + "/" + this.itinerary.getDescriptionString(Text.INDENT_NONE) + ".pdf"); + finalPdf.close(); + + // only can close when all operations are done + for (PDDocument pdf : this.pdfList) { + pdf.close(); + } + } +} diff --git a/src/main/java/seedu/waddle/logic/StageManager.java b/src/main/java/seedu/waddle/logic/StageManager.java new file mode 100644 index 00000000000..613cda2e457 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/StageManager.java @@ -0,0 +1,54 @@ +package seedu.waddle.logic; + +import static java.util.Objects.requireNonNull; + +import seedu.waddle.model.itinerary.Itinerary; + +/** + * This class keeps track of the stage the user is in. + */ +public class StageManager { + private static StageManager instance; + // default stage is home + private Stages currentStage = Stages.HOME; + // stores the currently selected Itinerary + private Itinerary selectedItinerary; + + private StageManager() { + } + + public static StageManager getInstance() { + if (StageManager.instance == null) { + StageManager.instance = new StageManager(); + return StageManager.instance; + } + return StageManager.instance; + } + + public Stages getCurrentStage() { + return this.currentStage; + } + + public boolean isCurrentStage(Stages stage) { + return this.currentStage == stage; + } + + public void setHomeStage() { + this.currentStage = Stages.HOME; + this.selectedItinerary = null; + } + + public void setWishStage(Itinerary selectedItinerary) throws NullPointerException { + requireNonNull(selectedItinerary); + this.currentStage = Stages.WISH; + this.selectedItinerary = selectedItinerary; + } + + public void switchStage(Stages selectedStage) { + this.currentStage = selectedStage; + } + + public Itinerary getSelectedItinerary() { + return this.selectedItinerary; + } +} diff --git a/src/main/java/seedu/waddle/logic/Stages.java b/src/main/java/seedu/waddle/logic/Stages.java new file mode 100644 index 00000000000..eb71a6c43f4 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/Stages.java @@ -0,0 +1,10 @@ +package seedu.waddle.logic; + +/** + * Enum for stages. + */ +public enum Stages { + NONE, // used in CommandResult to indicate that no state change occurred + HOME, //home page + WISH //wishlist stage +} diff --git a/src/main/java/seedu/waddle/logic/commands/AddCommand.java b/src/main/java/seedu/waddle/logic/commands/AddCommand.java new file mode 100644 index 00000000000..cb4de617968 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/commands/AddCommand.java @@ -0,0 +1,69 @@ +package seedu.waddle.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_BUDGET; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_COUNTRY; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_ITINERARY_DURATION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_PEOPLE; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_START_DATE; + +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * Adds an itinerary to Waddle. + */ +public class AddCommand extends Command { + + public static final String COMMAND_WORD = "add"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an Itinerary to waddle. " + + "Parameters: " + + PREFIX_DESCRIPTION + "DESCRIPTION " + + PREFIX_START_DATE + "START_DATE " + + PREFIX_ITINERARY_DURATION + "DURATION " + + "[" + PREFIX_COUNTRY + "COUNTRY] " + + "[" + PREFIX_PEOPLE + "NUMBER_OF_WADDLERS] " + + "[" + PREFIX_BUDGET + "BUDGET]\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_DESCRIPTION + "Summer Trip " + + PREFIX_COUNTRY + "India " + + PREFIX_START_DATE + "2025-10-28 " + + PREFIX_ITINERARY_DURATION + "15 " + + PREFIX_PEOPLE + "4 " + + PREFIX_BUDGET + "1000 "; + + public static final String MESSAGE_SUCCESS = "New itinerary added:\n%1$s"; + public static final String MESSAGE_DUPLICATE_ITINERARY = "This itinerary already exists"; + + private final Itinerary toAdd; + + /** + * Creates an AddCommand to add the specified {@code Itinerary} + */ + public AddCommand(Itinerary itinerary) { + requireNonNull(itinerary); + toAdd = itinerary; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasItinerary(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_ITINERARY); + } + + model.addItinerary(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @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)); + } +} diff --git a/src/main/java/seedu/waddle/logic/commands/AddItemCommand.java b/src/main/java/seedu/waddle/logic/commands/AddItemCommand.java new file mode 100644 index 00000000000..711b9cb8651 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/commands/AddItemCommand.java @@ -0,0 +1,69 @@ +package seedu.waddle.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_COST; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_ITEM_DURATION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_PRIORITY; + +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.item.exceptions.DuplicateItemException; +import seedu.waddle.model.itinerary.Itinerary; + + +/** + * Adds an item to the itinerary. + */ +public class AddItemCommand extends Command { + + public static final String COMMAND_WORD = "add"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an item to an itinerary. " + + "Parameters: " + + PREFIX_DESCRIPTION + "DESCRIPTION " + + PREFIX_ITEM_DURATION + "DURATION " + + "[" + PREFIX_PRIORITY + "PRIORITY] " + + "[" + PREFIX_COST + "COST]\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_DESCRIPTION + "Visit Taj Mahal " + + PREFIX_ITEM_DURATION + "180"; + + public static final String MESSAGE_SUCCESS = "New item added:\n%1$s"; + public static final String MESSAGE_DUPLICATE_ITEM = "This item already exists"; + + private final Item toAdd; + + /** + * Creates an AddItemCommand to add the specified {@code Item} + */ + public AddItemCommand(Item item) { + requireNonNull(item); + toAdd = item; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + StageManager stageManager = StageManager.getInstance(); + + Itinerary itinerary = stageManager.getSelectedItinerary(); + + try { + itinerary.addItem(toAdd); + } catch (DuplicateItemException e) { + throw new CommandException(MESSAGE_DUPLICATE_ITEM); + } + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddItemCommand // instanceof handles nulls + && toAdd.equals(((AddItemCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/waddle/logic/commands/ClearCommand.java similarity index 53% rename from src/main/java/seedu/address/logic/commands/ClearCommand.java rename to src/main/java/seedu/waddle/logic/commands/ClearCommand.java index 9c86b1fa6e4..a388d4c99a8 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/waddle/logic/commands/ClearCommand.java @@ -1,23 +1,23 @@ -package seedu.address.logic.commands; +package seedu.waddle.logic.commands; import static java.util.Objects.requireNonNull; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; +import seedu.waddle.model.Model; +import seedu.waddle.model.Waddle; /** - * Clears the address book. + * Clears Waddle. */ public class ClearCommand extends Command { public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; + public static final String MESSAGE_SUCCESS = "Waddle has been cleared!"; @Override public CommandResult execute(Model model) { requireNonNull(model); - model.setAddressBook(new AddressBook()); + model.setWaddle(new Waddle()); return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/waddle/logic/commands/Command.java similarity index 78% rename from src/main/java/seedu/address/logic/commands/Command.java rename to src/main/java/seedu/waddle/logic/commands/Command.java index 64f18992160..b1b47aa9091 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/waddle/logic/commands/Command.java @@ -1,7 +1,7 @@ -package seedu.address.logic.commands; +package seedu.waddle.logic.commands; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; /** * Represents a command with hidden internal logic and the ability to be executed. diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/waddle/logic/commands/CommandResult.java similarity index 63% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/seedu/waddle/logic/commands/CommandResult.java index 92f900b7916..7c4d4e7b5e3 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/waddle/logic/commands/CommandResult.java @@ -1,9 +1,11 @@ -package seedu.address.logic.commands; +package seedu.waddle.logic.commands; import static java.util.Objects.requireNonNull; import java.util.Objects; +import seedu.waddle.logic.Stages; + /** * Represents the result of a command execution. */ @@ -11,19 +13,40 @@ 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; + private final Stages stage; + /** * Constructs a {@code CommandResult} with the specified fields. */ - public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, Stages stage) { this.feedbackToUser = requireNonNull(feedbackToUser); this.showHelp = showHelp; this.exit = exit; + this.stage = stage; + } + + /** + * Constructs a {@code CommandResult} with the specified fields. + */ + public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + this(feedbackToUser, showHelp, exit, Stages.NONE); + } + + /** + * Constructs a {@code CommandResult} with the specified fields. + */ + public CommandResult(String feedbackToUser, Stages stage) { + this(feedbackToUser, false, false, stage); } /** @@ -31,7 +54,7 @@ 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, Stages.NONE); } public String getFeedbackToUser() { @@ -46,6 +69,14 @@ public boolean isExit() { return exit; } + public boolean hasStage() { + return !(this.stage == Stages.NONE); + } + + public Stages getStage() { + return this.stage; + } + @Override public boolean equals(Object other) { if (other == this) { diff --git a/src/main/java/seedu/waddle/logic/commands/CopyCommand.java b/src/main/java/seedu/waddle/logic/commands/CopyCommand.java new file mode 100644 index 00000000000..e106228ca2a --- /dev/null +++ b/src/main/java/seedu/waddle/logic/commands/CopyCommand.java @@ -0,0 +1,48 @@ +package seedu.waddle.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.core.Messages.MESSAGE_COPY_ERROR; + +import java.awt.AWTError; +import java.awt.HeadlessException; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; + +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * Copies the itinerary onto the user's clipboard. + */ +public class CopyCommand extends Command { + + public static final String COMMAND_WORD = "copy"; + + public static final String MESSAGE_SUCCESS = "%1$s copied to clipboard!"; + + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + Itinerary selectedItinerary = StageManager.getInstance().getSelectedItinerary(); + String itineraryText = selectedItinerary.getTextRepresentation(); + StringSelection stringSelection = new StringSelection(itineraryText); + try { + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(stringSelection, null); + } catch (IllegalStateException | AWTError | HeadlessException e) { + throw new CommandException(MESSAGE_COPY_ERROR); + } + + return new CommandResult(String.format(MESSAGE_SUCCESS, selectedItinerary.getDescription())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof CopyCommand); // instanceof handles nulls + } +} diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/waddle/logic/commands/DeleteCommand.java similarity index 51% rename from src/main/java/seedu/address/logic/commands/DeleteCommand.java rename to src/main/java/seedu/waddle/logic/commands/DeleteCommand.java index 02fd256acba..f517a007c36 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/waddle/logic/commands/DeleteCommand.java @@ -1,28 +1,28 @@ -package seedu.address.logic.commands; +package seedu.waddle.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; +import seedu.waddle.commons.core.Messages; +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.itinerary.Itinerary; /** - * Deletes a person identified using it's displayed index from the address book. + * Deletes an itinerary identified using it's displayed index. */ public class DeleteCommand extends Command { public static final String COMMAND_WORD = "delete"; public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Deletes the person identified by the index number used in the displayed person list.\n" - + "Parameters: INDEX (must be a positive integer)\n" + + ": Deletes the itinerary identified by the index number used in the displayed itinerary list. " + + "Parameters: INDEX (must exist in the itinerary list)\n" + "Example: " + COMMAND_WORD + " 1"; - public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; + public static final String MESSAGE_DELETE_ITINERARY_SUCCESS = "Deleted itinerary: %1$s"; private final Index targetIndex; @@ -33,15 +33,15 @@ public DeleteCommand(Index targetIndex) { @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); + List lastShownList = model.getFilteredItineraryList(); if (targetIndex.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + throw new CommandException(Messages.MESSAGE_INVALID_ITINERARY_DISPLAYED_INDEX); } - Person personToDelete = lastShownList.get(targetIndex.getZeroBased()); - model.deletePerson(personToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); + Itinerary itineraryToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deleteItinerary(itineraryToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_ITINERARY_SUCCESS, itineraryToDelete)); } @Override diff --git a/src/main/java/seedu/waddle/logic/commands/DeleteItemCommand.java b/src/main/java/seedu/waddle/logic/commands/DeleteItemCommand.java new file mode 100644 index 00000000000..4676695f73a --- /dev/null +++ b/src/main/java/seedu/waddle/logic/commands/DeleteItemCommand.java @@ -0,0 +1,78 @@ +package seedu.waddle.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.waddle.commons.core.Messages; +import seedu.waddle.commons.core.index.MultiIndex; +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.item.Day; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.itinerary.Itinerary; + + +/** + * Deletes an item from the itinerary. + */ +public class DeleteItemCommand extends Command { + + public static final String COMMAND_WORD = "delete"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the item identified by the index number used in the displayed item list. " + + "Parameters: " + + "Parameters: INDEX (must exist in the Wishlist or day list)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_ITEM_SUCCESS = "Deleted item: %1$s"; + + private final MultiIndex targetIndex; + + /** + * Creates an AddItemCommand to add the specified {@code Item} + */ + public DeleteItemCommand(MultiIndex targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + StageManager stageManager = StageManager.getInstance(); + + // if not at wish stage, throw exception and change to wish stage + /* + if (!stageManager.isCurrentStage(Stages.WISH)) { + return new CommandResult(MESSAGE_WRONG_STAGE); + } + + stageManager.setHomeStage(); + */ + Itinerary itinerary = stageManager.getSelectedItinerary(); + + if (targetIndex.getDayIndex() == null) { + if (targetIndex.getTaskIndex().getZeroBased() >= itinerary.getUnscheduledSize()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + } else { + if (targetIndex.getDayIndex().getZeroBased() >= itinerary.getDuration().getValue()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + Day day = itinerary.getDays().get(targetIndex.getDayIndex().getZeroBased()); + if (targetIndex.getTaskIndex().getZeroBased() >= day.getItemSize()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + } + Item itemToDelete = itinerary.removeItem(targetIndex); + return new CommandResult(String.format(MESSAGE_DELETE_ITEM_SUCCESS, itemToDelete)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteItemCommand // instanceof handles nulls + && targetIndex.equals(((DeleteItemCommand) other).targetIndex)); + } +} diff --git a/src/main/java/seedu/waddle/logic/commands/EditCommand.java b/src/main/java/seedu/waddle/logic/commands/EditCommand.java new file mode 100644 index 00000000000..ccbaf2d56ba --- /dev/null +++ b/src/main/java/seedu/waddle/logic/commands/EditCommand.java @@ -0,0 +1,242 @@ +package seedu.waddle.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_BUDGET; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_COUNTRY; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_ITINERARY_DURATION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_PEOPLE; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_START_DATE; +import static seedu.waddle.model.Model.PREDICATE_SHOW_ALL_ITINERARIES; + +import java.util.List; +import java.util.Optional; + +import seedu.waddle.commons.core.Messages; +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.commons.util.CollectionUtil; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.itinerary.Budget; +import seedu.waddle.model.itinerary.Country; +import seedu.waddle.model.itinerary.Date; +import seedu.waddle.model.itinerary.Description; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.model.itinerary.ItineraryDuration; +import seedu.waddle.model.itinerary.People; + +/** + * Edits the details of an existing itinerary. + */ +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 itinerary identified " + + "by the index number used in the displayed itinerary list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_DESCRIPTION + "NAME] " + + "[" + PREFIX_COUNTRY + "COUNTRY] " + + "[" + PREFIX_START_DATE + "START_DATE] " + + "[" + PREFIX_ITINERARY_DURATION + "DURATION] " + + "[" + PREFIX_PEOPLE + "NUMBER_OF_WADDLERS] " + + "[" + PREFIX_BUDGET + "BUDGET]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_COUNTRY + "Australia " + + PREFIX_START_DATE + "2025-07-30 "; + + public static final String MESSAGE_EDIT_ITINERARY_SUCCESS = "Edited Itinerary: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_ITINERARY = "This itinerary already exists."; + + private final Index index; + private final EditItineraryDescriptor editItineraryDescriptor; + + /** + * @param index of the person in the filtered person list to edit + * @param editItineraryDescriptor details to edit the person with + */ + public EditCommand(Index index, EditItineraryDescriptor editItineraryDescriptor) { + requireNonNull(index); + requireNonNull(editItineraryDescriptor); + + this.index = index; + this.editItineraryDescriptor = new EditItineraryDescriptor(editItineraryDescriptor); + } + + /** + * Creates and returns a {@code Person} with the details of {@code personToEdit} + * edited with {@code editPersonDescriptor}. + */ + private static Itinerary createEditedItinerary(Itinerary itineraryToEdit, + EditItineraryDescriptor editItineraryDescriptor) { + assert itineraryToEdit != null; + + Description updatedDescription = + editItineraryDescriptor.getDescription().orElse(itineraryToEdit.getDescription()); + Country updatedCountry = editItineraryDescriptor.getCountry().orElse(itineraryToEdit.getCountry()); + Date updatedStartDate = editItineraryDescriptor.getStartDate().orElse(itineraryToEdit.getStartDate()); + ItineraryDuration updatedDuration = editItineraryDescriptor.getDuration() + .orElse(itineraryToEdit.getDuration()); + People updatedPeople = editItineraryDescriptor.getPeople().orElse(itineraryToEdit.getPeople()); + Budget updatedBudget = editItineraryDescriptor.getBudget().orElse(itineraryToEdit.getBudget()); + + Itinerary editedItinerary = new Itinerary(updatedDescription, updatedCountry, updatedStartDate, updatedDuration, + updatedPeople, updatedBudget); + editedItinerary.setUnscheduledItems(itineraryToEdit.getUnscheduledItemList()); + editedItinerary.setDays(itineraryToEdit.getDays()); + editedItinerary.calculateSpending(); + return editedItinerary; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredItineraryList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITINERARY_DISPLAYED_INDEX); + } + + Itinerary itineraryToEdit = lastShownList.get(index.getZeroBased()); + Itinerary editedItinerary = createEditedItinerary(itineraryToEdit, editItineraryDescriptor); + + if (editedItinerary.calculateSpending() > editedItinerary.getBudget().getValue()) { + throw new CommandException(Messages.MESSAGE_ITINERARY_OVER_BUDGET); + } + + if (!itineraryToEdit.isSameItinerary(editedItinerary) && model.hasItinerary(editedItinerary)) { + throw new CommandException(MESSAGE_DUPLICATE_ITINERARY); + } + + model.setItinerary(itineraryToEdit, editedItinerary); + model.updateFilteredItineraryList(PREDICATE_SHOW_ALL_ITINERARIES); + return new CommandResult(String.format(MESSAGE_EDIT_ITINERARY_SUCCESS, editedItinerary)); + } + + @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) + && editItineraryDescriptor.equals(e.editItineraryDescriptor); + } + + /** + * 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 EditItineraryDescriptor { + private Description description; + private Country country; + private Date startDate; + private ItineraryDuration duration; + private People people; + private Budget budget; + + public EditItineraryDescriptor() { + } + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditItineraryDescriptor(EditItineraryDescriptor toCopy) { + setDescription(toCopy.description); + setCountry(toCopy.country); + setStartDate(toCopy.startDate); + setDuration(toCopy.duration); + setPeople(toCopy.people); + setBudget(toCopy.budget); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(description, country, startDate, duration, people, budget); + } + + public Optional getDescription() { + return Optional.ofNullable(description); + } + + public void setDescription(Description description) { + this.description = description; + } + + public Optional getCountry() { + return Optional.ofNullable(country); + } + + public void setCountry(Country country) { + this.country = country; + } + + public Optional getStartDate() { + return Optional.ofNullable(startDate); + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public Optional getDuration() { + return Optional.ofNullable(duration); + } + + public void setDuration(ItineraryDuration duration) { + this.duration = duration; + } + + public Optional getPeople() { + return Optional.ofNullable(people); + } + + public void setPeople(People people) { + this.people = people; + } + + public Optional getBudget() { + return Optional.ofNullable(budget); + } + + public void setBudget(Budget budget) { + this.budget = budget; + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditItineraryDescriptor)) { + return false; + } + + // state check + EditItineraryDescriptor e = (EditItineraryDescriptor) other; + + return getDescription().equals(e.getDescription()) + && getCountry().equals(e.getCountry()) + && getStartDate().equals(e.getStartDate()) + && getDuration().equals(e.getDuration()) + && getPeople().equals(e.getPeople()) + && getBudget().equals(e.getBudget()); + } + } +} diff --git a/src/main/java/seedu/waddle/logic/commands/EditItemCommand.java b/src/main/java/seedu/waddle/logic/commands/EditItemCommand.java new file mode 100644 index 00000000000..f5710d87779 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/commands/EditItemCommand.java @@ -0,0 +1,238 @@ +package seedu.waddle.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_COST; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_ITEM_DURATION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_PRIORITY; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_START_TIME; + +import java.time.LocalTime; +import java.util.Optional; + +import seedu.waddle.commons.core.Messages; +import seedu.waddle.commons.core.index.MultiIndex; +import seedu.waddle.commons.util.CollectionUtil; +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.item.Cost; +import seedu.waddle.model.item.Day; +import seedu.waddle.model.item.Duration; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.item.Priority; +import seedu.waddle.model.itinerary.Description; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * Edits the details of an existing item. + */ +public class EditItemCommand extends Command { + + public static final String COMMAND_WORD = "edit"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the item identified " + + "by the index number used in the displayed item list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must exist in the Wishlist or day list) " + + "[" + PREFIX_DESCRIPTION + "DESCRIPTION] " + + "[" + PREFIX_PRIORITY + "PRIORITY] " + + "[" + PREFIX_COST + "COST] " + + "[" + PREFIX_ITEM_DURATION + "DURATION] " + + "[" + PREFIX_START_TIME + "START_TIME]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_DESCRIPTION + "Visit the Eiffel Tower "; + + public static final String MESSAGE_EDIT_ITEM_SUCCESS = "Edited item: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_ITEM = "This item already exists."; + + public static final String MESSAGE_EDIT_START_TIME = "Editing start time of unscheduled item is not allowed"; + + private final MultiIndex multiIndex; + private final EditItemDescriptor editItemDescriptor; + + /** + * @param multiIndex of the item in the unique item list to edit + * @param editItemDescriptor details to edit the item with + */ + public EditItemCommand(MultiIndex multiIndex, EditItemDescriptor editItemDescriptor) { + requireNonNull(multiIndex); + requireNonNull(editItemDescriptor); + + this.multiIndex = multiIndex; + this.editItemDescriptor = new EditItemDescriptor(editItemDescriptor); + } + + /** + * Creates and returns an {@code Item} with the details of {@code itemToEdit} + * edited with {@code editItemDescriptor}. + */ + private static Item createEditedItem(Item itemToEdit, + EditItemDescriptor editItemDescriptor) { + assert itemToEdit != null; + + Description updatedDescription = editItemDescriptor.getDescription().orElse(itemToEdit.getDescription()); + Priority updatedPriority = editItemDescriptor.getPriority().orElse(itemToEdit.getPriority()); + Cost updatedCost = editItemDescriptor.getCost().orElse(itemToEdit.getCost()); + Duration updatedDuration = editItemDescriptor.getDuration().orElse(itemToEdit.getDuration()); + + Item editedItem = new Item(updatedDescription, updatedPriority, updatedCost, updatedDuration); + editedItem.setStartTime(editItemDescriptor.getStartTime().orElse(itemToEdit.getStartTime())); + + return editedItem; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + StageManager stageManager = StageManager.getInstance(); + Itinerary itinerary = stageManager.getSelectedItinerary(); + + if (multiIndex.getDayIndex() == null) { + if (multiIndex.getTaskIndex().getZeroBased() >= itinerary.getUnscheduledSize()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + Item itemToEdit = itinerary.getItem(multiIndex); + Item editedItem = createEditedItem(itemToEdit, editItemDescriptor); + if (!itemToEdit.isSameItem(editedItem) && itinerary.hasItem(editedItem)) { + throw new CommandException(MESSAGE_DUPLICATE_ITEM); + } + itinerary.setItem(itemToEdit, editedItem, multiIndex); + + return new CommandResult(String.format(MESSAGE_EDIT_ITEM_SUCCESS, editedItem)); + } else { + if (multiIndex.getDayIndex().getZeroBased() >= itinerary.getDuration().getValue()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + Day day = itinerary.getDays().get(multiIndex.getDayIndex().getZeroBased()); + if (multiIndex.getTaskIndex().getZeroBased() >= day.getItemSize()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + Item itemToEdit = itinerary.getItem(multiIndex); + Item editedItem = createEditedItem(itemToEdit, editItemDescriptor); + //if new cos causes over budget throw command exception + if (itinerary.getBudget().calculateLeftOverBudget() + + itemToEdit.getCost().getValue() - editedItem.getCost().getValue() < 0) { + throw new CommandException(Messages.MESSAGE_OVER_BUDGET); + } + + if (!itemToEdit.isSameItem(editedItem) && day.hasItem(editedItem)) { + throw new CommandException(MESSAGE_DUPLICATE_ITEM); + } + itinerary.setItem(itemToEdit, editedItem, multiIndex); + + return new CommandResult(String.format(MESSAGE_EDIT_ITEM_SUCCESS, editedItem)); + } + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditItemCommand)) { + return false; + } + + // state check + EditItemCommand e = (EditItemCommand) other; + return multiIndex.equals(e.multiIndex) + && editItemDescriptor.equals(e.editItemDescriptor); + } + + /** + * 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 EditItemDescriptor { + private Description description; + private Priority priority; + private Cost cost; + private Duration duration; + private LocalTime startTime; + + public EditItemDescriptor() { + } + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditItemDescriptor(EditItemDescriptor toCopy) { + setDescription(toCopy.description); + setPriority(toCopy.priority); + setCost(toCopy.cost); + setDuration(toCopy.duration); + setStartTime(toCopy.startTime); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(description, priority, cost, duration, startTime); + } + + public Optional getDescription() { + return Optional.ofNullable(description); + } + + public void setDescription(Description description) { + this.description = description; + } + + public Optional getPriority() { + return Optional.ofNullable(priority); + } + + public void setPriority(Priority priority) { + this.priority = priority; + } + + public Optional getCost() { + return Optional.ofNullable(cost); + } + + public void setCost(Cost cost) { + this.cost = cost; + } + + public Optional getDuration() { + return Optional.ofNullable(duration); + } + + public void setDuration(Duration duration) { + this.duration = duration; + } + + public Optional getStartTime() { + return Optional.ofNullable(startTime); + } + + public void setStartTime(LocalTime startTime) { + this.startTime = startTime; + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditItemDescriptor)) { + return false; + } + + // state check + EditItemDescriptor e = (EditItemDescriptor) other; + + return getDescription().equals(e.getDescription()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/waddle/logic/commands/ExitCommand.java similarity index 75% rename from src/main/java/seedu/address/logic/commands/ExitCommand.java rename to src/main/java/seedu/waddle/logic/commands/ExitCommand.java index 3dd85a8ba90..57d9f6a278a 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/waddle/logic/commands/ExitCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package seedu.waddle.logic.commands; -import seedu.address.model.Model; +import seedu.waddle.model.Model; /** * Terminates the program. @@ -9,7 +9,7 @@ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Waddle as requested ..."; @Override public CommandResult execute(Model model) { diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/waddle/logic/commands/FindCommand.java similarity index 64% rename from src/main/java/seedu/address/logic/commands/FindCommand.java rename to src/main/java/seedu/waddle/logic/commands/FindCommand.java index d6b19b0a0de..f9302e0294e 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/seedu/waddle/logic/commands/FindCommand.java @@ -1,23 +1,23 @@ -package seedu.address.logic.commands; +package seedu.waddle.logic.commands; import static java.util.Objects.requireNonNull; -import seedu.address.commons.core.Messages; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.waddle.commons.core.Messages; +import seedu.waddle.model.Model; +import seedu.waddle.model.itinerary.NameContainsKeywordsPredicate; /** - * Finds and lists all persons in address book whose name contains any of the argument keywords. + * Finds and lists all itineraries in Waddle 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 " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all itineraries 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"; + + "Example: " + COMMAND_WORD + " summer"; private final NameContainsKeywordsPredicate predicate; @@ -28,9 +28,9 @@ public FindCommand(NameContainsKeywordsPredicate predicate) { @Override public CommandResult execute(Model model) { requireNonNull(model); - model.updateFilteredPersonList(predicate); + model.updateFilteredItineraryList(predicate); return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); + String.format(Messages.MESSAGE_ITINERARIES_LISTED_OVERVIEW, model.getFilteredItineraryList().size())); } @Override diff --git a/src/main/java/seedu/waddle/logic/commands/FreeCommand.java b/src/main/java/seedu/waddle/logic/commands/FreeCommand.java new file mode 100644 index 00000000000..cd8dd0d0216 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/commands/FreeCommand.java @@ -0,0 +1,26 @@ +package seedu.waddle.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.waddle.logic.StageManager; +import seedu.waddle.model.Model; + +/** + * Lists all free timeslots in the itinerary to the user. + */ +public class FreeCommand extends Command { + + public static final String COMMAND_WORD = "free"; + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + return new CommandResult(StageManager.getInstance().getSelectedItinerary().getVacantSlots()); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FreeCommand); // instanceof handles nulls + } +} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/waddle/logic/commands/HelpCommand.java similarity index 77% rename from src/main/java/seedu/address/logic/commands/HelpCommand.java rename to src/main/java/seedu/waddle/logic/commands/HelpCommand.java index bf824f91bd0..da45def5527 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/waddle/logic/commands/HelpCommand.java @@ -1,9 +1,9 @@ -package seedu.address.logic.commands; +package seedu.waddle.logic.commands; -import seedu.address.model.Model; +import seedu.waddle.model.Model; /** - * Format full help instructions for every command for display. + * Opens the help window with a URL to the user guide. */ public class HelpCommand extends Command { diff --git a/src/main/java/seedu/waddle/logic/commands/HomeCommand.java b/src/main/java/seedu/waddle/logic/commands/HomeCommand.java new file mode 100755 index 00000000000..72073a5e33b --- /dev/null +++ b/src/main/java/seedu/waddle/logic/commands/HomeCommand.java @@ -0,0 +1,50 @@ +package seedu.waddle.logic.commands; + +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.Stages; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; + +/** + * Brings the user back to the main page of Waddle. + */ +public class HomeCommand extends Command { + + public static final String COMMAND_WORD = "home"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": returns to the home page\n" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_HOME_SUCCESS = "Waddled back to the home page"; + + public static final String MESSAGE_ALREADY_HOME_SUCCESS = "Already waddling in the home page"; + + @Override + public CommandResult execute(Model model) throws CommandException { + StageManager stageManager = StageManager.getInstance(); + + // if already at home page, do nothing and tell user + if (stageManager.isCurrentStage(Stages.HOME)) { + return new CommandResult(MESSAGE_ALREADY_HOME_SUCCESS); + } + + // change to home stage in stage manager + stageManager.setHomeStage(); + + // return command result with HOME stage change + return new CommandResult(MESSAGE_HOME_SUCCESS, Stages.HOME); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + return other instanceof HomeCommand; + + } +} diff --git a/src/main/java/seedu/waddle/logic/commands/ListCommand.java b/src/main/java/seedu/waddle/logic/commands/ListCommand.java new file mode 100644 index 00000000000..3b779a16289 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/commands/ListCommand.java @@ -0,0 +1,24 @@ +package seedu.waddle.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.model.Model.PREDICATE_SHOW_ALL_ITINERARIES; + +import seedu.waddle.model.Model; + +/** + * Lists all itineraries in Waddle to the user. + */ +public class ListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + + public static final String MESSAGE_SUCCESS = "Listed all itineraries"; + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredItineraryList(PREDICATE_SHOW_ALL_ITINERARIES); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/waddle/logic/commands/PdfCommand.java b/src/main/java/seedu/waddle/logic/commands/PdfCommand.java new file mode 100755 index 00000000000..4015237845b --- /dev/null +++ b/src/main/java/seedu/waddle/logic/commands/PdfCommand.java @@ -0,0 +1,60 @@ +package seedu.waddle.logic.commands; + +import java.io.IOException; + +import seedu.waddle.logic.PdfFiller; +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.Stages; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * Export an itinerary into PDF format. + */ +public class PdfCommand extends Command { + + public static final String COMMAND_WORD = "pdf"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": export current itinerary to PDF\n" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_EXPORT_SUCCESS = + "Created a pdf for %1$s! Find it under Waddle in your Documents folder!"; + public static final String MESSAGE_EXPORT_FAILURE = "Failed to export!"; + + public static final String MESSAGE_EXPORT_WRONG_STAGE = "Please select an itinerary before exporting."; + + @Override + public CommandResult execute(Model model) throws CommandException { + StageManager stageManager = StageManager.getInstance(); + + if (stageManager.isCurrentStage(Stages.HOME)) { + return new CommandResult(MESSAGE_EXPORT_WRONG_STAGE); + } + + Itinerary itinerary = stageManager.getSelectedItinerary(); + + try { + String pdfTemplate = "/template/waddle_template.pdf"; + PdfFiller pdfFiller = new PdfFiller(itinerary, pdfTemplate); + pdfFiller.fillItinerary(); + } catch (IOException e) { + return new CommandResult(MESSAGE_EXPORT_FAILURE); + } + return new CommandResult(String.format(MESSAGE_EXPORT_SUCCESS, itinerary.getDescription())); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + return other instanceof PdfCommand; + + } +} diff --git a/src/main/java/seedu/waddle/logic/commands/PlanCommand.java b/src/main/java/seedu/waddle/logic/commands/PlanCommand.java new file mode 100644 index 00000000000..386281a91bb --- /dev/null +++ b/src/main/java/seedu/waddle/logic/commands/PlanCommand.java @@ -0,0 +1,76 @@ +package seedu.waddle.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_DAY_NUMBER; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_START_TIME; + +import java.time.LocalTime; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.itinerary.DayNumber; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * Plans an item in the itinerary Wishlist, moving it to the specified day list. + */ +public class PlanCommand extends Command { + + public static final String COMMAND_WORD = "plan"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Schedules an item identified " + + "by the index number used in the item list.\n" + + "Parameters: INDEX (must exist in the wishlist) " + + PREFIX_DAY_NUMBER + "DAY_NUMBER " + + PREFIX_START_TIME + "START_TIME " + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_DAY_NUMBER + "1 " + + PREFIX_START_TIME + "12:00 "; + + public static final String MESSAGE_SUCCESS = "Item scheduled: %1$s"; + public static final String MESSAGE_INVALID_DAY_NUMBER = "The day you have selected does not exist"; + public static final String MESSAGE_INVALID_ITEM_NUMBER = "The item you have selected does not exist"; + + private final Index itemIndex; + private final DayNumber dayNumber; + private final LocalTime startTime; + + /** + * Creates a PlanCommand to add the specified {@code Item} + */ + public PlanCommand(Index itemIndex, DayNumber dayNumber, LocalTime startTime) { + requireNonNull(itemIndex); + requireNonNull(dayNumber); + requireNonNull(startTime); + + this.itemIndex = itemIndex; + this.dayNumber = dayNumber; + this.startTime = startTime; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + StageManager stageManager = StageManager.getInstance(); + + Itinerary itinerary = stageManager.getSelectedItinerary(); + + Item plannedItem; + + plannedItem = itinerary.planItem(itemIndex, dayNumber, startTime); + return new CommandResult(String.format(MESSAGE_SUCCESS, plannedItem.getDescription())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof PlanCommand // instanceof handles nulls + && itemIndex.equals(((PlanCommand) other).itemIndex) + && dayNumber.equals(((PlanCommand) other).dayNumber) + && startTime.equals(((PlanCommand) other).startTime)); + } +} diff --git a/src/main/java/seedu/waddle/logic/commands/SelectCommand.java b/src/main/java/seedu/waddle/logic/commands/SelectCommand.java new file mode 100644 index 00000000000..9ddd21efdc5 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/commands/SelectCommand.java @@ -0,0 +1,78 @@ +package seedu.waddle.logic.commands; + +import static seedu.waddle.commons.util.CollectionUtil.requireAllNonNull; + +import seedu.waddle.commons.core.Messages; +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.Stages; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * Moves to the planning stage of the selected itinerary. + */ +public class SelectCommand extends Command { + public static final String COMMAND_WORD = "select"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": selects an itinerary for planning " + + "by the index number used in the last itineraries listing.\n" + + "Parameters: INDEX (must exist in the itinerary list)\n" + + "Example: " + COMMAND_WORD + " 1 "; + public static final String MESSAGE_ARGUMENTS = "Index: %1$d"; + public static final String MESSAGE_SELECT_ITINERARY_SUCCESS = "Selected Itinerary: %1$s"; + private final Index index; + + /** + * @param index of the itinerary to select + */ + public SelectCommand(Index index) { + requireAllNonNull(index); + + this.index = index; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + StageManager stageManager = StageManager.getInstance(); + Itinerary selectedItinerary; + + // get the selected itinerary from the last shown list of itineraries + try { + selectedItinerary = model.getFilteredItineraryList().get(this.index.getZeroBased()); + } catch (IndexOutOfBoundsException e) { + throw new CommandException(Messages.MESSAGE_INVALID_ITINERARY_DISPLAYED_INDEX); + } + // change to wish stage in stage manager + try { + stageManager.setWishStage(selectedItinerary); + } catch (NullPointerException e) { + throw new CommandException(Messages.MESSAGE_INVALID_ITINERARY_DISPLAYED_INDEX); + } + + //TODO: allow users to directly select which planning stage + // instead of going to wish stage by default + + // return command result with stage change to wish by default for now (refer above) + return new CommandResult(String.format(MESSAGE_SELECT_ITINERARY_SUCCESS, selectedItinerary.getDescription()), + Stages.WISH); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof SelectCommand)) { + return false; + } + + // state check + SelectCommand e = (SelectCommand) other; + return index.equals(e.index); + } +} diff --git a/src/main/java/seedu/waddle/logic/commands/UnplanCommand.java b/src/main/java/seedu/waddle/logic/commands/UnplanCommand.java new file mode 100644 index 00000000000..1bc5891e719 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/commands/UnplanCommand.java @@ -0,0 +1,62 @@ +package seedu.waddle.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.waddle.commons.core.index.MultiIndex; +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * Unplans an item in the itinerary day list. + */ +public class UnplanCommand extends Command { + + public static final String COMMAND_WORD = "unplan"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Unschedules an item identified " + + "by the index number used in the day list.\n" + + "Parameters: DAY_INDEX.TASK_INDEX (must exist in the day list) " + + "Example: " + COMMAND_WORD + " 1.2 "; + + public static final String MESSAGE_SUCCESS = "Item unscheduled: %1$s"; + public static final String MESSAGE_INVALID_INDEX_NUMBER = "The index you have selected does not exist"; + + private final MultiIndex multiIndex; + + /** + * Creates an UnplanCommand to add the specified {@code Item} + */ + public UnplanCommand(MultiIndex multiIndex) { + requireNonNull(multiIndex); + + this.multiIndex = multiIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + StageManager stageManager = StageManager.getInstance(); + + Itinerary itinerary = stageManager.getSelectedItinerary(); + + Item unplannedItem; + try { + unplannedItem = itinerary.unplanItem(multiIndex); + } catch (IndexOutOfBoundsException | NullPointerException e) { + throw new CommandException(MESSAGE_INVALID_INDEX_NUMBER); + } + + return new CommandResult(String.format(MESSAGE_SUCCESS, unplannedItem.getDescription())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UnplanCommand // instanceof handles nulls + && multiIndex.equals(((UnplanCommand) other).multiIndex)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/waddle/logic/commands/exceptions/CommandException.java similarity index 89% rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java rename to src/main/java/seedu/waddle/logic/commands/exceptions/CommandException.java index a16bd14f2cd..568813e0f4d 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/seedu/waddle/logic/commands/exceptions/CommandException.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands.exceptions; +package seedu.waddle.logic.commands.exceptions; /** * Represents an error which occurs during execution of a {@link Command}. diff --git a/src/main/java/seedu/waddle/logic/parser/AddCommandParser.java b/src/main/java/seedu/waddle/logic/parser/AddCommandParser.java new file mode 100644 index 00000000000..c9aee5f7328 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/parser/AddCommandParser.java @@ -0,0 +1,82 @@ +package seedu.waddle.logic.parser; + +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_BUDGET; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_COUNTRY; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_ITINERARY_DURATION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_PEOPLE; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_START_DATE; + +import java.util.stream.Stream; + +import seedu.waddle.logic.commands.AddCommand; +import seedu.waddle.logic.parser.exceptions.ParseException; +import seedu.waddle.model.itinerary.Budget; +import seedu.waddle.model.itinerary.Country; +import seedu.waddle.model.itinerary.Date; +import seedu.waddle.model.itinerary.Description; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.model.itinerary.ItineraryDuration; +import seedu.waddle.model.itinerary.People; + +/** + * Parses input arguments and creates a new AddCommand object + */ +public class AddCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddCommand + * and returns an AddCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_DESCRIPTION, PREFIX_COUNTRY, PREFIX_START_DATE, + PREFIX_ITINERARY_DURATION, PREFIX_PEOPLE, PREFIX_BUDGET); + + if (!arePrefixesPresent(argMultimap, PREFIX_DESCRIPTION, PREFIX_START_DATE, PREFIX_ITINERARY_DURATION) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + + Description name = ParserUtil.parseDescription(argMultimap.getValue(PREFIX_DESCRIPTION).get()); + Date startDate = ParserUtil.parseDate(argMultimap.getValue(PREFIX_START_DATE).get()); + ItineraryDuration duration = ParserUtil.parseItineraryDuration( + argMultimap.getValue(PREFIX_ITINERARY_DURATION).get()); + + Country country; + if (arePrefixesPresent(argMultimap, PREFIX_COUNTRY)) { + country = ParserUtil.parseCountry(argMultimap.getValue(PREFIX_COUNTRY).get()); + } else { + country = ParserUtil.parseCountry("default"); + } + + People people; + if (arePrefixesPresent(argMultimap, PREFIX_PEOPLE)) { + people = ParserUtil.parsePeople(argMultimap.getValue(PREFIX_PEOPLE).get()); + } else { + people = ParserUtil.parsePeople("1"); + } + + Budget budget; + if (arePrefixesPresent(argMultimap, PREFIX_BUDGET)) { + budget = ParserUtil.parseBudget(argMultimap.getValue(PREFIX_BUDGET).get()); + } else { + budget = ParserUtil.parseBudget("0"); + } + + Itinerary itinerary = new Itinerary(name, country, startDate, duration, people, budget); + + return new AddCommand(itinerary); + } + + /** + * 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/waddle/logic/parser/AddItemCommandParser.java b/src/main/java/seedu/waddle/logic/parser/AddItemCommandParser.java new file mode 100644 index 00000000000..b06c9ee548e --- /dev/null +++ b/src/main/java/seedu/waddle/logic/parser/AddItemCommandParser.java @@ -0,0 +1,75 @@ +package seedu.waddle.logic.parser; + +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_COST; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_ITEM_DURATION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_PRIORITY; + +import java.util.stream.Stream; + +import seedu.waddle.logic.commands.AddItemCommand; +import seedu.waddle.logic.parser.exceptions.ParseException; +import seedu.waddle.model.item.Cost; +import seedu.waddle.model.item.Duration; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.item.Priority; +import seedu.waddle.model.itinerary.Description; + +/** + * Parses input arguments and creates a new AddItemCommand object + */ +public class AddItemCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the AddItemCommand + * and returns an AddItemCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddItemCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_DESCRIPTION, PREFIX_ITEM_DURATION, + PREFIX_PRIORITY, PREFIX_COST); + + if (!arePrefixesPresent(argMultimap, PREFIX_DESCRIPTION, PREFIX_ITEM_DURATION) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddItemCommand.MESSAGE_USAGE)); + } + + Description description = ParserUtil.parseDescription(argMultimap.getValue(PREFIX_DESCRIPTION).get()); + Priority priority; + + if (arePrefixesPresent(argMultimap, PREFIX_PRIORITY)) { + priority = ParserUtil.parsePriority(argMultimap.getValue(PREFIX_PRIORITY).get()); + } else { + priority = ParserUtil.parsePriority("1"); + } + + Cost cost; + if (arePrefixesPresent(argMultimap, PREFIX_COST)) { + cost = ParserUtil.parseCost(argMultimap.getValue(PREFIX_COST).get()); + } else { + cost = ParserUtil.parseCost("0"); + } + + Duration duration; + if (arePrefixesPresent(argMultimap, PREFIX_ITEM_DURATION)) { + duration = ParserUtil.parseDuration(argMultimap.getValue(PREFIX_ITEM_DURATION).get()); + } else { + duration = ParserUtil.parseDuration("0"); + } + + Item item = new Item(description, priority, cost, duration); + + return new AddItemCommand(item); + } + + /** + * 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/ArgumentMultimap.java b/src/main/java/seedu/waddle/logic/parser/ArgumentMultimap.java similarity index 98% rename from src/main/java/seedu/address/logic/parser/ArgumentMultimap.java rename to src/main/java/seedu/waddle/logic/parser/ArgumentMultimap.java index 954c8e18f8e..87b6cbec843 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/seedu/waddle/logic/parser/ArgumentMultimap.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.waddle.logic.parser; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/waddle/logic/parser/ArgumentTokenizer.java similarity index 99% rename from src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java rename to src/main/java/seedu/waddle/logic/parser/ArgumentTokenizer.java index 5c9aebfa488..3a5ace5a008 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/seedu/waddle/logic/parser/ArgumentTokenizer.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.waddle.logic.parser; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/seedu/waddle/logic/parser/CliSyntax.java b/src/main/java/seedu/waddle/logic/parser/CliSyntax.java new file mode 100644 index 00000000000..f501e50db50 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/parser/CliSyntax.java @@ -0,0 +1,21 @@ +package seedu.waddle.logic.parser; + +/** + * Contains Command Line Interface (CLI) syntax definitions common to multiple commands + */ +public class CliSyntax { + + /* Prefix definitions */ + public static final Prefix PREFIX_DESCRIPTION = new Prefix("d/"); + public static final Prefix PREFIX_COUNTRY = new Prefix("c/"); + public static final Prefix PREFIX_START_DATE = new Prefix("sd/"); + public static final Prefix PREFIX_START_TIME = new Prefix("st/"); + public static final Prefix PREFIX_ITINERARY_DURATION = new Prefix("du/"); + public static final Prefix PREFIX_PEOPLE = new Prefix("p/"); + public static final Prefix PREFIX_BUDGET = new Prefix("b/"); + public static final Prefix PREFIX_PRIORITY = new Prefix("p/"); + public static final Prefix PREFIX_COST = new Prefix("c/"); + public static final Prefix PREFIX_ITEM_DURATION = new Prefix("du/"); + public static final Prefix PREFIX_DAY_NUMBER = new Prefix("d/"); + +} diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/waddle/logic/parser/DeleteCommandParser.java similarity index 73% rename from src/main/java/seedu/address/logic/parser/DeleteCommandParser.java rename to src/main/java/seedu/waddle/logic/parser/DeleteCommandParser.java index 522b93081cc..e70a5971aec 100644 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ b/src/main/java/seedu/waddle/logic/parser/DeleteCommandParser.java @@ -1,10 +1,10 @@ -package seedu.address.logic.parser; +package seedu.waddle.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.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; +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.commands.DeleteCommand; +import seedu.waddle.logic.parser.exceptions.ParseException; /** * Parses input arguments and creates a new DeleteCommand object diff --git a/src/main/java/seedu/waddle/logic/parser/DeleteItemCommandParser.java b/src/main/java/seedu/waddle/logic/parser/DeleteItemCommandParser.java new file mode 100644 index 00000000000..87987684945 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/parser/DeleteItemCommandParser.java @@ -0,0 +1,31 @@ +package seedu.waddle.logic.parser; + +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.waddle.commons.core.index.MultiIndex; +import seedu.waddle.logic.commands.DeleteItemCommand; +import seedu.waddle.logic.parser.exceptions.ParseException; + + +/** + * Parses input arguments and creates a new AddItemCommand object + */ +public class DeleteItemCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the AddItemCommand + * and returns an AddItemCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteItemCommand parse(String args) throws ParseException { + try { + MultiIndex index = ParserUtil.parseMultiIndex(args); + return new DeleteItemCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteItemCommand.MESSAGE_USAGE), pe); + } + } + +} + diff --git a/src/main/java/seedu/waddle/logic/parser/EditCommandParser.java b/src/main/java/seedu/waddle/logic/parser/EditCommandParser.java new file mode 100644 index 00000000000..ec9c6469566 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/parser/EditCommandParser.java @@ -0,0 +1,70 @@ +package seedu.waddle.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_BUDGET; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_COUNTRY; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_ITINERARY_DURATION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_PEOPLE; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_START_DATE; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.commands.EditCommand; +import seedu.waddle.logic.parser.exceptions.ParseException; + +/** + * 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_DESCRIPTION, PREFIX_COUNTRY, PREFIX_START_DATE, + PREFIX_ITINERARY_DURATION, PREFIX_PEOPLE, PREFIX_BUDGET); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); + } + + EditCommand.EditItineraryDescriptor editItineraryDescriptor = new EditCommand.EditItineraryDescriptor(); + if (argMultimap.getValue(PREFIX_DESCRIPTION).isPresent()) { + editItineraryDescriptor.setDescription(ParserUtil.parseDescription( + argMultimap.getValue(PREFIX_DESCRIPTION).get())); + } + if (argMultimap.getValue(PREFIX_COUNTRY).isPresent()) { + editItineraryDescriptor.setCountry(ParserUtil.parseCountry(argMultimap.getValue(PREFIX_COUNTRY).get())); + } + if (argMultimap.getValue(PREFIX_START_DATE).isPresent()) { + editItineraryDescriptor.setStartDate(ParserUtil.parseDate(argMultimap.getValue(PREFIX_START_DATE).get())); + } + if (argMultimap.getValue(PREFIX_ITINERARY_DURATION).isPresent()) { + editItineraryDescriptor.setDuration(ParserUtil.parseItineraryDuration( + argMultimap.getValue(PREFIX_ITINERARY_DURATION).get())); + } + if (argMultimap.getValue(PREFIX_PEOPLE).isPresent()) { + editItineraryDescriptor.setPeople(ParserUtil.parsePeople(argMultimap.getValue(PREFIX_PEOPLE).get())); + } + if (argMultimap.getValue(PREFIX_BUDGET).isPresent()) { + editItineraryDescriptor.setBudget(ParserUtil.parseBudget(argMultimap.getValue(PREFIX_BUDGET).get())); + } + + + if (!editItineraryDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); + } + + return new EditCommand(index, editItineraryDescriptor); + } + +} diff --git a/src/main/java/seedu/waddle/logic/parser/EditItemCommandParser.java b/src/main/java/seedu/waddle/logic/parser/EditItemCommandParser.java new file mode 100644 index 00000000000..982939f2037 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/parser/EditItemCommandParser.java @@ -0,0 +1,76 @@ +package seedu.waddle.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_COST; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_ITEM_DURATION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_PRIORITY; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_START_TIME; + +import seedu.waddle.commons.core.index.MultiIndex; +import seedu.waddle.logic.commands.EditItemCommand; +import seedu.waddle.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EditItemCommand object + */ +public class EditItemCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditItemCommand + * and returns an EditItemCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditItemCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_DESCRIPTION, PREFIX_PRIORITY, + PREFIX_COST, PREFIX_ITEM_DURATION, PREFIX_START_TIME); + + MultiIndex multiIndex; + + try { + multiIndex = ParserUtil.parseMultiIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditItemCommand.MESSAGE_USAGE), pe); + } + + EditItemCommand.EditItemDescriptor editItemDescriptor = new EditItemCommand.EditItemDescriptor(); + if (argMultimap.getValue(PREFIX_DESCRIPTION).isPresent()) { + editItemDescriptor.setDescription( + ParserUtil.parseDescription(argMultimap.getValue(PREFIX_DESCRIPTION).get())); + } + + if (argMultimap.getValue(PREFIX_PRIORITY).isPresent()) { + editItemDescriptor.setPriority( + ParserUtil.parsePriority(argMultimap.getValue(PREFIX_PRIORITY).get())); + } + + if (argMultimap.getValue(PREFIX_COST).isPresent()) { + editItemDescriptor.setCost( + ParserUtil.parseCost(argMultimap.getValue(PREFIX_COST).get())); + } + + if (argMultimap.getValue(PREFIX_ITEM_DURATION).isPresent()) { + editItemDescriptor.setDuration( + ParserUtil.parseDuration(argMultimap.getValue(PREFIX_ITEM_DURATION).get())); + } + + if (argMultimap.getValue(PREFIX_START_TIME).isPresent()) { + if (multiIndex.getDayIndex() == null) { + throw new ParseException(EditItemCommand.MESSAGE_EDIT_START_TIME); + } + editItemDescriptor.setStartTime( + ParserUtil.parseStartTime(argMultimap.getValue(PREFIX_START_TIME).get())); + } + + if (!editItemDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditItemCommand.MESSAGE_NOT_EDITED); + } + + return new EditItemCommand(multiIndex, editItemDescriptor); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/waddle/logic/parser/FindCommandParser.java similarity index 74% rename from src/main/java/seedu/address/logic/parser/FindCommandParser.java rename to src/main/java/seedu/waddle/logic/parser/FindCommandParser.java index 4fb71f23103..66702488bc0 100644 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ b/src/main/java/seedu/waddle/logic/parser/FindCommandParser.java @@ -1,12 +1,12 @@ -package seedu.address.logic.parser; +package seedu.waddle.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import java.util.Arrays; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.waddle.logic.commands.FindCommand; +import seedu.waddle.logic.parser.exceptions.ParseException; +import seedu.waddle.model.itinerary.NameContainsKeywordsPredicate; /** * Parses input arguments and creates a new FindCommand object diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/waddle/logic/parser/Parser.java similarity index 72% rename from src/main/java/seedu/address/logic/parser/Parser.java rename to src/main/java/seedu/waddle/logic/parser/Parser.java index d6551ad8e3f..7e66f9a6ad5 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/seedu/waddle/logic/parser/Parser.java @@ -1,7 +1,7 @@ -package seedu.address.logic.parser; +package seedu.waddle.logic.parser; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.waddle.logic.commands.Command; +import seedu.waddle.logic.parser.exceptions.ParseException; /** * Represents a Parser that is able to parse user input into a {@code Command} of type {@code T}. diff --git a/src/main/java/seedu/waddle/logic/parser/ParserUtil.java b/src/main/java/seedu/waddle/logic/parser/ParserUtil.java new file mode 100644 index 00000000000..b2d80ae0946 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/parser/ParserUtil.java @@ -0,0 +1,242 @@ +package seedu.waddle.logic.parser; + +import static java.util.Objects.requireNonNull; + +import java.time.LocalTime; +import java.time.format.DateTimeParseException; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.commons.core.index.MultiIndex; +import seedu.waddle.commons.util.StringUtil; +import seedu.waddle.logic.parser.exceptions.ParseException; +import seedu.waddle.model.item.Cost; +import seedu.waddle.model.item.Duration; +import seedu.waddle.model.item.Priority; +import seedu.waddle.model.item.StartTime; +import seedu.waddle.model.itinerary.Budget; +import seedu.waddle.model.itinerary.Country; +import seedu.waddle.model.itinerary.Date; +import seedu.waddle.model.itinerary.DayNumber; +import seedu.waddle.model.itinerary.Description; +import seedu.waddle.model.itinerary.ItineraryDuration; +import seedu.waddle.model.itinerary.People; + + +/** + * 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 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 { + String trimmedIndex = oneBasedIndex.trim(); + if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { + throw new ParseException(MESSAGE_INVALID_INDEX); + } + Index index = Index.fromOneBased(Integer.parseInt(trimmedIndex)); + if (index.getZeroBased() < 0) { + throw new ParseException(MESSAGE_INVALID_INDEX); + } + return index; + } + + /** + * Parses {@code oneBasedMultiIndex} into an {@code MultiIndex} and returns it. Leading and trailing whitespaces + * will be trimmed. + * @throws ParseException if the specified MultiIndex is invalid (not non-zero unsigned integer). + */ + public static MultiIndex parseMultiIndex(String oneBasedMultiIndex) throws ParseException { + String trimmedMultiIndex = oneBasedMultiIndex.trim(); + if (!MultiIndex.isValidMultiIndex(trimmedMultiIndex)) { + throw new ParseException(MultiIndex.MESSAGE_CONSTRAINTS); + } + String[] oneBasedIndexList = trimmedMultiIndex.split("\\.", 2); + MultiIndex multiIndex = new MultiIndex(); + for (String oneBasedIndex : oneBasedIndexList) { + Index index = parseIndex(oneBasedIndex); + multiIndex.addIndex(index); + } + return multiIndex; + } + + + /** + * Parses a {@code String description} into a {@code Description}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code name} is invalid. + */ + public static Description parseDescription(String description) throws ParseException { + requireNonNull(description); + String trimmedDescription = description.trim(); + if (!Description.isValidDescription(trimmedDescription)) { + throw new ParseException(Description.MESSAGE_CONSTRAINTS); + } + return new Description(trimmedDescription); + } + + /** + * Parses a {@code String country} into a {@code Country}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code country} is invalid. + */ + public static Country parseCountry(String country) throws ParseException { + requireNonNull(country); + String trimmedCountry = country.trim(); + if (!Country.isValidCountry(trimmedCountry)) { + throw new ParseException(Country.MESSAGE_CONSTRAINTS); + } + return new Country(trimmedCountry); + } + + /** + * Parses a {@code String date} into an {@code Date}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code date} is invalid. + */ + public static Date parseDate(String date) throws ParseException { + requireNonNull(date); + String trimmedDate = date.trim(); + if (!Date.isValidDate(trimmedDate)) { + throw new ParseException(Date.MESSAGE_CONSTRAINTS); + } + return new Date(trimmedDate); + } + + /** + * Parses a {@code String duration} into an {@code ItineraryDuration}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code duration} is invalid. + */ + public static ItineraryDuration parseItineraryDuration(String duration) throws ParseException { + requireNonNull(duration); + String trimmedDuration = duration.trim(); + if (!ItineraryDuration.isValidDuration(trimmedDuration)) { + throw new ParseException(ItineraryDuration.MESSAGE_CONSTRAINTS); + } + return new ItineraryDuration(trimmedDuration); + } + + /** + * Parses a {@code String people} into an {@code People}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code people} is invalid. + */ + public static People parsePeople(String people) throws ParseException { + requireNonNull(people); + String trimmedPeople = people.trim(); + if (!People.isValidPeople(trimmedPeople)) { + throw new ParseException(People.MESSAGE_CONSTRAINTS); + } + return new People(trimmedPeople); + } + + /** + * Parses a {@code String budget} into an {@code Budget}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code budget} is invalid. + */ + public static Budget parseBudget(String budget) throws ParseException { + requireNonNull(budget); + String trimmedBudget = budget.trim(); + if (!Budget.isValidBudget(trimmedBudget)) { + throw new ParseException(Budget.MESSAGE_CONSTRAINTS); + } + return new Budget(trimmedBudget); + } + + + /** + * Parses a {@code String priority} into a {@code Priority}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code priority} is invalid. + */ + public static Priority parsePriority(String priority) throws ParseException { + requireNonNull(priority); + String trimmedPriority = priority.trim(); + int stars = 0; + try { + stars = Integer.parseInt(trimmedPriority); + } catch (NumberFormatException e) { + throw new ParseException(Priority.MESSAGE_CONSTRAINTS); + } + if (!Priority.isValidPriority(stars)) { + throw new ParseException(Priority.MESSAGE_CONSTRAINTS); + } + return new Priority(stars); + } + + /** + * Parses a {@code String cost} into a {@code Cost}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code cost} is invalid. + */ + public static Cost parseCost(String cost) throws ParseException { + requireNonNull(cost); + String trimmedCost = cost.trim(); + if (!Cost.isValidCost(trimmedCost)) { + throw new ParseException(Cost.MESSAGE_CONSTRAINTS); + } + return new Cost(trimmedCost); + } + + /** + * Parses a {@code String duration} into a {@code Duration}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code duration} is invalid. + */ + public static Duration parseDuration(String duration) throws ParseException { + requireNonNull(duration); + String trimmedDuration = duration.trim(); + if (!Duration.isValidDuration(trimmedDuration)) { + throw new ParseException(Duration.MESSAGE_CONSTRAINTS); + } + return new Duration(trimmedDuration); + } + + /** + * Parses a {@code int Day Number}. + * Leading and trailing whitespaces will be trimmed. + */ + public static DayNumber parseDayNumber(String dayNumber) throws ParseException { + requireNonNull(dayNumber); + String trimmedDayNumber = dayNumber.trim(); + if (!DayNumber.isValidDayNumber(trimmedDayNumber)) { + throw new ParseException(DayNumber.MESSAGE_CONSTRAINTS); + } + return new DayNumber(trimmedDayNumber); + } + + /** + * Parses a {@code String startTime} into a {@code StartTime}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code startTime} is invalid. + */ + public static LocalTime parseStartTime(String startTime) throws ParseException { + requireNonNull(startTime); + String trimmedStartTime = startTime.trim(); + LocalTime time; + try { + time = LocalTime.parse(trimmedStartTime); + } catch (DateTimeParseException e) { + throw new ParseException(StartTime.MESSAGE_CONSTRAINTS); + } + return time; + } + +} diff --git a/src/main/java/seedu/waddle/logic/parser/PlanCommandParser.java b/src/main/java/seedu/waddle/logic/parser/PlanCommandParser.java new file mode 100644 index 00000000000..b25a76d0c3a --- /dev/null +++ b/src/main/java/seedu/waddle/logic/parser/PlanCommandParser.java @@ -0,0 +1,57 @@ +package seedu.waddle.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_DAY_NUMBER; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_START_TIME; + +import java.time.LocalTime; +import java.util.stream.Stream; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.commands.PlanCommand; +import seedu.waddle.logic.parser.exceptions.ParseException; +import seedu.waddle.model.itinerary.DayNumber; + +/** + * Parses input arguments and creates a new PlanCommand object + */ +public class PlanCommandParser implements Parser { + + /** + * 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()); + } + + /** + * Parses the given {@code String} of arguments in the context of the PlanCommand + * and returns a PlanCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format + */ + public PlanCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_DAY_NUMBER, PREFIX_START_TIME); + + if (!arePrefixesPresent(argMultimap, PREFIX_DAY_NUMBER, PREFIX_START_TIME)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + PlanCommand.MESSAGE_USAGE)); + } + + Index index; + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, PlanCommand.MESSAGE_USAGE), pe); + } + + DayNumber dayNumber = ParserUtil.parseDayNumber(argMultimap.getValue(PREFIX_DAY_NUMBER).get()); + LocalTime startTime = ParserUtil.parseStartTime(argMultimap.getValue(PREFIX_START_TIME).get()); + + return new PlanCommand(index, dayNumber, startTime); + } +} diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/seedu/waddle/logic/parser/Prefix.java similarity index 95% rename from src/main/java/seedu/address/logic/parser/Prefix.java rename to src/main/java/seedu/waddle/logic/parser/Prefix.java index c859d5fa5db..cb091474be0 100644 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ b/src/main/java/seedu/waddle/logic/parser/Prefix.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.waddle.logic.parser; /** * A prefix that marks the beginning of an argument in an arguments string. diff --git a/src/main/java/seedu/waddle/logic/parser/SelectCommandParser.java b/src/main/java/seedu/waddle/logic/parser/SelectCommandParser.java new file mode 100644 index 00000000000..1858f5dd3a1 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/parser/SelectCommandParser.java @@ -0,0 +1,38 @@ +package seedu.waddle.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.commons.exceptions.IllegalValueException; +import seedu.waddle.logic.commands.SelectCommand; +import seedu.waddle.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new SelectCommand object + */ +public class SelectCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the SelectCommand + * and returns a SelectCommand object for execution. + * + * @param args Arguments + * @return SelectCommand + * @throws ParseException If the user input does not conform to the expected format + */ + public SelectCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args); + + Index index; + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (IllegalValueException ive) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + SelectCommand.MESSAGE_USAGE), ive); + } + + return new SelectCommand(index); + } +} + diff --git a/src/main/java/seedu/waddle/logic/parser/UnplanCommandParser.java b/src/main/java/seedu/waddle/logic/parser/UnplanCommandParser.java new file mode 100644 index 00000000000..92ca318d683 --- /dev/null +++ b/src/main/java/seedu/waddle/logic/parser/UnplanCommandParser.java @@ -0,0 +1,40 @@ +package seedu.waddle.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.waddle.commons.core.index.MultiIndex; +import seedu.waddle.commons.exceptions.IllegalValueException; +import seedu.waddle.logic.commands.UnplanCommand; +import seedu.waddle.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new UnplanCommand object + */ +public class UnplanCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the UnplanCommand + * and returns an UnplanCommand object for execution. + * @throws ParseException if the user input does not conform to the expected format + */ + public UnplanCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args); + + MultiIndex multiIndex; + try { + multiIndex = ParserUtil.parseMultiIndex(argMultimap.getPreamble()); + } catch (IllegalValueException ive) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + UnplanCommand.MESSAGE_USAGE), ive); + } + + if (!multiIndex.containsMultiIndex()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + UnplanCommand.MESSAGE_USAGE)); + } + + return new UnplanCommand(multiIndex); + } +} diff --git a/src/main/java/seedu/waddle/logic/parser/WaddleParser.java b/src/main/java/seedu/waddle/logic/parser/WaddleParser.java new file mode 100644 index 00000000000..d749bd5e17e --- /dev/null +++ b/src/main/java/seedu/waddle/logic/parser/WaddleParser.java @@ -0,0 +1,187 @@ +package seedu.waddle.logic.parser; + +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.commons.core.Messages.MESSAGE_UNAVAILABLE_COMMAND_HOME; +import static seedu.waddle.commons.core.Messages.MESSAGE_UNAVAILABLE_COMMAND_ITINERARY; +import static seedu.waddle.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.waddle.commons.core.Messages.MESSAGE_UNKNOWN_STAGE; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.Stages; +import seedu.waddle.logic.commands.AddCommand; +import seedu.waddle.logic.commands.AddItemCommand; +import seedu.waddle.logic.commands.ClearCommand; +import seedu.waddle.logic.commands.Command; +import seedu.waddle.logic.commands.CopyCommand; +import seedu.waddle.logic.commands.DeleteCommand; +import seedu.waddle.logic.commands.DeleteItemCommand; +import seedu.waddle.logic.commands.EditCommand; +import seedu.waddle.logic.commands.EditItemCommand; +import seedu.waddle.logic.commands.ExitCommand; +import seedu.waddle.logic.commands.FindCommand; +import seedu.waddle.logic.commands.FreeCommand; +import seedu.waddle.logic.commands.HelpCommand; +import seedu.waddle.logic.commands.HomeCommand; +import seedu.waddle.logic.commands.ListCommand; +import seedu.waddle.logic.commands.PdfCommand; +import seedu.waddle.logic.commands.PlanCommand; +import seedu.waddle.logic.commands.SelectCommand; +import seedu.waddle.logic.commands.UnplanCommand; +import seedu.waddle.logic.parser.exceptions.ParseException; + +/** + * Parses user input. + */ +public class WaddleParser { + + /** + * 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"); + + Stages currStage = StageManager.getInstance().getCurrentStage(); + + switch (currStage) { + case HOME: + return parseHomeCommand(commandWord, arguments); + case WISH: + return parseWishCommand(commandWord, arguments); + default: + throw new ParseException(MESSAGE_UNKNOWN_STAGE); + } + } + + /** + * Parses user input into command for execution. + * + * @param commandWord The command word. + * @param arguments The arguments. + * @return The command. + * @throws ParseException ParseException. + */ + public Command parseHomeCommand(String commandWord, String arguments) throws ParseException { + 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 SelectCommand.COMMAND_WORD: + return new SelectCommandParser().parse(arguments); + + case HomeCommand.COMMAND_WORD: + return new HomeCommand(); + + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); + + case PlanCommand.COMMAND_WORD: + + case UnplanCommand.COMMAND_WORD: + + case FreeCommand.COMMAND_WORD: + + case PdfCommand.COMMAND_WORD: + + case CopyCommand.COMMAND_WORD: + throw new ParseException(MESSAGE_UNAVAILABLE_COMMAND_HOME); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } + + /** + * Parses user input into command for execution. + * + * @param commandWord The command word. + * @param arguments The arguments. + * @return The command. + * @throws ParseException ParseException. + */ + public Command parseWishCommand(String commandWord, String arguments) throws ParseException { + switch (commandWord) { + + case HomeCommand.COMMAND_WORD: + return new HomeCommand(); + + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + + case FreeCommand.COMMAND_WORD: + return new FreeCommand(); + + case AddItemCommand.COMMAND_WORD: + return new AddItemCommandParser().parse(arguments); + + case EditItemCommand.COMMAND_WORD: + return new EditItemCommandParser().parse(arguments); + + case DeleteItemCommand.COMMAND_WORD: + return new DeleteItemCommandParser().parse(arguments); + + case PlanCommand.COMMAND_WORD: + return new PlanCommandParser().parse(arguments); + + case UnplanCommand.COMMAND_WORD: + return new UnplanCommandParser().parse(arguments); + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); + + case PdfCommand.COMMAND_WORD: + return new PdfCommand(); + + case CopyCommand.COMMAND_WORD: + return new CopyCommand(); + + case ClearCommand.COMMAND_WORD: + + case ListCommand.COMMAND_WORD: + + case FindCommand.COMMAND_WORD: + + case SelectCommand.COMMAND_WORD: + throw new ParseException(MESSAGE_UNAVAILABLE_COMMAND_ITINERARY); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/seedu/waddle/logic/parser/exceptions/ParseException.java similarity index 73% rename from src/main/java/seedu/address/logic/parser/exceptions/ParseException.java rename to src/main/java/seedu/waddle/logic/parser/exceptions/ParseException.java index 158a1a54c1c..4b543b8d84d 100644 --- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java +++ b/src/main/java/seedu/waddle/logic/parser/exceptions/ParseException.java @@ -1,6 +1,6 @@ -package seedu.address.logic.parser.exceptions; +package seedu.waddle.logic.parser.exceptions; -import seedu.address.commons.exceptions.IllegalValueException; +import seedu.waddle.commons.exceptions.IllegalValueException; /** * Represents a parse error encountered by a parser. diff --git a/src/main/java/seedu/waddle/model/Model.java b/src/main/java/seedu/waddle/model/Model.java new file mode 100644 index 00000000000..1376a335735 --- /dev/null +++ b/src/main/java/seedu/waddle/model/Model.java @@ -0,0 +1,88 @@ +package seedu.waddle.model; + +import java.nio.file.Path; +import java.util.function.Predicate; + +import javafx.collections.ObservableList; +import seedu.waddle.commons.core.GuiSettings; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * The API of the Model component. + */ +public interface Model { + /** {@code Predicate} that always evaluate to true */ + Predicate PREDICATE_SHOW_ALL_ITINERARIES = unused -> true; + + /** + * Replaces user prefs data with the data in {@code userPrefs}. + */ + void setUserPrefs(ReadOnlyUserPrefs userPrefs); + + /** + * Returns the user prefs. + */ + ReadOnlyUserPrefs getUserPrefs(); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Sets the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); + + /** + * Returns the user prefs' Waddle file path. + */ + Path getWaddleFilePath(); + + /** + * Sets the user prefs' Waddle file path. + */ + void setWaddleFilePath(Path waddleFilePath); + + /** + * Replaces Waddle data with the data in {@code waddle}. + */ + void setWaddle(ReadOnlyWaddle waddle); + + /** Returns Waddle */ + ReadOnlyWaddle getWaddle(); + + /** + * Returns true if a itinerary with the same identity as {@code itinerary} exists in Waddle. + */ + boolean hasItinerary(Itinerary itinerary); + + /** + * Deletes the given itinerary. + * The itinerary must exist in Waddle. + */ + void deleteItinerary(Itinerary target); + + /** + * Adds the given itinerary. + * {@code itinerary} must not already exist in Waddle. + */ + void addItinerary(Itinerary itinerary); + + /** + * Replaces the given itinerary {@code target} with {@code editedItinerary}. + * {@code target} must exist in Waddle. + * The itinerary identity of {@code editedItinerary} must not be the same as + * another existing itinerary in Waddle + */ + void setItinerary(Itinerary target, Itinerary editedItinerary); + + /** Returns an unmodifiable view of the filtered itinerary list */ + ObservableList getFilteredItineraryList(); + + /** + * Updates the filter of the filtered itinerary list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredItineraryList(Predicate predicate); +} diff --git a/src/main/java/seedu/waddle/model/ModelManager.java b/src/main/java/seedu/waddle/model/ModelManager.java new file mode 100644 index 00000000000..a293b791d9c --- /dev/null +++ b/src/main/java/seedu/waddle/model/ModelManager.java @@ -0,0 +1,150 @@ +package seedu.waddle.model; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.util.CollectionUtil.requireAllNonNull; + +import java.nio.file.Path; +import java.util.function.Predicate; +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import seedu.waddle.commons.core.GuiSettings; +import seedu.waddle.commons.core.LogsCenter; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * Represents the in-memory model of Waddle data. + */ +public class ModelManager implements Model { + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + + private final Waddle waddle; + private final UserPrefs userPrefs; + private final FilteredList filteredItineraries; + + /** + * Initializes a ModelManager with the given waddle and userPrefs. + */ + public ModelManager(ReadOnlyWaddle waddle, ReadOnlyUserPrefs userPrefs) { + requireAllNonNull(waddle, userPrefs); + + logger.fine("Initializing with Waddle: " + waddle + " and user prefs " + userPrefs); + + this.waddle = new Waddle(waddle); + this.userPrefs = new UserPrefs(userPrefs); + filteredItineraries = new FilteredList<>(this.waddle.getItineraryList()); + } + + public ModelManager() { + this(new Waddle(), new UserPrefs()); + } + + //=========== UserPrefs ================================================================================== + + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + requireNonNull(userPrefs); + this.userPrefs.resetData(userPrefs); + } + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + return userPrefs; + } + + @Override + public GuiSettings getGuiSettings() { + return userPrefs.getGuiSettings(); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + requireNonNull(guiSettings); + userPrefs.setGuiSettings(guiSettings); + } + + @Override + public Path getWaddleFilePath() { + return userPrefs.getWaddleFilePath(); + } + + @Override + public void setWaddleFilePath(Path waddleFilePath) { + requireNonNull(waddleFilePath); + userPrefs.setWaddleFilePath(waddleFilePath); + } + + //=========== Waddle ================================================================================ + + @Override + public void setWaddle(ReadOnlyWaddle waddle) { + this.waddle.resetData(waddle); + } + + @Override + public ReadOnlyWaddle getWaddle() { + return waddle; + } + + @Override + public boolean hasItinerary(Itinerary itinerary) { + requireNonNull(itinerary); + return waddle.hasItinerary(itinerary); + } + + @Override + public void deleteItinerary(Itinerary target) { + waddle.removeItinerary(target); + } + + @Override + public void addItinerary(Itinerary itinerary) { + waddle.addItinerary(itinerary); + updateFilteredItineraryList(PREDICATE_SHOW_ALL_ITINERARIES); + } + + @Override + public void setItinerary(Itinerary target, Itinerary editedItinerary) { + requireAllNonNull(target, editedItinerary); + + waddle.setItinerary(target, editedItinerary); + } + + //=========== Filtered Itinerary List Accessors ============================================================= + + /** + * Returns an unmodifiable view of the list of {@code Itinerary} backed by the internal list of + * {@code versionedWaddle} + */ + @Override + public ObservableList getFilteredItineraryList() { + return filteredItineraries; + } + + @Override + public void updateFilteredItineraryList(Predicate predicate) { + requireNonNull(predicate); + filteredItineraries.setPredicate(predicate); + } + + @Override + public boolean equals(Object obj) { + // short circuit if same object + if (obj == this) { + return true; + } + + // instanceof handles nulls + if (!(obj instanceof ModelManager)) { + return false; + } + + // state check + ModelManager other = (ModelManager) obj; + return waddle.equals(other.waddle) + && userPrefs.equals(other.userPrefs) + && filteredItineraries.equals(other.filteredItineraries); + } + +} diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/waddle/model/ReadOnlyUserPrefs.java similarity index 57% rename from src/main/java/seedu/address/model/ReadOnlyUserPrefs.java rename to src/main/java/seedu/waddle/model/ReadOnlyUserPrefs.java index befd58a4c73..d8c7ba29e01 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/seedu/waddle/model/ReadOnlyUserPrefs.java @@ -1,8 +1,8 @@ -package seedu.address.model; +package seedu.waddle.model; import java.nio.file.Path; -import seedu.address.commons.core.GuiSettings; +import seedu.waddle.commons.core.GuiSettings; /** * Unmodifiable view of user prefs. @@ -11,6 +11,6 @@ public interface ReadOnlyUserPrefs { GuiSettings getGuiSettings(); - Path getAddressBookFilePath(); + Path getWaddleFilePath(); } diff --git a/src/main/java/seedu/waddle/model/ReadOnlyWaddle.java b/src/main/java/seedu/waddle/model/ReadOnlyWaddle.java new file mode 100644 index 00000000000..2857c9fe227 --- /dev/null +++ b/src/main/java/seedu/waddle/model/ReadOnlyWaddle.java @@ -0,0 +1,17 @@ +package seedu.waddle.model; + +import javafx.collections.ObservableList; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * Unmodifiable view of a Waddle. + */ +public interface ReadOnlyWaddle { + + /** + * Returns an unmodifiable view of the itinerary list. + * This list will not contain any duplicate itineraries. + */ + ObservableList getItineraryList(); + +} diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/waddle/model/UserPrefs.java similarity index 70% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/seedu/waddle/model/UserPrefs.java index 25a5fd6eab9..4a1d38175c9 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/waddle/model/UserPrefs.java @@ -1,4 +1,4 @@ -package seedu.address.model; +package seedu.waddle.model; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.nio.file.Paths; import java.util.Objects; -import seedu.address.commons.core.GuiSettings; +import seedu.waddle.commons.core.GuiSettings; /** * Represents User's preferences. @@ -14,7 +14,7 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path waddleFilePath = Paths.get("data" , "waddle.json"); /** * Creates a {@code UserPrefs} with default values. @@ -35,7 +35,7 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) { public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); - setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setWaddleFilePath(newUserPrefs.getWaddleFilePath()); } public GuiSettings getGuiSettings() { @@ -47,13 +47,13 @@ public void setGuiSettings(GuiSettings guiSettings) { this.guiSettings = guiSettings; } - public Path getAddressBookFilePath() { - return addressBookFilePath; + public Path getWaddleFilePath() { + return waddleFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - this.addressBookFilePath = addressBookFilePath; + public void setWaddleFilePath(Path waddleFilePath) { + requireNonNull(waddleFilePath); + this.waddleFilePath = waddleFilePath; } @Override @@ -68,19 +68,19 @@ public boolean equals(Object other) { UserPrefs o = (UserPrefs) other; return guiSettings.equals(o.guiSettings) - && addressBookFilePath.equals(o.addressBookFilePath); + && waddleFilePath.equals(o.waddleFilePath); } @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, waddleFilePath); } @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 : " + waddleFilePath); return sb.toString(); } diff --git a/src/main/java/seedu/waddle/model/Waddle.java b/src/main/java/seedu/waddle/model/Waddle.java new file mode 100644 index 00000000000..896bdc89abb --- /dev/null +++ b/src/main/java/seedu/waddle/model/Waddle.java @@ -0,0 +1,121 @@ +package seedu.waddle.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.model.itinerary.UniqueItineraryList; + +/** + * Wraps all data at the Waddle level + * Duplicates are not allowed (by .isSameItinerary comparison) + */ +public class Waddle implements ReadOnlyWaddle { + + private final UniqueItineraryList itineraries; + + /* + * 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. + */ + { + itineraries = new UniqueItineraryList(); + } + + public Waddle() {} + + /** + * Creates a Waddle using the Itineraries in the {@code toBeCopied} + */ + public Waddle(ReadOnlyWaddle toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// list overwrite operations + + /** + * Replaces the contents of the itinerary list with {@code itineraries}. + * {@code itineraries} must not contain duplicate itineraries. + */ + public void setItineraries(List itineraries) { + this.itineraries.setItineraries(itineraries); + } + + /** + * Resets the existing data of this {@code Waddle} with {@code newData}. + */ + public void resetData(ReadOnlyWaddle newData) { + requireNonNull(newData); + + setItineraries(newData.getItineraryList()); + } + + //// itinerary-level operations + + /** + * Returns true if an itinerary with the same identity as {@code itinerary} exists in Waddle. + */ + public boolean hasItinerary(Itinerary itinerary) { + requireNonNull(itinerary); + return itineraries.contains(itinerary); + } + + /** + * Adds an Itinerary to Waddle. + * The itinerary must not already exist in Waddle. + */ + public void addItinerary(Itinerary p) { + itineraries.add(p); + } + + /** + * Replaces the given itinerary {@code target} in the list with {@code editedItinerary}. + * {@code target} must exist in Waddle. + * The itinerary identity of {@code editedItinerary} must not be the same as + * another existing itinerary in Waddle. + */ + public void setItinerary(Itinerary target, Itinerary editedItinerary) { + requireNonNull(editedItinerary); + + itineraries.setItinerary(target, editedItinerary); + } + + /** + * Removes {@code key} from this {@code Waddle}. + * {@code key} must exist in Waddle. + */ + public void removeItinerary(Itinerary key) { + itineraries.remove(key); + } + + //// util methods + + @Override + public String toString() { + return itineraries.asUnmodifiableObservableList().size() + " itineraries"; + // TODO: refine later + } + + @Override + public ObservableList getItineraryList() { + return itineraries.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Waddle // instanceof handles nulls + && itineraries.equals(((Waddle) other).itineraries)); + } + + @Override + public int hashCode() { + return itineraries.hashCode(); + } +} diff --git a/src/main/java/seedu/waddle/model/item/Cost.java b/src/main/java/seedu/waddle/model/item/Cost.java new file mode 100644 index 00000000000..6d8d18a394b --- /dev/null +++ b/src/main/java/seedu/waddle/model/item/Cost.java @@ -0,0 +1,61 @@ +package seedu.waddle.model.item; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.util.AppUtil.checkArgument; + +/** + * Represents the estimated cost of an Item in the Itinerary. + */ +public class Cost { + public static final String MESSAGE_CONSTRAINTS = + "Cost must be a value from $0 to $1,000,000."; + private final float cost; + + /** + * Constructs a {@code Cost}. + * + * @param cost A valid cost. + */ + public Cost(String cost) { + requireNonNull(cost); + checkArgument(isValidCost(cost), MESSAGE_CONSTRAINTS); + // round off the decimal to 2dp (money) + this.cost = Math.round(Float.parseFloat(cost) * 100.0F) / 100.0F; + } + + /** + * Returns true if a given string is a valid Cost + */ + public static boolean isValidCost(String test) { + float value; + try { + value = Float.parseFloat(test); + } catch (NumberFormatException e) { + return false; + } + return value >= 0 && value <= 1000000; + } + + public float getValue() { + return this.cost; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Cost)) { + return false; + } + + Cost otherCost = (Cost) other; + return this.cost == otherCost.cost; + } + + @Override + public String toString() { + return String.valueOf(cost); + } +} diff --git a/src/main/java/seedu/waddle/model/item/Day.java b/src/main/java/seedu/waddle/model/item/Day.java new file mode 100644 index 00000000000..d6e3e9f2ade --- /dev/null +++ b/src/main/java/seedu/waddle/model/item/Day.java @@ -0,0 +1,217 @@ +package seedu.waddle.model.item; + +import static seedu.waddle.commons.core.Messages.MESSAGE_CONFLICTING_ITEMS; +import static seedu.waddle.commons.core.Messages.MESSAGE_ITEM_PAST_MIDNIGHT; + +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Optional; + +import seedu.waddle.commons.core.Text; +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.item.exceptions.Period; + +/** + * Encapsulates a day in an itinerary. + */ +public class Day { + private final Comparator startTimeComparator = new Comparator() { + @Override + public int compare(Item item1, Item item2) { + return item1.getStartTime().compareTo(item2.getStartTime()); + } + }; + private final int dayNumber; + private final UniqueItemList itemList; + + /** + * Constructor. + * + * @param dayNumber The day number. + */ + public Day(int dayNumber) { + this.dayNumber = dayNumber; + this.itemList = new UniqueItemList(); + } + + /** + * Adds an item to this day if there are no time conflicts. + * + * @param item The item to be added. + * @throws CommandException Conflicting items message thrown if there are time conflicts. + */ + public void addItem(Item item) throws CommandException { + Optional> conflictingItems = getConflictingItems(item); + if (conflictingItems.isEmpty()) { + throw new CommandException(String.format(MESSAGE_ITEM_PAST_MIDNIGHT, item.getDescription())); + } + StringBuilder conflicts = new StringBuilder(); + if (!conflictingItems.get().isEmpty()) { + for (Item cItem : conflictingItems.get()) { + conflicts.append(" ").append(cItem.getDescription()).append(": ").append(cItem.getStartTime()) + .append(" - ").append(cItem.getEndTime()).append("\n"); + } + throw new CommandException(String.format(MESSAGE_CONFLICTING_ITEMS, conflicts)); + } + this.itemList.add(item); + this.itemList.sort(startTimeComparator); + } + + /** + * Removes an item from this day. Resets the item's startTime field. + * + * @param index The index of the item to be removed. + * @return The removed item. + */ + public Item removeItem(Index index) { + Item removedItem = this.itemList.remove(index.getZeroBased()); + //removedItem.resetStartTime(); + return removedItem; + } + + public Item getItem(Index index) { + return this.itemList.get(index.getZeroBased()); + } + + /** + * Deletes the day. Resets the startTime field of all items in this day. + * + * @return The list of items stored in this day. + */ + public UniqueItemList deleteDay() { + for (Item item : this.itemList) { + item.resetStartTime(); + } + return this.itemList; + } + + /** + * For a given item, return an Optional list of items that conflict in time. + * An Optional with an empty list is returned if there are no conflicts. + * If the item goes past midnight (not allowed), an empty Optional is returned. + * If there are conflicting items, an Optional with the list of conflicting items are returned. + * + * @param newItem The item to check for. + * @return A list of conflicting items, possibly an empty list. + */ + private Optional> getConflictingItems(Item newItem) { + ArrayList conflictingItems = new ArrayList<>(); + // item goes past midnight and overflows + if (newItem.getEndTime().isBefore(newItem.getStartTime()) + || newItem.getEndTime().equals(newItem.getStartTime())) { + return Optional.empty(); + } + // check for conflicting items + for (Item item : this.itemList) { + // same start time + boolean sameStartTime = item.getStartTime().equals(newItem.getStartTime()); + // if new start time is before item start time + // conflict if new end time is after item start time + boolean startTimeConflict = newItem.getStartTime().isBefore(item.getStartTime()) + && newItem.getEndTime().isAfter(item.getStartTime()); + // if new start time is after item start time + // conflict if new start time is before item end time + boolean endTimeConflict = newItem.getStartTime().isAfter(item.getStartTime()) + && newItem.getStartTime().isBefore(item.getEndTime()); + + if (sameStartTime || startTimeConflict || endTimeConflict) { + conflictingItems.add(item); + } + } + return Optional.of(conflictingItems); + } + + public int getItemSize() { + return itemList.getSize(); + } + + public boolean hasItem(Item item) { + return this.itemList.contains(item); + } + + public UniqueItemList getItemList() { + return this.itemList; + } + + public int getDayNumber() { + return this.dayNumber; + } + + /** + * Compiles the vacant time slots in this day and formats it as a string. + * + * @return The vacant slots as a string. + */ + public String getVacantSlots() { + if (this.itemList.getSize() == 0) { + return "Day " + (this.dayNumber + 1) + ":" + System.lineSeparator() + + " Free!" + System.lineSeparator(); + } + StringBuilder vacantSlots = new StringBuilder("Day "); + vacantSlots.append((this.dayNumber + 1)).append(":").append(System.lineSeparator()); + + ArrayList vacantPeriods = new ArrayList<>(); + Period toBeSplit = new Period(LocalTime.MIN, LocalTime.MAX); + for (Item item : this.itemList) { + vacantPeriods.addAll(splitTimeSlot(toBeSplit, new Period(item.getStartTime(), item.getEndTime()))); + if (vacantPeriods.size() > 0) { + // remove the last period to continue splitting + toBeSplit = vacantPeriods.remove(vacantPeriods.size() - 1); + } else { + toBeSplit = null; + break; + } + } + // add the last period back if there is any + if (toBeSplit != null) { + vacantPeriods.add(toBeSplit); + } + for (Period period : vacantPeriods) { + vacantSlots.append(" ").append(period.getStartString()).append(" - ") + .append(period.getEndString()).append(System.lineSeparator()); + } + + return vacantSlots.toString(); + } + + private ArrayList splitTimeSlot(Period big, Period small) { + ArrayList splitPeriods = new ArrayList<>(); + if (big.getStart().equals(small.getStart()) && big.getEnd().equals(small.getEnd())) { + return splitPeriods; + } else if (big.getStart().equals(small.getStart())) { + splitPeriods.add(new Period(small.getEnd(), big.getEnd())); + } else if (big.getEnd().equals(small.getEnd())) { + splitPeriods.add(new Period(big.getStart(), small.getStart())); + } else if (small.getStart().isAfter(big.getStart()) && big.getEnd().isAfter(small.getEnd())) { + splitPeriods.add(new Period(big.getStart(), small.getStart())); + splitPeriods.add(new Period(small.getEnd(), big.getEnd())); + } + return splitPeriods; + } + + /** + * Generates a text representation of the day. + * + * @return The text representation. + */ + public String getTextRepresentation() { + StringBuilder dayText = new StringBuilder(); + dayText.append("Day ").append((this.dayNumber + 1)).append(System.lineSeparator()); + StringBuilder itemsText = new StringBuilder(); + int itemCount = 1; + for (Item item : this.itemList) { + itemsText.append(itemCount).append(". ").append(item.toString()) + .append(System.lineSeparator()); + if (itemCount < this.itemList.getSize()) { + itemsText.append(System.lineSeparator()); + } + itemCount++; + } + dayText.append(Text.indent(itemsText.toString(), Text.INDENT_FOUR)) + .append(System.lineSeparator()); + + return dayText.toString(); + } +} diff --git a/src/main/java/seedu/waddle/model/item/Duration.java b/src/main/java/seedu/waddle/model/item/Duration.java new file mode 100644 index 00000000000..db88be2cd60 --- /dev/null +++ b/src/main/java/seedu/waddle/model/item/Duration.java @@ -0,0 +1,61 @@ +package seedu.waddle.model.item; + +import static seedu.waddle.commons.util.AppUtil.checkArgument; + +/** + * Represents an Item's duration in minutes. + */ +public class Duration { + public static final String MESSAGE_CONSTRAINTS = "Duration must be more than 0 minutes and not more than " + + "1440 minutes (1 day)."; + private final int duration; + + /** + * Constructs a {@code Duration}. + * + * @param duration A valid duration. + */ + public Duration(String duration) { + checkArgument(isValidDuration(duration), MESSAGE_CONSTRAINTS); + this.duration = Integer.valueOf(duration); + } + + public int getValue() { + return this.duration; + } + + /** + * Returns true if the given string is a positive integer or null. + */ + public static boolean isValidDuration(String test) { + if (test == null) { + return true; + } + int value; + try { + value = Integer.valueOf(test); + } catch (NumberFormatException e) { + return false; + } + return value > 0 && value <= 1440; + } + + @Override + public String toString() { + return String.valueOf(duration); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Duration)) { + return false; + } + + Duration otherDuration = (Duration) other; + return this.duration == otherDuration.duration; + } +} diff --git a/src/main/java/seedu/waddle/model/item/Item.java b/src/main/java/seedu/waddle/model/item/Item.java new file mode 100644 index 00000000000..f9e6b5f4e3c --- /dev/null +++ b/src/main/java/seedu/waddle/model/item/Item.java @@ -0,0 +1,149 @@ +package seedu.waddle.model.item; + +import static seedu.waddle.commons.util.CollectionUtil.requireAllNonNull; + +import java.time.LocalTime; + +import seedu.waddle.commons.core.Text; +import seedu.waddle.model.itinerary.Description; + +/** + * Represents an item in the itinerary. + */ +public class Item { + private final Description description; + private final Priority priority; + private final Cost cost; + private final Duration duration; + private LocalTime startTime; + private LocalTime endTime; + + /** + * Constructor for an item. + * + * @param description description of the item + */ + public Item(Description description, Priority priority, Cost cost, Duration duration) { + requireAllNonNull(description, priority); + this.description = description; + this.priority = priority; + this.cost = cost; + this.duration = duration; + } + + public Description getDescription() { + return description; + } + + public String getDescriptionString(int indents) { + return Text.indent(this.description.toString(), indents); + } + + public Priority getPriority() { + return priority; + } + + public String getPriorityString(int indents) { + return Text.indent("★".repeat(this.priority.getValue()), indents); + } + + public Cost getCost() { + return this.cost; + } + + public String getCostString(int indents) { + return Text.indent("Cost $" + Text.MONEY_PRINT_FORMATTER.format(this.cost.getValue()), indents); + } + + public Duration getDuration() { + return duration; + } + + public String getDurationString(int indents) { + return Text.indent("Duration " + this.duration + " mins", indents); + } + + public LocalTime getStartTime() { + return this.startTime; + } + + public void setStartTime(LocalTime startTime) { + this.startTime = startTime; + } + + public LocalTime getEndTime() { + LocalTime endTime = this.startTime.plusMinutes(this.duration.getValue()); + if (this.startTime.isBefore(LocalTime.MAX) && endTime.equals(LocalTime.MIDNIGHT)) { + return LocalTime.MAX; + } + return endTime; + } + + public String getTimeString(int indents) { + if (this.startTime != null) { + String endTime = getEndTime().toString(); + if (getEndTime().equals(LocalTime.MAX)) { + endTime = LocalTime.MIDNIGHT.toString() + " (next day)"; + } + if (this.duration != null) { + return Text.indent("Time: " + this.startTime + " - " + endTime, indents); + } else { + return Text.indent("Time: " + this.startTime, indents); + } + } + return Text.indent("Time: (Not planned)", indents); + } + + public void resetStartTime() { + this.startTime = null; + } + + /** + * Returns true if both items have the same description. + * This defines a weaker notion of equality between two persons. + */ + public boolean isSameItem(Item otherItem) { + if (otherItem == this) { + return true; + } + + return otherItem != null + && otherItem.getDescription().equals(getDescription()); + } + + /** + * Returns true if both items have the same identity and data fields. + * This defines a stronger notion of equality between two itineraries. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Item)) { + return false; + } + + Item otherItem = (Item) other; + return this.description.equals(otherItem.getDescription()) + && this.duration.equals(otherItem.getDuration()) + && this.cost.equals(otherItem.getCost()) + && this.priority.equals(otherItem.getPriority()); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getDescriptionString(Text.INDENT_NONE)) + .append(System.getProperty("line.separator")) + .append(getPriorityString(Text.INDENT_FOUR)) + .append(System.getProperty("line.separator")) + .append(getCostString(Text.INDENT_FOUR)) + .append(System.getProperty("line.separator")) + .append(getDurationString(Text.INDENT_FOUR)) + .append(System.getProperty("line.separator")) + .append(getTimeString(Text.INDENT_FOUR)); + return builder.toString(); + } +} diff --git a/src/main/java/seedu/waddle/model/item/Priority.java b/src/main/java/seedu/waddle/model/item/Priority.java new file mode 100644 index 00000000000..2404059db5d --- /dev/null +++ b/src/main/java/seedu/waddle/model/item/Priority.java @@ -0,0 +1,59 @@ +package seedu.waddle.model.item; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.util.AppUtil.checkArgument; + +/** + * Represents an Item's priority in the Itinerary. + * Guarantees: number between 1 and 5, default is 1; is valid as declared in {@link #isValidPriority(Integer)} + */ +public class Priority { + + public static final String MESSAGE_CONSTRAINTS = + "Priority should only contain a number between 1 and 5"; + + private final Integer stars; + + /** + * Constructs a {@code Priority}. + * + * @param stars A valid priority. + */ + public Priority(Integer stars) { + requireNonNull(stars); + checkArgument(isValidPriority(stars), MESSAGE_CONSTRAINTS); + this.stars = stars; + } + + /** + * Returns true if a given string is a valid priority. + */ + public static boolean isValidPriority(Integer test) { + return test > 0 && test <= 5; + } + + public int getValue() { + return this.stars; + } + + @Override + public String toString() { + return this.stars.toString(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Priority // instanceof handles nulls + && this.stars.equals(((Priority) other).getValue())); // state check + } + + @Override + public int hashCode() { + return this.stars.hashCode(); + } + + public int compareTo(Priority p) { + return this.stars.compareTo(p.getValue()); + } +} diff --git a/src/main/java/seedu/waddle/model/item/StartTime.java b/src/main/java/seedu/waddle/model/item/StartTime.java new file mode 100644 index 00000000000..e4c8414b829 --- /dev/null +++ b/src/main/java/seedu/waddle/model/item/StartTime.java @@ -0,0 +1,52 @@ +package seedu.waddle.model.item; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.util.AppUtil.checkArgument; + +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Represents the start time of the item. + */ +public class StartTime { + public static final String MESSAGE_CONSTRAINTS = + "Start time should be written as HH:mm in 24H format. For example, 3:25pm is 15:25."; + private static final String timePattern = "HH:mm"; + private static final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(timePattern); + private final LocalTime startTime; + + /** + * Constructs a {@code StartTime}. + * + * @param startTime A valid start time. + */ + public StartTime(String startTime) { + requireNonNull(startTime); + checkArgument(isValidStartTime(startTime), MESSAGE_CONSTRAINTS); + this.startTime = LocalTime.parse(startTime, timeFormatter); + } + + /** + * Returns true if a given string is a valid Cost + */ + public static boolean isValidStartTime(String test) { + LocalTime time; + try { + time = LocalTime.parse(test, timeFormatter); + } catch (DateTimeParseException e) { + return false; + } + return true; + } + + public LocalTime getStartTime() { + return this.startTime; + } + + @Override + public String toString() { + return this.startTime.toString(); + } +} diff --git a/src/main/java/seedu/waddle/model/item/UniqueItemList.java b/src/main/java/seedu/waddle/model/item/UniqueItemList.java new file mode 100644 index 00000000000..21002ec0d4c --- /dev/null +++ b/src/main/java/seedu/waddle/model/item/UniqueItemList.java @@ -0,0 +1,150 @@ +package seedu.waddle.model.item; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.waddle.model.item.exceptions.DuplicateItemException; +import seedu.waddle.model.item.exceptions.ItemNotFoundException; + +/** + * Represents the list of items in an itinerary. + */ +public class UniqueItemList implements Iterable { + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Get an item in the list. + */ + public Item get(int index) { + return internalList.get(index); + } + + /** + * Returns number of items in the list. + */ + public int getSize() { + return internalList.size(); + } + + /** + * Returns true if the list contains an equivalent item as the given argument. + */ + public boolean contains(Item toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameItem); + } + + /** + * Adds an item to the list. + * The item must not already exist in the list. + */ + public void add(Item toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateItemException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the item {@code target} in the list with {@code editedItem}. + * {@code target} must exist in the list. + * The item identity of {@code editedItem} must not be the same as another existing item in the list. + */ + public void setItem(Item target, Item editedItem) { + requireAllNonNull(target, editedItem); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new ItemNotFoundException(); + } + + if (!target.isSameItem(editedItem) && contains(editedItem)) { + throw new DuplicateItemException(); + } + + internalList.set(index, editedItem); + } + + /** + * Removes the equivalent item from the list. + * The item must exist in the list. + */ + public Item remove(int index) { + Item toRemove = get(index); + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new ItemNotFoundException(); + } + return toRemove; + } + + public void setItems(UniqueItemList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code items}. + * {@code items} must not contain duplicate items. + */ + public void setItems(List items) { + requireAllNonNull(items); + if (!itemsAreUnique(items)) { + throw new DuplicateItemException(); + } + + internalList.setAll(items); + } + + /** + * 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 UniqueItemList // instanceof handles nulls + && internalList.equals(((UniqueItemList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code items} contains only unique 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).isSameItem(items.get(j))) { + return false; + } + } + } + return true; + } + + public void sort(Comparator comparator) { + this.internalList.sort(comparator); + } + +} diff --git a/src/main/java/seedu/waddle/model/item/exceptions/DuplicateItemException.java b/src/main/java/seedu/waddle/model/item/exceptions/DuplicateItemException.java new file mode 100644 index 00000000000..5e789ca5b3f --- /dev/null +++ b/src/main/java/seedu/waddle/model/item/exceptions/DuplicateItemException.java @@ -0,0 +1,10 @@ +package seedu.waddle.model.item.exceptions; + +/** + * Signals that the operation will result in duplicate Items. + */ +public class DuplicateItemException extends RuntimeException { + public DuplicateItemException() { + super("Operation would result in duplicate items"); + } +} diff --git a/src/main/java/seedu/waddle/model/item/exceptions/ItemNotFoundException.java b/src/main/java/seedu/waddle/model/item/exceptions/ItemNotFoundException.java new file mode 100644 index 00000000000..464c8236124 --- /dev/null +++ b/src/main/java/seedu/waddle/model/item/exceptions/ItemNotFoundException.java @@ -0,0 +1,10 @@ +package seedu.waddle.model.item.exceptions; + +/** + * Signals that the operation is unable to find the specified item. + */ +public class ItemNotFoundException extends RuntimeException { + public ItemNotFoundException() { + super("The item could not be found."); + } +} diff --git a/src/main/java/seedu/waddle/model/item/exceptions/Period.java b/src/main/java/seedu/waddle/model/item/exceptions/Period.java new file mode 100644 index 00000000000..6dfc7e2d42c --- /dev/null +++ b/src/main/java/seedu/waddle/model/item/exceptions/Period.java @@ -0,0 +1,42 @@ +package seedu.waddle.model.item.exceptions; + +import java.time.LocalTime; + +/** + * This class encapsulates a time period. + */ +public class Period { + private final LocalTime start; + private final LocalTime end; + + /** + * Constructor. + * + * @param start Start time. + * @param end End time. + */ + public Period(LocalTime start, LocalTime end) { + //assert(end.isAfter(start) || start.equals(end)) : "start and end time must be valid"; + this.start = start; + this.end = end; + } + + public LocalTime getStart() { + return this.start; + } + + public LocalTime getEnd() { + return this.end; + } + + public String getStartString() { + return this.start.toString(); + } + + public String getEndString() { + if (this.end.equals(LocalTime.MAX)) { + return LocalTime.MIDNIGHT.toString() + " (next day)"; + } + return this.end.toString(); + } +} diff --git a/src/main/java/seedu/waddle/model/itinerary/Budget.java b/src/main/java/seedu/waddle/model/itinerary/Budget.java new file mode 100644 index 00000000000..2698f9ad2e4 --- /dev/null +++ b/src/main/java/seedu/waddle/model/itinerary/Budget.java @@ -0,0 +1,83 @@ +package seedu.waddle.model.itinerary; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.util.AppUtil.checkArgument; + +/** + * Represents an Itinerary's budget. + */ +public class Budget { + public static final String MESSAGE_CONSTRAINTS = + "Budget must be a value from $0 to $1,000,000."; + public static final String VALIDATION_REGEX = "\\d+([.][0-9]+)?$"; + private final float initialBudget; + private float spending; + + /** + * Constructs a {@code Budget}. + * + * @param budgetStr A valid value. + */ + public Budget(String budgetStr) { + requireNonNull(budgetStr); + checkArgument(isValidBudget(budgetStr), MESSAGE_CONSTRAINTS); + // round off the decimal to 2dp (money) + this.initialBudget = Math.round(Float.parseFloat(budgetStr) * 100.0F) / 100.0F; + this.spending = 0; + } + + /** + * Returns true if a given string is a valid budget. + */ + public static boolean isValidBudget(String test) { + if (!test.matches(VALIDATION_REGEX)) { + return false; + } + float budget; + try { + budget = Float.parseFloat(test); + } catch (NumberFormatException e) { + return false; + } + return budget >= 0 && budget <= 1000000; + } + + + @Override + public String toString() { + return String.valueOf(this.initialBudget); + } + + public float getValue() { + return this.initialBudget; + } + + public void updateSpending(float amount) { + this.spending = Math.round((this.spending + amount) * 100.0F) / 100.0F; + } + + public float calculateLeftOverBudget() { + return this.initialBudget - this.spending; + } + + public float getSpending() { + return this.spending; + } + + public void setSpending(float amt) { + this.spending = Math.round(amt * 100.0F) / 100.0F;; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Budget // instanceof handles nulls + && this.initialBudget == (((Budget) other).getValue())); // state check + } + + @Override + public int hashCode() { + return this.toString().hashCode(); + } + +} diff --git a/src/main/java/seedu/waddle/model/itinerary/Country.java b/src/main/java/seedu/waddle/model/itinerary/Country.java new file mode 100644 index 00000000000..1a34d03049e --- /dev/null +++ b/src/main/java/seedu/waddle/model/itinerary/Country.java @@ -0,0 +1,62 @@ +package seedu.waddle.model.itinerary; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.util.AppUtil.checkArgument; + +/** + * Represents an Itinerary's country in Waddle. + * Guarantees: immutable; is valid as declared in {@link #isValidCountry(String)} + */ +public class Country { + + public static final String MESSAGE_CONSTRAINTS = + "Country should only contain alphanumeric characters and spaces"; + + /* + * The first character of the country must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; + + public final String country; + + /** + * Constructs a {@code Country}. + * + * @param country A valid country. + */ + public Country(String country) { + requireNonNull(country); + checkArgument(isValidCountry(country), MESSAGE_CONSTRAINTS); + this.country = country; + } + + /** + * Returns true if a given string is a valid name. + */ + public static boolean isValidCountry(String test) { + return test.matches(VALIDATION_REGEX); + } + + + @Override + public String toString() { + if (country.equals("default")) { + return "(Not specified)"; + } + return country; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Country // instanceof handles nulls + && country.equals(((Country) other).country)); // state check + } + + @Override + public int hashCode() { + return country.hashCode(); + } + +} diff --git a/src/main/java/seedu/waddle/model/itinerary/Date.java b/src/main/java/seedu/waddle/model/itinerary/Date.java new file mode 100644 index 00000000000..1101f55b194 --- /dev/null +++ b/src/main/java/seedu/waddle/model/itinerary/Date.java @@ -0,0 +1,67 @@ +package seedu.waddle.model.itinerary; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.util.AppUtil.checkArgument; + +import java.time.LocalDate; +import java.time.format.DateTimeParseException; + +/** + * Represents an Itinerary's Date. + * Guarantees: immutable; is valid as declared in {@link #isValidDate(String)} + */ +public class Date { + + public static final String MESSAGE_CONSTRAINTS = + "Please provide a valid future date in the following format: yyyy-mm-dd."; + + public static final String VALIDATION_REGEX = "\\d{4}-\\d{2}-\\d{2}"; + + public final LocalDate date; + + /** + * Constructs a {@code Date}. + * + * @param date A valid date. + */ + public Date(String date) { + requireNonNull(date); + checkArgument(isValidDate(date), MESSAGE_CONSTRAINTS); + this.date = LocalDate.parse(date); + } + + /** + * Returns true if a given string is a valid name. + */ + public static boolean isValidDate(String test) { + LocalDate date; + try { + date = LocalDate.parse(test); + } catch (DateTimeParseException e) { + return false; + } + return !date.isBefore(LocalDate.now()); // cannot be past date + } + + public LocalDate getValue() { + return this.date; + } + + @Override + public String toString() { + return date.toString(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Date // instanceof handles nulls + && date.equals(((Date) other).date)); // state check + } + + @Override + public int hashCode() { + return date.hashCode(); + } + +} diff --git a/src/main/java/seedu/waddle/model/itinerary/DayNumber.java b/src/main/java/seedu/waddle/model/itinerary/DayNumber.java new file mode 100644 index 00000000000..c9129e57e82 --- /dev/null +++ b/src/main/java/seedu/waddle/model/itinerary/DayNumber.java @@ -0,0 +1,49 @@ +package seedu.waddle.model.itinerary; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.util.AppUtil.checkArgument; + +import seedu.waddle.commons.core.index.Index; + +/** + * Represents an Itinerary's day number element. + * Guarantees: immutable; is valid as declared in {@link #isValidDayNumber(String)} + */ +public class DayNumber { + public static final String MESSAGE_CONSTRAINTS = + "Day number should only contain positive numbers"; + public static final String VALIDATION_REGEX = "\\d+"; + + public final Index dayNumber; + + /** + * Constructs a {@code DayNumber}. + * + * @param dayNumber A valid value. + */ + public DayNumber(String dayNumber) { + requireNonNull(dayNumber); + checkArgument(isValidDayNumber(dayNumber), MESSAGE_CONSTRAINTS); + this.dayNumber = Index.fromOneBased(Integer.parseInt(dayNumber)); + } + + /** + * Returns true if a given string is a valid day number. + */ + public static boolean isValidDayNumber(String test) { + return test.matches(VALIDATION_REGEX); + } + + + @Override + public String toString() { + return String.valueOf(dayNumber); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof seedu.waddle.model.itinerary.DayNumber // instanceof handles nulls + && dayNumber.equals(((seedu.waddle.model.itinerary.DayNumber) other).dayNumber)); // state check + } +} diff --git a/src/main/java/seedu/waddle/model/itinerary/Description.java b/src/main/java/seedu/waddle/model/itinerary/Description.java new file mode 100644 index 00000000000..b8fd217e2b4 --- /dev/null +++ b/src/main/java/seedu/waddle/model/itinerary/Description.java @@ -0,0 +1,60 @@ +package seedu.waddle.model.itinerary; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.util.AppUtil.checkArgument; + +/** + * Represents an itinerary's description. + * Guarantees: immutable; is valid as declared in {@link #isValidDescription(String)} + */ +public class Description { + + public static final String MESSAGE_CONSTRAINTS = + "Description should not be blank and should only contain alphanumeric characters,spaces" + + " and these following special characters: ( ) & ! ' : , . -"; + + /* + * The first character of the description must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[a-zA-Z0-9\\-()&!':,.\\s]+"; + + public final String description; + + /** + * Constructs a {@code Description}. + * + * @param description A valid description. + */ + public Description(String description) { + requireNonNull(description); + checkArgument(isValidDescription(description), MESSAGE_CONSTRAINTS); + this.description = description; + } + + /** + * Returns true if a given string is a valid description. + */ + public static boolean isValidDescription(String test) { + return test.matches(VALIDATION_REGEX); + } + + + @Override + public String toString() { + return description; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Description // instanceof handles nulls + && description.equals(((Description) other).description)); // state check + } + + @Override + public int hashCode() { + return description.hashCode(); + } + +} diff --git a/src/main/java/seedu/waddle/model/itinerary/Itinerary.java b/src/main/java/seedu/waddle/model/itinerary/Itinerary.java new file mode 100644 index 00000000000..a206735d84a --- /dev/null +++ b/src/main/java/seedu/waddle/model/itinerary/Itinerary.java @@ -0,0 +1,403 @@ +package seedu.waddle.model.itinerary; + +import static seedu.waddle.commons.util.CollectionUtil.requireAllNonNull; + +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.waddle.commons.core.Messages; +import seedu.waddle.commons.core.Text; +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.commons.core.index.MultiIndex; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.item.Day; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.item.UniqueItemList; +import seedu.waddle.model.item.exceptions.DuplicateItemException; + +/** + * Represents an Itinerary in Waddle. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Itinerary { + + // Details field + private final Description description; + private final Country country; + private final Date startDate; + private final ItineraryDuration duration; + private final People people; + private final Budget budget; + private final StringProperty observableBudgetString; + private final UniqueItemList unscheduledItemList; + private final List days; + private final Comparator priorityComparator = new Comparator() { + @Override + public int compare(Item item1, Item item2) { + return item2.getPriority().compareTo(item1.getPriority()); + } + }; + + /** + * Every field must be present and not null. + */ + public Itinerary(Description description, Country country, Date startDate, ItineraryDuration duration, + People people, Budget budget) { + requireAllNonNull(description, startDate, duration); + this.description = description; + this.country = country; + this.startDate = startDate; + this.duration = duration; + this.people = people; + this.budget = budget; + this.observableBudgetString = new SimpleStringProperty(); + this.unscheduledItemList = new UniqueItemList(); + this.days = new ArrayList<>(); + for (int i = 0; i < duration.getValue(); i++) { + // TODO day number should start with 1 instead of 0? + this.days.add(new Day(i)); + } + } + + public Description getDescription() { + return description; + } + + public Country getCountry() { + return country; + } + + public Date getStartDate() { + return startDate; + } + + public ItineraryDuration getDuration() { + return this.duration; + } + + public People getPeople() { + return people; + } + + public Budget getBudget() { + return this.budget; + } + + public UniqueItemList getItemList() { + return unscheduledItemList; + } + + public List getDays() { + return this.days; + } + + public void setDays(List dayList) { + for (int i = 0; i < dayList.size(); i++) { + if (i < getDuration().getValue()) { + this.days.set(i, dayList.get(i)); + } else { + // transfer all items from extra days to unscheduled item list + for (Item item : dayList.get(i).deleteDay()) { + addItem(item); + this.budget.updateSpending(-item.getCost().getValue()); + } + } + } + } + + public void setUnscheduledItems(UniqueItemList unscheduledItemList) { + for (Item item : unscheduledItemList) { + addItem(item); + } + } + + /** + * Returns true if both itineraries have the same name. + * This defines a weaker notion of equality between two itineraries. + */ + public boolean isSameItinerary(Itinerary otherItinerary) { + if (otherItinerary == this) { + return true; + } + + return otherItinerary != null + && otherItinerary.getDescription().equals(getDescription()); + } + + public boolean hasItem(Item item) { + return this.unscheduledItemList.contains(item); + } + + /** + * Add item into itinerary. + * + * @param item Item to be added. + */ + public void addItem(Item item) { + for (Day day: days) { + if (day.hasItem(item)) { + throw new DuplicateItemException(); + } + } + this.unscheduledItemList.add(item); + sortUnscheduledItemList(); + } + + /** + * Remove item from itinerary. + * + * @param index A MultiIndex specifying position of task. + * @return The item to be removed. + */ + public Item removeItem(MultiIndex index) { + if (index.getDayIndex() == null) { + return this.unscheduledItemList.remove(index.getTaskIndex().getZeroBased()); + } else { + Day day = this.days.get(index.getDayIndex().getZeroBased()); + return day.removeItem(index.getTaskIndex()); + } + } + + public void setItem(Item target, Item editedItem, MultiIndex index) throws CommandException { + if (index.getDayIndex() == null) { + this.unscheduledItemList.setItem(target, editedItem); + sortUnscheduledItemList(); + } else { + Day day = this.days.get(index.getDayIndex().getZeroBased()); + day.removeItem(index.getTaskIndex()); + try { + day.addItem(editedItem); + } catch (CommandException e) { + day.addItem(target); + throw e; + } + this.budget.updateSpending(-target.getCost().getValue()); + this.budget.updateSpending(editedItem.getCost().getValue()); + this.observableBudgetString.set(getBudgetString(Text.INDENT_NONE)); + } + } + + public int getUnscheduledSize() { + return this.unscheduledItemList.getSize(); + } + + public UniqueItemList getUnscheduledItemList() { + return this.unscheduledItemList; + } + + private void sortUnscheduledItemList() { + this.unscheduledItemList.sort(priorityComparator); + } + + public Item getItem(MultiIndex index) { + if (index.getDayIndex() == null) { + return this.unscheduledItemList.get(index.getTaskIndex().getZeroBased()); + } else { + Day day = this.days.get(index.getDayIndex().getZeroBased()); + return day.getItem(index.getTaskIndex()); + } + } + + /** + * Plan an item. + * + * @param itemIndex Index of item in unscheduled list. + * @param dayNumber Day to include this item. + * @param startTime startTime of the item. + * @return The planned item. + * @throws CommandException When adding item to specific day leads to conflict in time. + */ + public Item planItem(Index itemIndex, DayNumber dayNumber, LocalTime startTime) throws CommandException { + Item item; + try { + item = this.unscheduledItemList.get(itemIndex.getZeroBased()); + } catch (IndexOutOfBoundsException e) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + + if (this.budget.calculateLeftOverBudget() - item.getCost().getValue() < 0) { + throw new CommandException(Messages.MESSAGE_OVER_BUDGET); + } + item.setStartTime(startTime); + Day day; + try { + day = this.days.get(dayNumber.dayNumber.getZeroBased()); + day.addItem(item); + } catch (IndexOutOfBoundsException e) { + throw new CommandException(Messages.MESSAGE_INVALID_DAY_INDEX); + } catch (CommandException e) { + // if time conflict detected, reset the time of the item + item.resetStartTime(); + throw e; + } + + this.unscheduledItemList.remove(itemIndex.getZeroBased()); + this.budget.updateSpending(item.getCost().getValue()); + this.observableBudgetString.set(getBudgetString(Text.INDENT_NONE)); + return item; + } + + /** + * Unplan an item. + * + * @param index A multiIndex to locate the day and index of task within the day + */ + public Item unplanItem(MultiIndex index) { + Day day = this.days.get(index.getDayIndex().getZeroBased()); + Item unplannedItem = day.removeItem(index.getTaskIndex()); + unplannedItem.resetStartTime(); + addItem(unplannedItem); + sortUnscheduledItemList(); + this.budget.updateSpending(-unplannedItem.getCost().getValue()); + this.observableBudgetString.set(getBudgetString(Text.INDENT_NONE)); + return unplannedItem; + } + + /** + * Calculates the total spending and updates the budget + */ + public float calculateSpending() { + float totalSpending = 0; + for (Day day : this.days) { + for (Item item : day.getItemList()) { + totalSpending += item.getCost().getValue(); + } + } + this.budget.setSpending(totalSpending); + return totalSpending; + } + + public String getVacantSlots() { + StringBuilder vacantSlots = new StringBuilder(); + for (Day day : this.days) { + vacantSlots.append(day.getVacantSlots()).append(System.lineSeparator()); + } + return vacantSlots.toString(); + } + + public ObservableList> getUnmodifiableItemGroups() { + ObservableList> itemGroups = FXCollections.observableArrayList(); + itemGroups.add(this.unscheduledItemList.asUnmodifiableObservableList()); + for (Day day : this.days) { + ObservableList itemList = day.getItemList().asUnmodifiableObservableList(); + itemGroups.add(itemList); + } + return FXCollections.unmodifiableObservableList(itemGroups); + } + + public String getDescriptionString(int indents) { + return Text.indent(this.description.toString(), indents); + } + + public String getCountryString(int indents) { + return Text.indent("Country: " + this.country, indents); + } + + public String getDurationString(int indents) { + return Text.indent("Duration: " + this.duration.getValue() + " Days", indents); + } + + public String getTimeString(int indents) { + if (this.startDate != null) { + if (this.duration != null) { + return Text.indent("Dates: " + this.startDate + " - " + + this.startDate.getValue().plusDays(this.duration.getValue() - 1), indents); + } else { + return Text.indent("Dates: " + this.startDate, indents); + } + } + return Text.indent("Dates: (Not planned)", indents); + } + + public String getPeopleString(int indents) { + return Text.indent("Waddlers: " + this.people, indents); + } + + public String getBudgetString(int indents) { + String budgetString = Text.MONEY_PRINT_FORMATTER.format(this.budget.getValue()); + String leftOverString = Text.MONEY_PRINT_FORMATTER.format(this.budget.calculateLeftOverBudget()); + if (this.budget.getSpending() == 0) { + return Text.indent("Budget: $" + budgetString, indents); + } else { + return Text.indent("Budget: $" + budgetString + ", $" + + leftOverString + " remaining", indents); + } + } + + public StringProperty getObservableBudgetString(int indents) { + this.observableBudgetString.set(getBudgetString(indents)); + return this.observableBudgetString; + } + + /** + * Generates a text representation of the day. + * + * @return The text representation. + */ + public String getTextRepresentation() { + StringBuilder itineraryText = new StringBuilder(); + itineraryText.append(this) + .append(System.lineSeparator()).append(System.lineSeparator()); + StringBuilder daysText = new StringBuilder(); + for (Day day : this.days) { + daysText.append(day.getTextRepresentation()); + } + itineraryText.append(daysText); + + return itineraryText.toString(); + } + + /** + * Returns true if both itineraries have the same identity and data fields. + * This defines a stronger notion of equality between two itineraries. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Itinerary)) { + return false; + } + + Itinerary otherItinerary = (Itinerary) other; + return otherItinerary.getDescription().equals(getDescription()) + && otherItinerary.getCountry().equals(getCountry()) + && otherItinerary.getStartDate().equals(getStartDate()) + && otherItinerary.getDuration().equals(getDuration()) + && otherItinerary.getPeople().equals(getPeople()) + && otherItinerary.getBudget().equals(getBudget()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(description, country, startDate, duration, people, budget); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getDescriptionString(Text.INDENT_NONE)) + .append(System.getProperty("line.separator")) + .append(getCountryString(Text.INDENT_FOUR)) + .append(System.getProperty("line.separator")) + .append(getDurationString(Text.INDENT_FOUR)) + .append(System.getProperty("line.separator")) + .append(getTimeString(Text.INDENT_FOUR)) + .append(System.getProperty("line.separator")) + .append(getPeopleString(Text.INDENT_FOUR)) + .append(System.getProperty("line.separator")) + .append(getBudgetString(Text.INDENT_FOUR)); + + return builder.toString(); + } +} diff --git a/src/main/java/seedu/waddle/model/itinerary/ItineraryDuration.java b/src/main/java/seedu/waddle/model/itinerary/ItineraryDuration.java new file mode 100644 index 00000000000..727185971db --- /dev/null +++ b/src/main/java/seedu/waddle/model/itinerary/ItineraryDuration.java @@ -0,0 +1,70 @@ +package seedu.waddle.model.itinerary; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.util.AppUtil.checkArgument; + +/** + * Represents an Itinerary's duration in days. + */ +public class ItineraryDuration { + public static final String MESSAGE_CONSTRAINTS = + "Duration must be at least 1 day, and not more than 365 days."; + public static final String VALIDATION_REGEX = "\\d+"; + + private final int duration; + + + /** + * Constructs a {@code ItineraryDuration}. + * + * @param duration A valid duration. + */ + public ItineraryDuration(String duration) { + requireNonNull(duration); + checkArgument(isValidDuration(duration), MESSAGE_CONSTRAINTS); + this.duration = Integer.parseInt(duration); + } + + /** + * Returns true if a given string is a valid duration. + */ + public static boolean isValidDuration(String test) { + if (!test.matches(VALIDATION_REGEX)) { + return false; + } + int value; + try { + value = Integer.parseInt(test); + } catch (NumberFormatException e) { + return false; + } + return value >= 1 && value <= 365; + } + + public int getValue() { + return this.duration; + } + + // TODO: implement with Date + public Date getEndFromStart(Date date) { + return date; + } + + @Override + public String toString() { + return String.valueOf(duration); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ItineraryDuration // instanceof handles nulls + && duration == (((ItineraryDuration) other).duration)); // state check + } + + @Override + public int hashCode() { + return toString().hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/waddle/model/itinerary/NameContainsKeywordsPredicate.java similarity index 69% rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/seedu/waddle/model/itinerary/NameContainsKeywordsPredicate.java index c9b5868427c..aaecde90c56 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/seedu/waddle/model/itinerary/NameContainsKeywordsPredicate.java @@ -1,14 +1,14 @@ -package seedu.address.model.person; +package seedu.waddle.model.itinerary; import java.util.List; import java.util.function.Predicate; -import seedu.address.commons.util.StringUtil; +import seedu.waddle.commons.util.StringUtil; /** - * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. + * Tests that a {@code Itinerary}'s {@code Description} 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,10 @@ public NameContainsKeywordsPredicate(List keywords) { } @Override - public boolean test(Person person) { + public boolean test(Itinerary itinerary) { return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(itinerary.getDescription() + .description, keyword)); } @Override diff --git a/src/main/java/seedu/waddle/model/itinerary/People.java b/src/main/java/seedu/waddle/model/itinerary/People.java new file mode 100644 index 00000000000..7349c1bafdd --- /dev/null +++ b/src/main/java/seedu/waddle/model/itinerary/People.java @@ -0,0 +1,59 @@ +package seedu.waddle.model.itinerary; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.util.AppUtil.checkArgument; + +/** + * Represents an Itinerary's people element. + * For now, it only records the number of people, can extend to contain names or person object. + * Guarantees: immutable; is valid as declared in {@link #isValidPeople(String)} + */ +public class People { + + public static final String MESSAGE_CONSTRAINTS = + "Number of people should only contain positive numbers"; + public static final String VALIDATION_REGEX = "\\d+"; + + public final String numOfPeople; + + /** + * Constructs a {@code People}. + * + * @param numOfPeople A valid value. + */ + public People(String numOfPeople) { + requireNonNull(numOfPeople); + checkArgument(isValidPeople(numOfPeople), MESSAGE_CONSTRAINTS); + this.numOfPeople = numOfPeople; + } + + /** + * Returns true if a given string is a valid number of people. + */ + public static boolean isValidPeople(String test) { + if (!test.matches(VALIDATION_REGEX)) { + return false; + } + int people = Integer.parseInt(test); + return people >= 1; // at least 1 Waddler + } + + + @Override + public String toString() { + return numOfPeople; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof People // instanceof handles nulls + && numOfPeople.equals(((People) other).numOfPeople)); // state check + } + + @Override + public int hashCode() { + return numOfPeople.hashCode(); + } + +} diff --git a/src/main/java/seedu/waddle/model/itinerary/UniqueItineraryList.java b/src/main/java/seedu/waddle/model/itinerary/UniqueItineraryList.java new file mode 100644 index 00000000000..9c14a625fc9 --- /dev/null +++ b/src/main/java/seedu/waddle/model/itinerary/UniqueItineraryList.java @@ -0,0 +1,139 @@ +package seedu.waddle.model.itinerary; + +import static java.util.Objects.requireNonNull; +import static seedu.waddle.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.waddle.model.itinerary.exceptions.DuplicateItineraryException; +import seedu.waddle.model.itinerary.exceptions.ItineraryNotFoundException; + +/** + * A list of itineraries that enforces uniqueness between its elements and does not allow nulls. + * An itinerary is considered unique by comparing using {@code Itinerary#isSameItinerary(Itinerary)}. + * As such, adding and updating of itineraries uses Itinerary#isSameItinerary(Itinerary) for equality + * so as to ensure that the itinerary being added or updated is unique in terms of identity in + * the UniqueItineraryList. However, the removal of a itinerary uses Itinerary#equals(Object) so + * as to ensure that the itinerary with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Itinerary#isSameItinerary(Itinerary) + */ +public class UniqueItineraryList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent itinerary as the given argument. + */ + public boolean contains(Itinerary toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameItinerary); + } + + /** + * Adds an itinerary to the list. + * The itinerary must not already exist in the list. + */ + public void add(Itinerary toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateItineraryException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the itinerary {@code target} in the list with {@code editedItinerary}. + * {@code target} must exist in the list. + * The itinerary identity of {@code editedItinerary} must not be the same as another + * existing itinerary in the list. + */ + public void setItinerary(Itinerary target, Itinerary editedItinerary) { + requireAllNonNull(target, editedItinerary); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new ItineraryNotFoundException(); + } + + if (!target.isSameItinerary(editedItinerary) && contains(editedItinerary)) { + throw new DuplicateItineraryException(); + } + + internalList.set(index, editedItinerary); + } + + /** + * Removes the equivalent itinerary from the list. + * The itinerary must exist in the list. + */ + public void remove(Itinerary toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new ItineraryNotFoundException(); + } + } + + public void setItineraries(UniqueItineraryList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code itineraries}. + * {@code itineraries} must not contain duplicate itineraries. + */ + public void setItineraries(List itineraries) { + requireAllNonNull(itineraries); + if (!itinerariesAreUnique(itineraries)) { + throw new DuplicateItineraryException(); + } + + internalList.setAll(itineraries); + } + + /** + * 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 UniqueItineraryList // instanceof handles nulls + && internalList.equals(((UniqueItineraryList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code itinerary} contains only unique itineraries. + */ + private boolean itinerariesAreUnique(List itineraries) { + for (int i = 0; i < itineraries.size() - 1; i++) { + for (int j = i + 1; j < itineraries.size(); j++) { + if (itineraries.get(i).isSameItinerary(itineraries.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/waddle/model/itinerary/exceptions/DuplicateItineraryException.java b/src/main/java/seedu/waddle/model/itinerary/exceptions/DuplicateItineraryException.java new file mode 100644 index 00000000000..221db9e61db --- /dev/null +++ b/src/main/java/seedu/waddle/model/itinerary/exceptions/DuplicateItineraryException.java @@ -0,0 +1,10 @@ +package seedu.waddle.model.itinerary.exceptions; + +/** + * Signals that the operation will result in duplicate Itineraries. + */ +public class DuplicateItineraryException extends RuntimeException { + public DuplicateItineraryException() { + super("Operation would result in duplicate itineraries"); + } +} diff --git a/src/main/java/seedu/waddle/model/itinerary/exceptions/ItineraryNotFoundException.java b/src/main/java/seedu/waddle/model/itinerary/exceptions/ItineraryNotFoundException.java new file mode 100644 index 00000000000..2efcde82f14 --- /dev/null +++ b/src/main/java/seedu/waddle/model/itinerary/exceptions/ItineraryNotFoundException.java @@ -0,0 +1,6 @@ +package seedu.waddle.model.itinerary.exceptions; + +/** + * Signals that the operation is unable to find the specified person. + */ +public class ItineraryNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/waddle/model/util/SampleDataUtil.java b/src/main/java/seedu/waddle/model/util/SampleDataUtil.java new file mode 100644 index 00000000000..ae0ad886cf0 --- /dev/null +++ b/src/main/java/seedu/waddle/model/util/SampleDataUtil.java @@ -0,0 +1,34 @@ +package seedu.waddle.model.util; + +import seedu.waddle.model.ReadOnlyWaddle; +import seedu.waddle.model.Waddle; +import seedu.waddle.model.itinerary.Budget; +import seedu.waddle.model.itinerary.Country; +import seedu.waddle.model.itinerary.Date; +import seedu.waddle.model.itinerary.Description; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.model.itinerary.ItineraryDuration; +import seedu.waddle.model.itinerary.People; + +/** + * Contains utility methods for populating {@code Waddle} with sample data. + */ +public class SampleDataUtil { + public static Itinerary[] getSampleItineraries() { + return new Itinerary[]{ + new Itinerary(new Description("Graduation Trip"), new Country("Singapore"), new Date("2025-07-30"), + new ItineraryDuration("30"), new People("5"), new Budget("1000")), + new Itinerary(new Description("Winter Trip"), new Country("Sweden"), new Date("2025-11-15"), + new ItineraryDuration("7"), new People("3"), new Budget("5000")), + }; + } + + public static ReadOnlyWaddle getSampleWaddle() { + Waddle sampleWaddle = new Waddle(); + for (Itinerary sampleItinerary : getSampleItineraries()) { + sampleWaddle.addItinerary(sampleItinerary); + } + return sampleWaddle; + } + +} diff --git a/src/main/java/seedu/waddle/storage/JsonAdaptedDay.java b/src/main/java/seedu/waddle/storage/JsonAdaptedDay.java new file mode 100644 index 00000000000..d850a99d642 --- /dev/null +++ b/src/main/java/seedu/waddle/storage/JsonAdaptedDay.java @@ -0,0 +1,70 @@ +package seedu.waddle.storage; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.waddle.commons.exceptions.IllegalValueException; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.item.Day; +import seedu.waddle.model.item.Item; + +/** + * Jackson-friendly version of {@link Day}. + */ +public class JsonAdaptedDay { + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Item's %s field is missing!"; + public static final String MESSAGE_CLASHING_ITEMS = "Day %d contains Items with clashing timings!"; + + private final Integer dayNumber; + + private final List items = new ArrayList<>(); + + /** + * Constructs a {@code JsonAdaptedDay} with the given day details. + */ + @JsonCreator + public JsonAdaptedDay(@JsonProperty("dayNumber") Integer dayNumber, + @JsonProperty("items") List items) { + this.dayNumber = dayNumber; + this.items.addAll(items); + } + + /** + * Converts a given {@code Day} into this class for Jackson use. + */ + public JsonAdaptedDay(Day source) { + dayNumber = source.getDayNumber(); + for (Item item : source.getItemList()) { + items.add(new JsonAdaptedItem(item)); + } + } + + /** + * Converts this Jackson-friendly adapted item object into the model's {@code Item} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted item. + */ + public Day toModelType() throws IllegalValueException { + + if (dayNumber == null) { + // TODO change to getSimpleName? + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "Day Number")); + } + final int modelDayNumber = dayNumber; + + Day day = new Day(modelDayNumber); + for (JsonAdaptedItem jsonAdaptedItem : items) { + Item item = jsonAdaptedItem.toModelType(); + try { + day.addItem(item); + } catch (CommandException e) { + throw new IllegalValueException(String.format(MESSAGE_CLASHING_ITEMS, modelDayNumber)); + } + } + + return day; + } +} diff --git a/src/main/java/seedu/waddle/storage/JsonAdaptedItem.java b/src/main/java/seedu/waddle/storage/JsonAdaptedItem.java new file mode 100644 index 00000000000..97d91aa1477 --- /dev/null +++ b/src/main/java/seedu/waddle/storage/JsonAdaptedItem.java @@ -0,0 +1,114 @@ +package seedu.waddle.storage; + +import java.time.LocalTime; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.waddle.commons.core.Text; +import seedu.waddle.commons.exceptions.IllegalValueException; +import seedu.waddle.model.item.Cost; +import seedu.waddle.model.item.Duration; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.item.Priority; +import seedu.waddle.model.itinerary.Description; + +/** + * Jackson-friendly version of {@link Item}. + */ +public class JsonAdaptedItem { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Item's %s field is missing!"; + + private final String description; + private final Integer stars; + private final String cost; + private final String duration; + private final String startTime; + + /** + * Constructs a {@code JsonAdaptedItem} with the given item details. + */ + @JsonCreator + public JsonAdaptedItem(@JsonProperty("description") String description, + @JsonProperty("priority") Integer stars, + @JsonProperty("cost") String cost, + @JsonProperty("duration") String duration, + @JsonProperty("startTime") String startTime) { + this.description = description; + this.stars = stars; + this.cost = cost; + this.duration = duration; + this.startTime = startTime; + } + + /** + * Converts a given {@code Item} into this class for Jackson use. + */ + public JsonAdaptedItem(Item source) { + description = source.getDescription().description; + stars = source.getPriority().getValue(); + cost = Text.MONEY_SAVE_FORMATTER.format(source.getCost().getValue()); + duration = source.getDuration().toString(); + if (source.getStartTime() == null) { + startTime = null; + } else { + startTime = source.getStartTime().toString(); + } + } + + /** + * Converts this Jackson-friendly adapted item object into the model's {@code Item} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted item. + */ + public Item toModelType() throws IllegalValueException { + + if (description == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Description.class.getSimpleName())); + } + if (!Description.isValidDescription(description)) { + throw new IllegalValueException(Description.MESSAGE_CONSTRAINTS); + } + + if (stars == null) { + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, Priority.class.getSimpleName())); + } + if (!Priority.isValidPriority(stars)) { + throw new IllegalValueException(Priority.MESSAGE_CONSTRAINTS); + } + + if (cost == null) { + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, Cost.class.getSimpleName())); + } + if (!Cost.isValidCost(cost)) { + throw new IllegalValueException(Cost.MESSAGE_CONSTRAINTS); + } + + if (duration == null) { + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, Duration.class.getSimpleName())); + } + if (!Duration.isValidDuration(duration)) { + throw new IllegalValueException(Duration.MESSAGE_CONSTRAINTS); + } + + final Description modelDescription = new Description(description); + final Priority modelPriority = new Priority(stars); + final Cost modelCost = new Cost(cost); + final Duration modelDuration = new Duration(duration); + + Item item = new Item(modelDescription, modelPriority, modelCost, modelDuration); + + if (startTime != null) { + final LocalTime modelStartTime = LocalTime.parse(startTime); + item.setStartTime(modelStartTime); + } + + return item; + } + +} diff --git a/src/main/java/seedu/waddle/storage/JsonAdaptedItinerary.java b/src/main/java/seedu/waddle/storage/JsonAdaptedItinerary.java new file mode 100644 index 00000000000..2cfef97c978 --- /dev/null +++ b/src/main/java/seedu/waddle/storage/JsonAdaptedItinerary.java @@ -0,0 +1,165 @@ +package seedu.waddle.storage; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.waddle.commons.core.Text; +import seedu.waddle.commons.exceptions.IllegalValueException; +import seedu.waddle.model.item.Day; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.item.exceptions.DuplicateItemException; +import seedu.waddle.model.itinerary.Budget; +import seedu.waddle.model.itinerary.Country; +import seedu.waddle.model.itinerary.Date; +import seedu.waddle.model.itinerary.Description; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.model.itinerary.ItineraryDuration; +import seedu.waddle.model.itinerary.People; + +/** + * Jackson-friendly version of {@link Itinerary}. + */ +class JsonAdaptedItinerary { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Itinerary's %s field is missing!"; + public static final String MESSAGE_DUPLICATE_ITEM = "Item list contains duplicate items."; + + private final String description; + private final String country; + private final String startDate; + private final String duration; + private final String people; + private final String budget; + + private final List items = new ArrayList<>(); + private final List days = new ArrayList<>(); + + + /** + * Constructs a {@code JsonAdaptedPerson} with the given itinerary details. + */ + @JsonCreator + public JsonAdaptedItinerary(@JsonProperty("description") String description, + @JsonProperty("country") String country, + @JsonProperty("startDate") String startDate, + @JsonProperty("duration") String duration, + @JsonProperty("people") String people, + @JsonProperty("budget") String budget, + @JsonProperty("items") List items, + @JsonProperty("days") List days) { + this.description = description; + this.country = country; + this.startDate = startDate; + this.duration = duration; + this.people = people; + this.budget = budget; + this.items.addAll(items); + this.days.addAll(days); + } + + /** + * Converts a given {@code Itinerary} into this class for Jackson use. + */ + public JsonAdaptedItinerary(Itinerary source) { + description = source.getDescription().description; + country = source.getCountry().country; + startDate = source.getStartDate().date.toString(); + duration = source.getDuration().toString(); + people = source.getPeople().numOfPeople; + budget = Text.MONEY_SAVE_FORMATTER.format(source.getBudget().getValue()); + for (Item item : source.getItemList()) { + items.add(new JsonAdaptedItem(item)); + } + for (Day day : source.getDays()) { + days.add(new JsonAdaptedDay(day)); + } + } + + /** + * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted person. + */ + public Itinerary toModelType() throws IllegalValueException { + + if (description == null) { + throw new IllegalValueException(String.format( + MISSING_FIELD_MESSAGE_FORMAT, Description.class.getSimpleName())); + } + if (!Description.isValidDescription(description)) { + throw new IllegalValueException(Description.MESSAGE_CONSTRAINTS); + } + + if (country == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Country.class.getSimpleName())); + } + if (!Country.isValidCountry(country)) { + throw new IllegalValueException(Country.MESSAGE_CONSTRAINTS); + } + + if (startDate == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Date.class.getSimpleName())); + } + if (!Date.isValidDate(startDate)) { + throw new IllegalValueException(Date.MESSAGE_CONSTRAINTS); + } + + if (duration == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + ItineraryDuration.class.getSimpleName())); + } + if (!ItineraryDuration.isValidDuration(duration)) { + throw new IllegalValueException(Date.MESSAGE_CONSTRAINTS); + } + + if (people == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + People.class.getSimpleName())); + } + if (!People.isValidPeople(people)) { + throw new IllegalValueException(People.MESSAGE_CONSTRAINTS); + } + + if (budget == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Budget.class.getSimpleName())); + } + if (!Budget.isValidBudget(budget)) { + throw new IllegalValueException(Budget.MESSAGE_CONSTRAINTS); + } + final Description modelDescription = new Description(description); + final Country modelCountry = new Country(country); + final Date modelStartDate = new Date(startDate); + final ItineraryDuration modelDuration = new ItineraryDuration(duration); + final People modelPeople = new People(people); + final Budget modelBudget = new Budget(budget); + + Itinerary itinerary = new Itinerary(modelDescription, modelCountry, modelStartDate, modelDuration, + modelPeople, modelBudget); + + final List modelDays = new ArrayList<>(); + for (JsonAdaptedDay jsonAdaptedDay : days) { + Day day = jsonAdaptedDay.toModelType(); + modelDays.add(day); + } + itinerary.setDays(modelDays); + itinerary.calculateSpending(); + + for (JsonAdaptedItem jsonAdaptedItem : items) { + Item item = jsonAdaptedItem.toModelType(); + try { + itinerary.addItem(item); + } catch (DuplicateItemException e) { + throw new IllegalValueException(MESSAGE_DUPLICATE_ITEM); + } + } + + return itinerary; + } + +} diff --git a/src/main/java/seedu/waddle/storage/JsonSerializableWaddle.java b/src/main/java/seedu/waddle/storage/JsonSerializableWaddle.java new file mode 100644 index 00000000000..f642db1a469 --- /dev/null +++ b/src/main/java/seedu/waddle/storage/JsonSerializableWaddle.java @@ -0,0 +1,61 @@ +package seedu.waddle.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.waddle.commons.exceptions.IllegalValueException; +import seedu.waddle.model.ReadOnlyWaddle; +import seedu.waddle.model.Waddle; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * An Immutable Waddle that is serializable to JSON format. + */ +@JsonRootName(value = "waddle") +class JsonSerializableWaddle { + + public static final String MESSAGE_DUPLICATE_ITINERARY = "Itinerary list contains duplicate itinerary(ies)."; + + private final List itineraries = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableAddressBook} with the given itineraries. + */ + @JsonCreator + public JsonSerializableWaddle(@JsonProperty("itineraries") List itineraries) { + this.itineraries.addAll(itineraries); + } + + /** + * 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 JsonSerializableWaddle(ReadOnlyWaddle source) { + itineraries.addAll(source.getItineraryList().stream().map(JsonAdaptedItinerary::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 Waddle toModelType() throws IllegalValueException { + Waddle waddle = new Waddle(); + for (JsonAdaptedItinerary jsonAdaptedItinerary : itineraries) { + Itinerary itinerary = jsonAdaptedItinerary.toModelType(); + if (waddle.hasItinerary(itinerary)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_ITINERARY); + } + waddle.addItinerary(itinerary); + } + return waddle; + } + +} diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/seedu/waddle/storage/JsonUserPrefsStorage.java similarity index 83% rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java rename to src/main/java/seedu/waddle/storage/JsonUserPrefsStorage.java index bc2bbad84aa..829eaf8b34f 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/seedu/waddle/storage/JsonUserPrefsStorage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package seedu.waddle.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.waddle.commons.exceptions.DataConversionException; +import seedu.waddle.commons.util.JsonUtil; +import seedu.waddle.model.ReadOnlyUserPrefs; +import seedu.waddle.model.UserPrefs; /** * A class to access UserPrefs stored in the hard disk as a json file diff --git a/src/main/java/seedu/waddle/storage/JsonWaddleStorage.java b/src/main/java/seedu/waddle/storage/JsonWaddleStorage.java new file mode 100644 index 00000000000..86d35cbf0ee --- /dev/null +++ b/src/main/java/seedu/waddle/storage/JsonWaddleStorage.java @@ -0,0 +1,80 @@ +package seedu.waddle.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.waddle.commons.core.LogsCenter; +import seedu.waddle.commons.exceptions.DataConversionException; +import seedu.waddle.commons.exceptions.IllegalValueException; +import seedu.waddle.commons.util.FileUtil; +import seedu.waddle.commons.util.JsonUtil; +import seedu.waddle.model.ReadOnlyWaddle; + +/** + * A class to access Waddle data stored as a json file on the hard disk. + */ +public class JsonWaddleStorage implements WaddleStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonWaddleStorage.class); + + private Path filePath; + + public JsonWaddleStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getWaddleFilePath() { + return filePath; + } + + @Override + public Optional readWaddle() throws DataConversionException { + return readWaddle(filePath); + } + + /** + * Similar to {@link #readWaddle()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readWaddle(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonWaddle = JsonUtil.readJsonFile( + filePath, JsonSerializableWaddle.class); + if (!jsonWaddle.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonWaddle.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveWaddle(ReadOnlyWaddle waddle) throws IOException { + saveWaddle(waddle, filePath); + } + + /** + * Similar to {@link #saveWaddle(ReadOnlyWaddle)}. + * + * @param filePath location of the data. Cannot be null. + */ + public void saveWaddle(ReadOnlyWaddle waddle, Path filePath) throws IOException { + requireNonNull(waddle); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableWaddle(waddle), filePath); + } + +} diff --git a/src/main/java/seedu/waddle/storage/Storage.java b/src/main/java/seedu/waddle/storage/Storage.java new file mode 100644 index 00000000000..4dd5f904538 --- /dev/null +++ b/src/main/java/seedu/waddle/storage/Storage.java @@ -0,0 +1,32 @@ +package seedu.waddle.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.waddle.commons.exceptions.DataConversionException; +import seedu.waddle.model.ReadOnlyUserPrefs; +import seedu.waddle.model.ReadOnlyWaddle; +import seedu.waddle.model.UserPrefs; + +/** + * API of the Storage component + */ +public interface Storage extends WaddleStorage, UserPrefsStorage { + + @Override + Optional readUserPrefs() throws DataConversionException, IOException; + + @Override + void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; + + @Override + Path getWaddleFilePath(); + + @Override + Optional readWaddle() throws DataConversionException, IOException; + + @Override + void saveWaddle(ReadOnlyWaddle addressBook) throws IOException; + +} diff --git a/src/main/java/seedu/waddle/storage/StorageManager.java b/src/main/java/seedu/waddle/storage/StorageManager.java new file mode 100644 index 00000000000..ee238bd8629 --- /dev/null +++ b/src/main/java/seedu/waddle/storage/StorageManager.java @@ -0,0 +1,78 @@ +package seedu.waddle.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.waddle.commons.core.LogsCenter; +import seedu.waddle.commons.exceptions.DataConversionException; +import seedu.waddle.model.ReadOnlyUserPrefs; +import seedu.waddle.model.ReadOnlyWaddle; +import seedu.waddle.model.UserPrefs; + +/** + * Manages storage of Waddle data in local storage. + */ +public class StorageManager implements Storage { + + private static final Logger logger = LogsCenter.getLogger(StorageManager.class); + private final WaddleStorage waddleStorage; + private final UserPrefsStorage userPrefsStorage; + + /** + * Creates a {@code StorageManager} with the given {@code WaddleStorage} and {@code UserPrefStorage}. + */ + public StorageManager(WaddleStorage waddleStorage, UserPrefsStorage userPrefsStorage) { + this.waddleStorage = waddleStorage; + this.userPrefsStorage = userPrefsStorage; + } + + // ================ UserPrefs methods ============================== + + @Override + public Path getUserPrefsFilePath() { + return userPrefsStorage.getUserPrefsFilePath(); + } + + @Override + public Optional readUserPrefs() throws DataConversionException, IOException { + return userPrefsStorage.readUserPrefs(); + } + + @Override + public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { + userPrefsStorage.saveUserPrefs(userPrefs); + } + + + // ================ Waddle methods ============================== + + @Override + public Path getWaddleFilePath() { + return waddleStorage.getWaddleFilePath(); + } + + @Override + public Optional readWaddle() throws DataConversionException, IOException { + return readWaddle(waddleStorage.getWaddleFilePath()); + } + + @Override + public Optional readWaddle(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return waddleStorage.readWaddle(filePath); + } + + @Override + public void saveWaddle(ReadOnlyWaddle waddle) throws IOException { + saveWaddle(waddle, waddleStorage.getWaddleFilePath()); + } + + @Override + public void saveWaddle(ReadOnlyWaddle waddle, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + waddleStorage.saveWaddle(waddle, filePath); + } + +} diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/seedu/waddle/storage/UserPrefsStorage.java similarity index 71% rename from src/main/java/seedu/address/storage/UserPrefsStorage.java rename to src/main/java/seedu/waddle/storage/UserPrefsStorage.java index 29eef178dbc..6cbdb1490a5 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/seedu/waddle/storage/UserPrefsStorage.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package seedu.waddle.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.waddle.commons.exceptions.DataConversionException; +import seedu.waddle.model.ReadOnlyUserPrefs; +import seedu.waddle.model.UserPrefs; /** - * Represents a storage for {@link seedu.address.model.UserPrefs}. + * Represents a storage for {@link seedu.waddle.model.UserPrefs}. */ public interface UserPrefsStorage { @@ -27,7 +27,7 @@ public interface UserPrefsStorage { Optional readUserPrefs() throws DataConversionException, IOException; /** - * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage. + * Saves the given {@link seedu.waddle.model.ReadOnlyUserPrefs} to the storage. * @param userPrefs cannot be null. * @throws IOException if there was any problem writing to the file. */ diff --git a/src/main/java/seedu/waddle/storage/WaddleStorage.java b/src/main/java/seedu/waddle/storage/WaddleStorage.java new file mode 100644 index 00000000000..e3f7abc07bc --- /dev/null +++ b/src/main/java/seedu/waddle/storage/WaddleStorage.java @@ -0,0 +1,46 @@ +package seedu.waddle.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.waddle.commons.exceptions.DataConversionException; +import seedu.waddle.model.ReadOnlyWaddle; +import seedu.waddle.model.Waddle; + +/** + * Represents a storage for {@link Waddle}. + */ +public interface WaddleStorage { + + /** + * Returns the file path of the data file. + */ + Path getWaddleFilePath(); + + /** + * Returns Waddle data as a {@link ReadOnlyWaddle}. + * 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 readWaddle() throws DataConversionException, IOException; + + /** + * @see #getWaddleFilePath() + */ + Optional readWaddle(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyWaddle} to the storage. + * @param waddle cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveWaddle(ReadOnlyWaddle waddle) throws IOException; + + /** + * @see #saveWaddle(ReadOnlyWaddle) + */ + void saveWaddle(ReadOnlyWaddle waddle, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/waddle/ui/CommandBox.java similarity index 89% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/seedu/waddle/ui/CommandBox.java index 9e75478664b..e3266416b17 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/waddle/ui/CommandBox.java @@ -1,12 +1,12 @@ -package seedu.address.ui; +package seedu.waddle.ui; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.TextField; import javafx.scene.layout.Region; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.waddle.logic.commands.CommandResult; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.logic.parser.exceptions.ParseException; /** * The UI component that is responsible for receiving user command inputs. @@ -77,7 +77,7 @@ public interface CommandExecutor { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see seedu.waddle.logic.Logic#execute(String) */ CommandResult execute(String commandText) throws CommandException, ParseException; } diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/waddle/ui/HelpWindow.java similarity index 92% rename from src/main/java/seedu/address/ui/HelpWindow.java rename to src/main/java/seedu/waddle/ui/HelpWindow.java index 3f16b2fcf26..05bfea44c9c 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/waddle/ui/HelpWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.waddle.ui; import java.util.logging.Logger; @@ -8,14 +8,15 @@ import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.Stage; -import seedu.address.commons.core.LogsCenter; +import seedu.waddle.commons.core.LogsCenter; /** * Controller for a help page */ 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://ay2223s1-cs2103t-w11-4.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/waddle/ui/ItemCard.java b/src/main/java/seedu/waddle/ui/ItemCard.java new file mode 100644 index 00000000000..5a6584af86e --- /dev/null +++ b/src/main/java/seedu/waddle/ui/ItemCard.java @@ -0,0 +1,71 @@ +package seedu.waddle.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.waddle.commons.core.Text; +import seedu.waddle.model.item.Item; + +/** + * An UI component that displays information of a {@code Item}. + */ +public class ItemCard extends UiPart { + private static final String FXML = "ItemListCard.fxml"; + public final Item item; + + @FXML + private HBox cardPane; + @FXML + private Label description; + @FXML + private Label id; + // Priority and Category have not yet been implemented + @FXML + private Label priority; + @FXML + private Label duration; + @FXML + private Label time; + @FXML + private Label cost; + // @FXML + // private Label category; + + /** + * Creates a {@code ItemCode} with the given {@code Item} and index to display. + */ + public ItemCard(Item item, int dayNumber, int displayedIndex) { + super(FXML); + this.item = item; + if (dayNumber > 0) { + this.id.setText(dayNumber + "." + displayedIndex + " "); + } else { + this.id.setText(displayedIndex + ". "); + } + this.description.setText(item.getDescriptionString(Text.INDENT_NONE)); + this.priority.setText(item.getPriorityString(Text.INDENT_NONE)); + this.duration.setText(item.getDurationString(Text.INDENT_NONE)); + this.time.setText(item.getTimeString(Text.INDENT_NONE)); + this.cost.setText(item.getCostString(Text.INDENT_NONE)); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ItemCard)) { + return false; + } + + // state check + ItemCard card = (ItemCard) other; + return id.getText().equals(card.id.getText()) + && item.equals(card.item); + } + +} diff --git a/src/main/java/seedu/waddle/ui/ItemGroupCard.java b/src/main/java/seedu/waddle/ui/ItemGroupCard.java new file mode 100644 index 00000000000..7155c508d8c --- /dev/null +++ b/src/main/java/seedu/waddle/ui/ItemGroupCard.java @@ -0,0 +1,64 @@ +package seedu.waddle.ui; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import seedu.waddle.model.item.Item; + +/** + * An UI component that displays information of a {@code Itinerary}. + */ +public class ItemGroupCard extends UiPart { + + private static final String FXML = "ItemGroupListCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + public final ObservableList itemGroup; + + @FXML + private Label dayNumber; + @FXML + private StackPane itemListPanelPlaceholder; + + /** + * Creates a {@code ItineraryCode} with the given {@code Itinerary} and index to display. + */ + public ItemGroupCard(ObservableList itemGroup, int dayNumber) { + super(FXML); + this.itemGroup = itemGroup; + if (dayNumber == 0) { + this.dayNumber.setText("Wishlist"); + } else { + this.dayNumber.setText("Day " + dayNumber); + } + this.itemListPanelPlaceholder.getChildren().add(new ItemListPanel(itemGroup, dayNumber).getRoot()); + this.itemListPanelPlaceholder.setMinHeight(UiSizes.ITEM_LIST_MIN_HEIGHT); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ItemGroupCard)) { + return false; + } + + // state check + ItemGroupCard card = (ItemGroupCard) other; + return dayNumber.getText().equals(card.dayNumber.getText()) + && itemGroup.equals(card.itemGroup); + } +} diff --git a/src/main/java/seedu/waddle/ui/ItemGroupListPanel.java b/src/main/java/seedu/waddle/ui/ItemGroupListPanel.java new file mode 100644 index 00000000000..dd422c18a5b --- /dev/null +++ b/src/main/java/seedu/waddle/ui/ItemGroupListPanel.java @@ -0,0 +1,48 @@ +package seedu.waddle.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 seedu.waddle.commons.core.LogsCenter; +import seedu.waddle.model.item.Item; + +/** + * Panel containing the list of Items. + */ +public class ItemGroupListPanel extends ListPanel { + private static final String FXML = "ItemGroupListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(ItemGroupListPanel.class); + + @FXML + private ListView> itemGroupListView; + + /** + * Creates a {@code ItemListPanel} with the given {@code ObservableList}. + */ + public ItemGroupListPanel(ObservableList> itemGroups) { + super(FXML); + itemGroupListView.setItems(itemGroups); + itemGroupListView.setCellFactory(listView -> new ItemGroupListPanel.ItemGroupListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Item} using a {@code ItemCard}. + */ + class ItemGroupListViewCell extends ListCell> { + @Override + protected void updateItem(ObservableList itemGroup, boolean empty) { + super.updateItem(itemGroup, empty); + + if (empty || itemGroup == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new ItemGroupCard(itemGroup, getIndex()).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/waddle/ui/ItemListPanel.java b/src/main/java/seedu/waddle/ui/ItemListPanel.java new file mode 100644 index 00000000000..35d3d50127d --- /dev/null +++ b/src/main/java/seedu/waddle/ui/ItemListPanel.java @@ -0,0 +1,52 @@ +package seedu.waddle.ui; + +import java.util.logging.Logger; + +import javafx.beans.binding.Bindings; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import seedu.waddle.commons.core.LogsCenter; +import seedu.waddle.model.item.Item; + +/** + * Panel containing the list of Items. + */ +public class ItemListPanel extends ListPanel { + private static final String FXML = "ItemListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(ItemListPanel.class); + private final int dayNumber; + + @FXML + private ListView itemListView; + + /** + * Creates a {@code ItemListPanel} with the given {@code ObservableList}. + */ + public ItemListPanel(ObservableList itemList, int dayNumber) { + super(FXML); + this.dayNumber = dayNumber; + itemListView.setItems(itemList); + itemListView.setCellFactory(listView -> new ItemListPanel.ItemListViewCell()); + itemListView.prefHeightProperty().bind(Bindings.size(itemList).multiply(UiSizes.ITEM_CARD_HEIGHT)); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Item} using a {@code ItemCard}. + */ + class ItemListViewCell extends ListCell { + @Override + protected void updateItem(Item item, boolean empty) { + super.updateItem(item, empty); + + if (empty || item == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new ItemCard(item, dayNumber, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/waddle/ui/ItineraryCard.java b/src/main/java/seedu/waddle/ui/ItineraryCard.java new file mode 100644 index 00000000000..7155197a5af --- /dev/null +++ b/src/main/java/seedu/waddle/ui/ItineraryCard.java @@ -0,0 +1,77 @@ +package seedu.waddle.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.waddle.commons.core.Text; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * An UI component that displays information of a {@code Itinerary}. + */ +public class ItineraryCard extends UiPart { + + private static final String FXML = "ItineraryListCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + public final Itinerary itinerary; + + @FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label country; + @FXML + private Label duration; + @FXML + private Label time; + @FXML + private Label people; + @FXML + private Label budget; + + + /** + * Creates a {@code ItineraryCode} with the given {@code Itinerary} and index to display. + */ + public ItineraryCard(Itinerary itinerary, int displayedIndex) { + super(FXML); + this.itinerary = itinerary; + id.setText(displayedIndex + ". "); + name.setText(itinerary.getDescriptionString(Text.INDENT_NONE)); + country.setText(itinerary.getCountryString(Text.INDENT_NONE)); + time.setText(itinerary.getTimeString(Text.INDENT_NONE)); + duration.setText(itinerary.getDurationString(Text.INDENT_NONE)); + people.setText(itinerary.getPeopleString(Text.INDENT_NONE)); + budget.textProperty().bind(itinerary.getObservableBudgetString(Text.INDENT_NONE)); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ItineraryCard)) { + return false; + } + + // state check + ItineraryCard card = (ItineraryCard) other; + return id.getText().equals(card.id.getText()) + && itinerary.equals(card.itinerary); + } +} diff --git a/src/main/java/seedu/waddle/ui/ItineraryListPanel.java b/src/main/java/seedu/waddle/ui/ItineraryListPanel.java new file mode 100644 index 00000000000..697e3503abf --- /dev/null +++ b/src/main/java/seedu/waddle/ui/ItineraryListPanel.java @@ -0,0 +1,48 @@ +package seedu.waddle.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 seedu.waddle.commons.core.LogsCenter; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * Panel containing the list of Iineraries. + */ +public class ItineraryListPanel extends ListPanel { + private static final String FXML = "ItineraryListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(ItineraryListPanel.class); + + @FXML + private ListView itineraryListView; + + /** + * Creates a {@code ItineraryListPanel} with the given {@code ObservableList}. + */ + public ItineraryListPanel(ObservableList itineraryList) { + super(FXML); + itineraryListView.setItems(itineraryList); + itineraryListView.setCellFactory(listView -> new ItineraryListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Itinerary} using a {@code ItineraryCard}. + */ + class ItineraryListViewCell extends ListCell { + @Override + protected void updateItem(Itinerary itinerary, boolean empty) { + super.updateItem(itinerary, empty); + + if (empty || itinerary == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new ItineraryCard(itinerary, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/waddle/ui/ListPanel.java b/src/main/java/seedu/waddle/ui/ListPanel.java new file mode 100644 index 00000000000..6edcdc9d2a3 --- /dev/null +++ b/src/main/java/seedu/waddle/ui/ListPanel.java @@ -0,0 +1,15 @@ +package seedu.waddle.ui; + +import javafx.scene.layout.Region; + +/** + * List panel ui + */ +public abstract class ListPanel extends UiPart { + /** + * Creates a {@code ListPanel} with the given {@code FXML}. + */ + public ListPanel(String fxml) { + super(fxml); + } +} diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/waddle/ui/MainWindow.java similarity index 72% rename from src/main/java/seedu/address/ui/MainWindow.java rename to src/main/java/seedu/waddle/ui/MainWindow.java index 9106c3aa6e5..b00e2148d4c 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/waddle/ui/MainWindow.java @@ -1,7 +1,8 @@ -package seedu.address.ui; +package seedu.waddle.ui; import java.util.logging.Logger; +import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.MenuItem; @@ -10,12 +11,15 @@ import javafx.scene.input.KeyEvent; import javafx.scene.layout.StackPane; import javafx.stage.Stage; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.logic.Logic; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.waddle.commons.core.GuiSettings; +import seedu.waddle.commons.core.LogsCenter; +import seedu.waddle.logic.Logic; +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.commands.CommandResult; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.logic.parser.exceptions.ParseException; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.itinerary.Itinerary; /** * The Main Window. Provides the basic application layout containing @@ -27,14 +31,12 @@ public class MainWindow extends UiPart { private final Logger logger = LogsCenter.getLogger(getClass()); - private Stage primaryStage; - private Logic logic; - + private final Stage primaryStage; + private final Logic logic; + private final HelpWindow helpWindow; // Independent Ui parts residing in this Ui container - private PersonListPanel personListPanel; + private ListPanel listPanel; private ResultDisplay resultDisplay; - private HelpWindow helpWindow; - @FXML private StackPane commandBoxPlaceholder; @@ -42,7 +44,7 @@ public class MainWindow extends UiPart { private MenuItem helpMenuItem; @FXML - private StackPane personListPanelPlaceholder; + private StackPane listPanelPlaceholder; @FXML private StackPane resultDisplayPlaceholder; @@ -78,6 +80,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,13 +113,13 @@ 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()); + listPanel = new ItineraryListPanel(logic.getFilteredItineraryList()); + listPanelPlaceholder.getChildren().add(listPanel.getRoot()); resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); - StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); + StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getWaddleFilePath()); statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); CommandBox commandBox = new CommandBox(this::executeCommand); @@ -163,14 +166,24 @@ private void handleExit() { primaryStage.hide(); } - public PersonListPanel getPersonListPanel() { - return personListPanel; + public ListPanel getListPanel() { + return listPanel; + } + + /** + * Changes the list panel. + */ + @FXML + public void setListPanel(ListPanel listPanel) { + this.listPanel = listPanel; + listPanelPlaceholder.getChildren().removeAll(); + listPanelPlaceholder.getChildren().add(this.listPanel.getRoot()); } /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see seedu.waddle.logic.Logic#execute(String) */ private CommandResult executeCommand(String commandText) throws CommandException, ParseException { try { @@ -186,6 +199,23 @@ private CommandResult executeCommand(String commandText) throws CommandException handleExit(); } + // if the command results in a stage change, update the listPanel + if (commandResult.hasStage()) { + switch (commandResult.getStage()) { + case HOME: + ObservableList itineraryList = logic.getFilteredItineraryList(); + setListPanel(new ItineraryListPanel(itineraryList)); + break; + case WISH: + ObservableList> itemGroups = StageManager.getInstance() + .getSelectedItinerary().getUnmodifiableItemGroups(); + setListPanel(new ItemGroupListPanel(itemGroups)); + break; + default: + break; + } + } + return commandResult; } catch (CommandException | ParseException e) { logger.info("Invalid command: " + commandText); diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/waddle/ui/ResultDisplay.java similarity index 90% rename from src/main/java/seedu/address/ui/ResultDisplay.java rename to src/main/java/seedu/waddle/ui/ResultDisplay.java index 7d98e84eedf..ef0d999516a 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/seedu/waddle/ui/ResultDisplay.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.waddle.ui; import static java.util.Objects.requireNonNull; @@ -23,6 +23,7 @@ public ResultDisplay() { public void setFeedbackToUser(String feedbackToUser) { requireNonNull(feedbackToUser); resultDisplay.setText(feedbackToUser); + resultDisplay.setWrapText(true); } } diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/waddle/ui/StatusBarFooter.java similarity index 96% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/seedu/waddle/ui/StatusBarFooter.java index b577f829423..af78ac6faea 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/waddle/ui/StatusBarFooter.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.waddle.ui; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/seedu/waddle/ui/Ui.java similarity index 86% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/seedu/waddle/ui/Ui.java index 17aa0b494fe..70351e5ed1a 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/seedu/waddle/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.waddle.ui; import javafx.stage.Stage; diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/waddle/ui/UiManager.java similarity index 91% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/seedu/waddle/ui/UiManager.java index fdf024138bc..39b5105b476 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/waddle/ui/UiManager.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.waddle.ui; import java.util.logging.Logger; @@ -7,10 +7,10 @@ import javafx.scene.control.Alert.AlertType; import javafx.scene.image.Image; import javafx.stage.Stage; -import seedu.address.MainApp; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; +import seedu.waddle.MainApp; +import seedu.waddle.commons.core.LogsCenter; +import seedu.waddle.commons.util.StringUtil; +import seedu.waddle.logic.Logic; /** * The manager of the UI component. @@ -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/duck.png"; private Logic logic; private MainWindow mainWindow; diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/waddle/ui/UiPart.java similarity index 97% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/seedu/waddle/ui/UiPart.java index fc820e01a9c..8cc217130fe 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/waddle/ui/UiPart.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.waddle.ui; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.net.URL; import javafx.fxml.FXMLLoader; -import seedu.address.MainApp; +import seedu.waddle.MainApp; /** * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. diff --git a/src/main/java/seedu/waddle/ui/UiSizes.java b/src/main/java/seedu/waddle/ui/UiSizes.java new file mode 100644 index 00000000000..29caea431bb --- /dev/null +++ b/src/main/java/seedu/waddle/ui/UiSizes.java @@ -0,0 +1,9 @@ +package seedu.waddle.ui; + +/** + * Standard sizes for UI elements. + */ +public class UiSizes { + public static final double ITEM_CARD_HEIGHT = 130; + public static final double ITEM_LIST_MIN_HEIGHT = 20; +} diff --git a/src/main/resources/images/duck.png b/src/main/resources/images/duck.png new file mode 100644 index 00000000000..67fccd340d8 Binary files /dev/null and b/src/main/resources/images/duck.png differ diff --git a/src/main/resources/template/waddle_template.pdf b/src/main/resources/template/waddle_template.pdf new file mode 100644 index 00000000000..141e6cc4407 Binary files /dev/null and b/src/main/resources/template/waddle_template.pdf differ diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 36e6b001cd8..30ef67d07ff 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 */ + -fx-background-color: #fffae5, 20%; + background-color: #fffae5; /* Used in the default.html file */ } .label { @@ -13,14 +13,14 @@ .label-bright { -fx-font-size: 11pt; -fx-font-family: "Segoe UI Semibold"; - -fx-text-fill: white; + -fx-text-fill: black; -fx-opacity: 1; } .label-header { -fx-font-size: 32pt; -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; + -fx-text-fill: black; -fx-opacity: 1; } @@ -33,16 +33,16 @@ -fx-padding: 0 0 0 1; } -.tab-pane .tab-header-area { +.tab-pane .tab-h#resultDisplay { -fx-padding: 0 0 0 0; -fx-min-height: 0; -fx-max-height: 0; } .table-view { - -fx-base: #1d1d1d; - -fx-control-inner-background: #1d1d1d; - -fx-background-color: #1d1d1d; + -fx-base: #fffae5; + -fx-control-inner-background: #fffae5; + -fx-background-color: #fffae5; -fx-table-cell-border-color: transparent; -fx-table-header-border-color: transparent; -fx-padding: 5; @@ -67,7 +67,7 @@ .table-view .column-header .label { -fx-font-size: 20pt; -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; + -fx-text-fill: black; -fx-alignment: center-left; -fx-opacity: 1; } @@ -77,134 +77,187 @@ } .split-pane:horizontal .split-pane-divider { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#fffae5, 20%); -fx-border-color: transparent transparent transparent #4d4d4d; } .split-pane { -fx-border-radius: 1; -fx-border-width: 1; - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#fffae5, 20%); } -.list-view { +.itinerary-list-view { -fx-background-insets: 0; -fx-padding: 0; - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#fffae5, 20%); + -fx-border-color: black; + -fx-border-width: 1; + -fx-border-radius: 10; + -fx-padding: 0 0 0 5; } -.list-cell { - -fx-label-padding: 0 0 0 0; - -fx-graphic-text-gap : 0; - -fx-padding: 0 0 0 0; +.item-group-list-view { + -fx-background-insets: 0; + -fx-padding: 0; + -fx-background-color: derive(#fffae5, 20%); + -fx-border-color: black; + -fx-border-width: 1; + -fx-border-radius: 10; + -fx-padding: 0 0 0 5; } -.list-cell:filled:even { - -fx-background-color: #3c3e3f; +.item-list-view { + -fx-background-insets: 0; + -fx-padding: 0; + -fx-background-color: transparent; } -.list-cell:filled:odd { - -fx-background-color: #515658; +.item-card { + -fx-background-color: #fffae5; + -fx-text-fill: black; + -fx-background-radius: 10px; } -.list-cell:filled:selected { - -fx-background-color: #424d5f; +.list-cell:filled { + -fx-background-color: #ffb238; + -fx-padding: 5px; + -fx-background-insets: 5; + -fx-background-radius: 10px; } .list-cell:filled:selected #cardPane { - -fx-border-color: #3e7b91; + -fx-background-color: #ff9d5b; -fx-border-width: 1; -} - -.list-cell .label { - -fx-text-fill: white; + -fx-border-color: black; + -fx-border-radius: 10px; + -fx-background-radius: 10px; } .cell_big_label { - -fx-font-family: "Segoe UI Semibold"; - -fx-font-size: 16px; - -fx-text-fill: #010504; + -fx-font-family: "Segoe UI Bold"; + -fx-font-size: 20px; + -fx-text-fill: black; + -fx-padding: 0 0 5 0 } .cell_small_label { -fx-font-family: "Segoe UI"; - -fx-font-size: 13px; - -fx-text-fill: #010504; + -fx-font-size: 15px; + -fx-text-fill: black; + -fx-padding: 0 0 0 0 +} + +.day-number { + -fx-font-family: "Segoe UI Bold"; + -fx-font-size: 30px; + -fx-text-fill: White; + -fx-padding: 0 0 0 10 + } + +.item_cell_big_label { + -fx-font-family: "Segoe UI Bold"; + -fx-font-size: 20px; + -fx-text-fill: black; + -fx-padding: 0 0 5 0 +} + +.item_cell_small_label { + -fx-font-family: "Segoe UI"; + -fx-font-size: 15px; + -fx-text-fill: black; + -fx-padding: 0 0 0 0 } .stack-pane { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-text-fill: black; + -fx-background-color: derive(#fffae5, 20%); } .pane-with-border { - -fx-background-color: derive(#1d1d1d, 20%); - -fx-border-color: derive(#1d1d1d, 10%); - -fx-border-top-width: 1px; + -fx-border-color: transparent; + -fx-background-color: #fffae5; } .status-bar { - -fx-background-color: derive(#1d1d1d, 30%); + -fx-background-color: derive(#d1cdc0, 30%); } .result-display { - -fx-background-color: transparent; -fx-font-family: "Segoe UI Light"; -fx-font-size: 13pt; - -fx-text-fill: white; + -fx-text-fill: black; + -fx-border-width: 0px; + -fx-border-color: transparent; } .result-display .label { -fx-text-fill: black !important; + -fx-border-width: 0px; + -fx-border-color: transparent; } .status-bar .label { -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; + -fx-text-fill: black; -fx-padding: 4px; -fx-pref-height: 30px; } .status-bar-with-border { - -fx-background-color: derive(#1d1d1d, 30%); - -fx-border-color: derive(#1d1d1d, 25%); + -fx-background-color: #d1cdc0; + -fx-border-color: black; -fx-border-width: 1px; } .status-bar-with-border .label { - -fx-text-fill: white; + -fx-text-fill: black; } .grid-pane { - -fx-background-color: derive(#1d1d1d, 30%); - -fx-border-color: derive(#1d1d1d, 30%); + -fx-background-color: #fffae5; + -fx-border-color: black; -fx-border-width: 1px; } .grid-pane .stack-pane { - -fx-background-color: derive(#1d1d1d, 30%); + -fx-background-color: #fffae5; } .context-menu { - -fx-background-color: derive(#1d1d1d, 50%); + -fx-background-color: #fffae5; } .context-menu .label { - -fx-text-fill: white; + -fx-text-fill: black; } .menu-bar { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: #d1cdc0; } .menu-bar .label { -fx-font-size: 14pt; -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; + -fx-text-fill: black; -fx-opacity: 0.9; } -.menu .left-container { - -fx-background-color: black; + +.menu:showing { + -fx-background-color: #ffc37a; +} + +.menu:hover { + -fx-background-color: #ffc37a; +} + +.menu-item:hover { + -fx-background-color: #ffc37a; +} + +.menu-item { + -fx-background-color: #fffae5; } /* @@ -217,7 +270,7 @@ -fx-border-color: #e2e2e2; -fx-border-width: 2; -fx-background-radius: 0; - -fx-background-color: #1d1d1d; + -fx-background-color: red; -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif; -fx-font-size: 11pt; -fx-text-fill: #d8d8d8; @@ -225,12 +278,12 @@ } .button:hover { - -fx-background-color: #3a3a3a; + -fx-background-color: #fffae5; } .button:pressed, .button:default:hover:pressed { -fx-background-color: white; - -fx-text-fill: #1d1d1d; + -fx-text-fill: #fffae5; } .button:focused { @@ -243,8 +296,8 @@ .button:disabled, .button:default:disabled { -fx-opacity: 0.4; - -fx-background-color: #1d1d1d; - -fx-text-fill: white; + -fx-background-color: #fffae5; + -fx-text-fill: grey; } .button:default { @@ -257,11 +310,11 @@ } .dialog-pane { - -fx-background-color: #1d1d1d; + -fx-background-color: #fffae5; } .dialog-pane > *.button-bar > *.container { - -fx-background-color: #1d1d1d; + -fx-background-color: #fffae5; } .dialog-pane > *.label.content { @@ -271,7 +324,7 @@ } .dialog-pane:header *.header-panel { - -fx-background-color: derive(#1d1d1d, 25%); + -fx-background-color: derive(#fffae5, 25%); } .dialog-pane:header *.header-panel *.label { @@ -282,12 +335,14 @@ } .scroll-bar { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: transparent; + -fx-border-radius: 20; } .scroll-bar .thumb { -fx-background-color: derive(#1d1d1d, 50%); -fx-background-insets: 3; + -fx-border-radius: 20; } .scroll-bar .increment-button, .scroll-bar .decrement-button { @@ -307,25 +362,21 @@ -fx-padding: 8 1 8 1; } -#cardPane { - -fx-background-color: transparent; - -fx-border-width: 0; -} - #commandTypeLabel { -fx-font-size: 11px; -fx-text-fill: #F70D1A; } #commandTextField { - -fx-background-color: transparent #383838 transparent #383838; + -fx-background-color: transparent; -fx-background-insets: 0; - -fx-border-color: #383838 #383838 #ffffff #383838; + -fx-border-color: black; -fx-border-insets: 0; -fx-border-width: 1; + -fx-border-radius: 10; -fx-font-family: "Segoe UI Light"; -fx-font-size: 13pt; - -fx-text-fill: white; + -fx-text-fill: black; } #filterField, #personListPanel, #personWebpage { @@ -333,10 +384,26 @@ } #resultDisplay .content { - -fx-background-color: transparent, #383838, transparent, #383838; - -fx-background-radius: 0; + -fx-background-color: derive(#fffae5, 25%); +} + +#resultDisplay { + -fx-background-color: derive(#fffae5, 25%); + -fx-border-color: black; + -fx-border-width: 1px; + -fx-border-radius: 10px; + -fx-padding: 2px; } + + + + + + + + + #tags { -fx-hgap: 7; -fx-vgap: 3; diff --git a/src/main/resources/view/Extensions.css b/src/main/resources/view/Extensions.css index bfe82a85964..6ac12a149d5 100644 --- a/src/main/resources/view/Extensions.css +++ b/src/main/resources/view/Extensions.css @@ -5,7 +5,7 @@ .list-cell:empty { /* Empty cells will not have alternating colours */ - -fx-background: #383838; + -fx-background: transparent; } .tag-selector { diff --git a/src/main/resources/view/HelpWindow.css b/src/main/resources/view/HelpWindow.css index 17e8a8722cd..9a9db129132 100644 --- a/src/main/resources/view/HelpWindow.css +++ b/src/main/resources/view/HelpWindow.css @@ -1,5 +1,5 @@ #copyButton, #helpMessage { - -fx-text-fill: white; + -fx-text-fill: black; } #copyButton { @@ -15,5 +15,5 @@ } #helpMessageContainer { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: fffae5; } diff --git a/src/main/resources/view/ItemGroupListCard.fxml b/src/main/resources/view/ItemGroupListCard.fxml new file mode 100644 index 00000000000..d248a6eeb6d --- /dev/null +++ b/src/main/resources/view/ItemGroupListCard.fxml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/src/main/resources/view/ItemGroupListPanel.fxml b/src/main/resources/view/ItemGroupListPanel.fxml new file mode 100644 index 00000000000..c4c9505e57e --- /dev/null +++ b/src/main/resources/view/ItemGroupListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/view/ItemListCard.fxml b/src/main/resources/view/ItemListCard.fxml new file mode 100644 index 00000000000..2a4a0111145 --- /dev/null +++ b/src/main/resources/view/ItemListCard.fxml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/ItemListPanel.fxml similarity index 70% rename from src/main/resources/view/PersonListPanel.fxml rename to src/main/resources/view/ItemListPanel.fxml index 8836d323cc5..ed57895651a 100644 --- a/src/main/resources/view/PersonListPanel.fxml +++ b/src/main/resources/view/ItemListPanel.fxml @@ -4,5 +4,5 @@ - + diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/ItineraryListCard.fxml similarity index 71% rename from src/main/resources/view/PersonListCard.fxml rename to src/main/resources/view/ItineraryListCard.fxml index f08ea32ad55..7b0c3358151 100644 --- a/src/main/resources/view/PersonListCard.fxml +++ b/src/main/resources/view/ItineraryListCard.fxml @@ -9,7 +9,7 @@ - + @@ -27,10 +27,11 @@ - - diff --git a/src/main/resources/view/ItineraryListPanel.fxml b/src/main/resources/view/ItineraryListPanel.fxml new file mode 100644 index 00000000000..1f8c80c8dad --- /dev/null +++ b/src/main/resources/view/ItineraryListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index a431648f6c0..a46d6dd5735 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -12,9 +12,9 @@ + title="Waddle" minWidth="450" minHeight="600" onCloseRequest="#handleExit"> - + @@ -39,8 +39,8 @@ - + @@ -50,7 +50,7 @@ - + diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json deleted file mode 100644 index 48831cc7674..00000000000 --- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "persons": [ { - "name": "Alice Pauline", - "phone": "94351253", - "email": "alice@example.com", - "address": "123, Jurong West Ave 6, #08-111", - "tagged": [ "friends" ] - }, { - "name": "Alice Pauline", - "phone": "94351253", - "email": "pauline@example.com", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json deleted file mode 100644 index ad3f135ae42..00000000000 --- a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "persons": [ { - "name": "Hans Muster", - "phone": "9482424", - "email": "invalid@email!3e", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json deleted file mode 100644 index f10eddee12e..00000000000 --- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()", - "persons" : [ { - "name" : "Alice Pauline", - "phone" : "94351253", - "email" : "alice@example.com", - "address" : "123, Jurong West Ave 6, #08-111", - "tagged" : [ "friends" ] - }, { - "name" : "Benson Meier", - "phone" : "98765432", - "email" : "johnd@example.com", - "address" : "311, Clementi Ave 2, #02-25", - "tagged" : [ "owesMoney", "friends" ] - }, { - "name" : "Carl Kurz", - "phone" : "95352563", - "email" : "heinz@example.com", - "address" : "wall street", - "tagged" : [ ] - }, { - "name" : "Daniel Meier", - "phone" : "87652533", - "email" : "cornelia@example.com", - "address" : "10th street", - "tagged" : [ "friends" ] - }, { - "name" : "Elle Meyer", - "phone" : "9482224", - "email" : "werner@example.com", - "address" : "michegan ave", - "tagged" : [ ] - }, { - "name" : "Fiona Kunz", - "phone" : "9482427", - "email" : "lydia@example.com", - "address" : "little tokyo", - "tagged" : [ ] - }, { - "name" : "George Best", - "phone" : "9482442", - "email" : "anna@example.com", - "address" : "4th street", - "tagged" : [ ] - } ] -} diff --git a/src/test/data/JsonSerializableWaddleTest/duplicateItineraryWaddle.json b/src/test/data/JsonSerializableWaddleTest/duplicateItineraryWaddle.json new file mode 100644 index 00000000000..ca57270b137 --- /dev/null +++ b/src/test/data/JsonSerializableWaddleTest/duplicateItineraryWaddle.json @@ -0,0 +1,171 @@ +{ + "itineraries" : [ { + "description" : "Spring Trip", + "country" : "Australia", + "startDate" : "2023-01-01", + "duration" : "25", + "people" : "1", + "budget" : "30.0", + "items" : [ ], + "days" : [ { + "dayNumber" : 0, + "items" : [ ] + }, { + "dayNumber" : 1, + "items" : [ ] + }, { + "dayNumber" : 2, + "items" : [ ] + }, { + "dayNumber" : 3, + "items" : [ ] + }, { + "dayNumber" : 4, + "items" : [ ] + }, { + "dayNumber" : 5, + "items" : [ ] + }, { + "dayNumber" : 6, + "items" : [ ] + }, { + "dayNumber" : 7, + "items" : [ ] + }, { + "dayNumber" : 8, + "items" : [ ] + }, { + "dayNumber" : 9, + "items" : [ ] + }, { + "dayNumber" : 10, + "items" : [ ] + }, { + "dayNumber" : 11, + "items" : [ ] + }, { + "dayNumber" : 12, + "items" : [ ] + }, { + "dayNumber" : 13, + "items" : [ ] + }, { + "dayNumber" : 14, + "items" : [ ] + }, { + "dayNumber" : 15, + "items" : [ ] + }, { + "dayNumber" : 16, + "items" : [ ] + }, { + "dayNumber" : 17, + "items" : [ ] + }, { + "dayNumber" : 18, + "items" : [ ] + }, { + "dayNumber" : 19, + "items" : [ ] + }, { + "dayNumber" : 20, + "items" : [ ] + }, { + "dayNumber" : 21, + "items" : [ ] + }, { + "dayNumber" : 22, + "items" : [ ] + }, { + "dayNumber" : 23, + "items" : [ ] + }, { + "dayNumber" : 24, + "items" : [ ] + } ] + }, { + "description" : "Spring Trip", + "country" : "Australia", + "startDate" : "2023-01-01", + "duration" : "25", + "people" : "1", + "budget" : "30.0", + "items" : [ ], + "days" : [ { + "dayNumber" : 0, + "items" : [ ] + }, { + "dayNumber" : 1, + "items" : [ ] + }, { + "dayNumber" : 2, + "items" : [ ] + }, { + "dayNumber" : 3, + "items" : [ ] + }, { + "dayNumber" : 4, + "items" : [ ] + }, { + "dayNumber" : 5, + "items" : [ ] + }, { + "dayNumber" : 6, + "items" : [ ] + }, { + "dayNumber" : 7, + "items" : [ ] + }, { + "dayNumber" : 8, + "items" : [ ] + }, { + "dayNumber" : 9, + "items" : [ ] + }, { + "dayNumber" : 10, + "items" : [ ] + }, { + "dayNumber" : 11, + "items" : [ ] + }, { + "dayNumber" : 12, + "items" : [ ] + }, { + "dayNumber" : 13, + "items" : [ ] + }, { + "dayNumber" : 14, + "items" : [ ] + }, { + "dayNumber" : 15, + "items" : [ ] + }, { + "dayNumber" : 16, + "items" : [ ] + }, { + "dayNumber" : 17, + "items" : [ ] + }, { + "dayNumber" : 18, + "items" : [ ] + }, { + "dayNumber" : 19, + "items" : [ ] + }, { + "dayNumber" : 20, + "items" : [ ] + }, { + "dayNumber" : 21, + "items" : [ ] + }, { + "dayNumber" : 22, + "items" : [ ] + }, { + "dayNumber" : 23, + "items" : [ ] + }, { + "dayNumber" : 24, + "items" : [ ] + } ] + } ] +} diff --git a/src/test/data/JsonSerializableWaddleTest/invalidItineraryWaddle.json b/src/test/data/JsonSerializableWaddleTest/invalidItineraryWaddle.json new file mode 100644 index 00000000000..8a6a438f064 --- /dev/null +++ b/src/test/data/JsonSerializableWaddleTest/invalidItineraryWaddle.json @@ -0,0 +1,11 @@ +{ + "itineraries" : [ { + "description" : "Spring Trip", + "country" : "Australia", + "startDate" : "10", + "duration" : "20d", + "people" : "1", + "items" : [ ], + "days" : [ ] + } ] +} diff --git a/src/test/data/JsonSerializableWaddleTest/typicalItinerariesWaddle.json b/src/test/data/JsonSerializableWaddleTest/typicalItinerariesWaddle.json new file mode 100644 index 00000000000..31931732ac0 --- /dev/null +++ b/src/test/data/JsonSerializableWaddleTest/typicalItinerariesWaddle.json @@ -0,0 +1,151 @@ +{ + "_comment": "Waddle save file which contains the same Itinerary values as in TypicalItineraries#getTypicalWaddle()", + "itineraries" : [ { + "description" : "Spring Trip", + "country" : "Australia", + "startDate" : "2023-01-01", + "duration" : "14", + "people" : "1", + "budget" : "300.0", + "items" : [ ], + "days" : [ { + "dayNumber" : 0, + "items" : [ ] + }, { + "dayNumber" : 1, + "items" : [ ] + }, { + "dayNumber" : 2, + "items" : [ ] + }, { + "dayNumber" : 3, + "items" : [ ] + }, { + "dayNumber" : 4, + "items" : [ ] + }, { + "dayNumber" : 5, + "items" : [ ] + }, { + "dayNumber" : 6, + "items" : [ ] + }, { + "dayNumber" : 7, + "items" : [ ] + }, { + "dayNumber" : 8, + "items" : [ ] + }, { + "dayNumber" : 9, + "items" : [ ] + }, { + "dayNumber" : 10, + "items" : [ ] + }, { + "dayNumber" : 11, + "items" : [ ] + }, { + "dayNumber" : 12, + "items" : [ ] + }, { + "dayNumber" : 13, + "items" : [ ] + } ] + }, { + "description" : "Autumn Hiking", + "country" : "Canada", + "startDate" : "2023-02-02", + "duration" : "22", + "people" : "2", + "budget" : "700.0", + "items" : [ ], + "days" : [ { + "dayNumber" : 0, + "items" : [ ] + }, { + "dayNumber" : 1, + "items" : [ ] + }, { + "dayNumber" : 2, + "items" : [ ] + }, { + "dayNumber" : 3, + "items" : [ ] + }, { + "dayNumber" : 4, + "items" : [ ] + }, { + "dayNumber" : 5, + "items" : [ ] + }, { + "dayNumber" : 6, + "items" : [ ] + }, { + "dayNumber" : 7, + "items" : [ ] + }, { + "dayNumber" : 8, + "items" : [ ] + }, { + "dayNumber" : 9, + "items" : [ ] + }, { + "dayNumber" : 10, + "items" : [ ] + }, { + "dayNumber" : 11, + "items" : [ ] + }, { + "dayNumber" : 12, + "items" : [ ] + }, { + "dayNumber" : 13, + "items" : [ ] + }, { + "dayNumber" : 14, + "items" : [ ] + }, { + "dayNumber" : 15, + "items" : [ ] + }, { + "dayNumber" : 16, + "items" : [ ] + }, { + "dayNumber" : 17, + "items" : [ ] + }, { + "dayNumber" : 18, + "items" : [ ] + }, { + "dayNumber" : 19, + "items" : [ ] + }, { + "dayNumber" : 20, + "items" : [ ] + }, { + "dayNumber" : 21, + "items" : [ ] + } ] + }, { + "description" : "Graduation Trip", + "country" : "France", + "startDate" : "2023-03-03", + "duration" : "4", + "people" : "4", + "budget" : "2200.0", + "items" : [ ], + "days" : [ { + "dayNumber" : 0, + "items" : [ ] + }, { + "dayNumber" : 1, + "items" : [ ] + }, { + "dayNumber" : 2, + "items" : [ ] + }, { + "dayNumber" : 3, + "items" : [ ] + } ] + } ] +} diff --git a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json index b819bed900a..fcff695566f 100644 --- a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json @@ -7,5 +7,5 @@ "y" : 100 } }, - "addressBookFilePath" : "addressbook.json" + "addressBookFilePath" : "waddle.json" } diff --git a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json b/src/test/data/JsonWaddleStorageTest/invalidAndValidItineraryWaddle.json similarity index 100% rename from src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json rename to src/test/data/JsonWaddleStorageTest/invalidAndValidItineraryWaddle.json diff --git a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json b/src/test/data/JsonWaddleStorageTest/invalidItineraryWaddle.json similarity index 100% rename from src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json rename to src/test/data/JsonWaddleStorageTest/invalidItineraryWaddle.json diff --git a/src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json b/src/test/data/JsonWaddleStorageTest/notJsonFormatWaddle.json similarity index 100% rename from src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json rename to src/test/data/JsonWaddleStorageTest/notJsonFormatWaddle.json diff --git a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java deleted file mode 100644 index cb8714bb055..00000000000 --- a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package seedu.address.logic.commands; - -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.PersonBuilder; - -/** - * Contains integration tests (interaction with the Model) for {@code AddCommand}. - */ -public class AddCommandIntegrationTest { - - private Model model; - - @BeforeEach - public void setUp() { - model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - } - - @Test - public void execute_newPerson_success() { - Person validPerson = new PersonBuilder().build(); - - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.addPerson(validPerson); - - assertCommandSuccess(new AddCommand(validPerson), model, - String.format(AddCommand.MESSAGE_SUCCESS, validPerson), expectedModel); - } - - @Test - public void execute_duplicatePerson_throwsCommandException() { - Person personInList = model.getAddressBook().getPersonList().get(0); - assertCommandFailure(new AddCommand(personInList), model, AddCommand.MESSAGE_DUPLICATE_PERSON); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java deleted file mode 100644 index 5865713d5dd..00000000000 --- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java +++ /dev/null @@ -1,194 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.function.Predicate; - -import org.junit.jupiter.api.Test; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.PersonBuilder; - -public class AddCommandTest { - - @Test - public void constructor_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new AddCommand(null)); - } - - @Test - public void execute_personAcceptedByModel_addSuccessful() throws Exception { - ModelStubAcceptingPersonAdded modelStub = new ModelStubAcceptingPersonAdded(); - Person validPerson = new PersonBuilder().build(); - - CommandResult commandResult = new AddCommand(validPerson).execute(modelStub); - - assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, validPerson), commandResult.getFeedbackToUser()); - assertEquals(Arrays.asList(validPerson), modelStub.personsAdded); - } - - @Test - public void execute_duplicatePerson_throwsCommandException() { - Person validPerson = new PersonBuilder().build(); - AddCommand addCommand = new AddCommand(validPerson); - ModelStub modelStub = new ModelStubWithPerson(validPerson); - - assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_PERSON, () -> addCommand.execute(modelStub)); - } - - @Test - public void equals() { - Person alice = new PersonBuilder().withName("Alice").build(); - Person bob = new PersonBuilder().withName("Bob").build(); - AddCommand addAliceCommand = new AddCommand(alice); - AddCommand addBobCommand = new AddCommand(bob); - - // same object -> returns true - assertTrue(addAliceCommand.equals(addAliceCommand)); - - // same values -> returns true - AddCommand addAliceCommandCopy = new AddCommand(alice); - assertTrue(addAliceCommand.equals(addAliceCommandCopy)); - - // different types -> returns false - assertFalse(addAliceCommand.equals(1)); - - // null -> returns false - assertFalse(addAliceCommand.equals(null)); - - // different person -> returns false - assertFalse(addAliceCommand.equals(addBobCommand)); - } - - /** - * A default model stub that have all of the methods failing. - */ - private class ModelStub implements Model { - @Override - public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ReadOnlyUserPrefs getUserPrefs() { - throw new AssertionError("This method should not be called."); - } - - @Override - public GuiSettings getGuiSettings() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setGuiSettings(GuiSettings guiSettings) { - throw new AssertionError("This method should not be called."); - } - - @Override - public Path getAddressBookFilePath() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void addPerson(Person person) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setAddressBook(ReadOnlyAddressBook newData) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - throw new AssertionError("This method should not be called."); - } - - @Override - public boolean hasPerson(Person person) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void deletePerson(Person target) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setPerson(Person target, Person editedPerson) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ObservableList getFilteredPersonList() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void updateFilteredPersonList(Predicate predicate) { - throw new AssertionError("This method should not be called."); - } - } - - /** - * A Model stub that contains a single person. - */ - private class ModelStubWithPerson extends ModelStub { - private final Person person; - - ModelStubWithPerson(Person person) { - requireNonNull(person); - this.person = person; - } - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return this.person.isSamePerson(person); - } - } - - /** - * A Model stub that always accept the person being added. - */ - private class ModelStubAcceptingPersonAdded extends ModelStub { - final ArrayList personsAdded = new ArrayList<>(); - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return personsAdded.stream().anyMatch(person::isSamePerson); - } - - @Override - public void addPerson(Person person) { - requireNonNull(person); - personsAdded.add(person); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return new AddressBook(); - } - } - -} diff --git a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java deleted file mode 100644 index 80d9110c03a..00000000000 --- a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package seedu.address.logic.commands; - -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.Test; - -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; - -public class ClearCommandTest { - - @Test - public void execute_emptyAddressBook_success() { - Model model = new ModelManager(); - Model expectedModel = new ModelManager(); - - assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); - } - - @Test - public void execute_nonEmptyAddressBook_success() { - Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - expectedModel.setAddressBook(new AddressBook()); - - assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java deleted file mode 100644 index 643a1d08069..00000000000 --- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java +++ /dev/null @@ -1,128 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -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.testutil.Assert.assertThrows; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; - -/** - * Contains helper methods for testing commands. - */ -public class CommandTestUtil { - - public static final String VALID_NAME_AMY = "Amy Bee"; - public static final String VALID_NAME_BOB = "Bob Choo"; - public static final String VALID_PHONE_AMY = "11111111"; - public static final String VALID_PHONE_BOB = "22222222"; - public static final String VALID_EMAIL_AMY = "amy@example.com"; - public static final String VALID_EMAIL_BOB = "bob@example.com"; - public static final String VALID_ADDRESS_AMY = "Block 312, Amy Street 1"; - public static final String VALID_ADDRESS_BOB = "Block 123, Bobby Street 3"; - public static final String VALID_TAG_HUSBAND = "husband"; - public static final String VALID_TAG_FRIEND = "friend"; - - public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY; - public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB; - public static final String PHONE_DESC_AMY = " " + PREFIX_PHONE + VALID_PHONE_AMY; - public static final String PHONE_DESC_BOB = " " + PREFIX_PHONE + VALID_PHONE_BOB; - public static final String EMAIL_DESC_AMY = " " + PREFIX_EMAIL + VALID_EMAIL_AMY; - public static final String EMAIL_DESC_BOB = " " + PREFIX_EMAIL + VALID_EMAIL_BOB; - public static final String ADDRESS_DESC_AMY = " " + PREFIX_ADDRESS + VALID_ADDRESS_AMY; - public static final String ADDRESS_DESC_BOB = " " + PREFIX_ADDRESS + VALID_ADDRESS_BOB; - public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND; - public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND; - - public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed in names - public static final String INVALID_PHONE_DESC = " " + PREFIX_PHONE + "911a"; // 'a' not allowed in phones - public static final String INVALID_EMAIL_DESC = " " + PREFIX_EMAIL + "bob!yahoo"; // missing '@' symbol - public static final String INVALID_ADDRESS_DESC = " " + PREFIX_ADDRESS; // empty string not allowed for addresses - public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in tags - - public static final String PREAMBLE_WHITESPACE = "\t \r \n"; - public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble"; - - public static final EditCommand.EditPersonDescriptor DESC_AMY; - public static final EditCommand.EditPersonDescriptor DESC_BOB; - - static { - DESC_AMY = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) - .withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) - .withTags(VALID_TAG_FRIEND).build(); - DESC_BOB = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) - .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB) - .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); - } - - /** - * Executes the given {@code command}, confirms that
- * - the returned {@link CommandResult} matches {@code expectedCommandResult}
- * - the {@code actualModel} matches {@code expectedModel} - */ - public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult, - Model expectedModel) { - try { - CommandResult result = command.execute(actualModel); - assertEquals(expectedCommandResult, result); - assertEquals(expectedModel, actualModel); - } catch (CommandException ce) { - throw new AssertionError("Execution of command should not fail.", ce); - } - } - - /** - * Convenience wrapper to {@link #assertCommandSuccess(Command, Model, CommandResult, Model)} - * that takes a string {@code expectedMessage}. - */ - public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage, - Model expectedModel) { - CommandResult expectedCommandResult = new CommandResult(expectedMessage); - assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel); - } - - /** - * Executes the given {@code command}, confirms that
- * - a {@code CommandException} is thrown
- * - the CommandException message matches {@code expectedMessage}
- * - the address book, filtered person list and selected person in {@code actualModel} remain unchanged - */ - public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) { - // we are unable to defensively copy the model for comparison later, so we can - // only do so by copying its components. - AddressBook expectedAddressBook = new AddressBook(actualModel.getAddressBook()); - List expectedFilteredList = new ArrayList<>(actualModel.getFilteredPersonList()); - - assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel)); - assertEquals(expectedAddressBook, actualModel.getAddressBook()); - assertEquals(expectedFilteredList, actualModel.getFilteredPersonList()); - } - /** - * Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the - * {@code model}'s address book. - */ - public static void showPersonAtIndex(Model model, Index targetIndex) { - assertTrue(targetIndex.getZeroBased() < model.getFilteredPersonList().size()); - - Person person = model.getFilteredPersonList().get(targetIndex.getZeroBased()); - final String[] splitName = person.getName().fullName.split("\\s+"); - model.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); - - assertEquals(1, model.getFilteredPersonList().size()); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java deleted file mode 100644 index 45a8c910ba1..00000000000 --- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; - -/** - * Contains integration tests (interaction with the Model) and unit tests for - * {@code DeleteCommand}. - */ -public class DeleteCommandTest { - - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - - @Test - public void execute_validIndexUnfilteredList_success() { - Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); - - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); - - ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.deletePerson(personToDelete); - - assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_invalidIndexUnfilteredList_throwsCommandException() { - Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); - DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); - - assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - @Test - public void execute_validIndexFilteredList_success() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - - Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); - - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); - - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.deletePerson(personToDelete); - showNoPerson(expectedModel); - - assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_invalidIndexFilteredList_throwsCommandException() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - - Index outOfBoundIndex = INDEX_SECOND_PERSON; - // ensures that outOfBoundIndex is still in bounds of address book list - assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size()); - - DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); - - assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - @Test - public void equals() { - DeleteCommand deleteFirstCommand = new DeleteCommand(INDEX_FIRST_PERSON); - DeleteCommand deleteSecondCommand = new DeleteCommand(INDEX_SECOND_PERSON); - - // same object -> returns true - assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); - - // same values -> returns true - DeleteCommand deleteFirstCommandCopy = new DeleteCommand(INDEX_FIRST_PERSON); - assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); - - // different types -> returns false - assertFalse(deleteFirstCommand.equals(1)); - - // null -> returns false - assertFalse(deleteFirstCommand.equals(null)); - - // different person -> returns false - assertFalse(deleteFirstCommand.equals(deleteSecondCommand)); - } - - /** - * Updates {@code model}'s filtered list to show no one. - */ - private void showNoPerson(Model model) { - model.updateFilteredPersonList(p -> false); - - assertTrue(model.getFilteredPersonList().isEmpty()); - } -} diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/EditCommandTest.java deleted file mode 100644 index 214c6c2507b..00000000000 --- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java +++ /dev/null @@ -1,173 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; -import seedu.address.testutil.PersonBuilder; - -/** - * Contains integration tests (interaction with the Model) and unit tests for EditCommand. - */ -public class EditCommandTest { - - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - - @Test - public void execute_allFieldsSpecifiedUnfilteredList_success() { - Person editedPerson = new PersonBuilder().build(); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(editedPerson).build(); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, descriptor); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_someFieldsSpecifiedUnfilteredList_success() { - Index indexLastPerson = Index.fromOneBased(model.getFilteredPersonList().size()); - Person lastPerson = model.getFilteredPersonList().get(indexLastPerson.getZeroBased()); - - PersonBuilder personInList = new PersonBuilder(lastPerson); - Person editedPerson = personInList.withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) - .withTags(VALID_TAG_HUSBAND).build(); - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) - .withPhone(VALID_PHONE_BOB).withTags(VALID_TAG_HUSBAND).build(); - EditCommand editCommand = new EditCommand(indexLastPerson, descriptor); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(lastPerson, editedPerson); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_noFieldSpecifiedUnfilteredList_success() { - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, new EditPersonDescriptor()); - Person editedPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_filteredList_success() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - - Person personInFilteredList = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - Person editedPerson = new PersonBuilder(personInFilteredList).withName(VALID_NAME_BOB).build(); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, - new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_duplicatePersonUnfilteredList_failure() { - Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(firstPerson).build(); - EditCommand editCommand = new EditCommand(INDEX_SECOND_PERSON, descriptor); - - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); - } - - @Test - public void execute_duplicatePersonFilteredList_failure() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - - // edit person in filtered list into a duplicate in address book - Person personInList = model.getAddressBook().getPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, - new EditPersonDescriptorBuilder(personInList).build()); - - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); - } - - @Test - public void execute_invalidPersonIndexUnfilteredList_failure() { - Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build(); - EditCommand editCommand = new EditCommand(outOfBoundIndex, descriptor); - - assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - /** - * Edit filtered list where index is larger than size of filtered list, - * but smaller than size of address book - */ - @Test - public void execute_invalidPersonIndexFilteredList_failure() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - Index outOfBoundIndex = INDEX_SECOND_PERSON; - // ensures that outOfBoundIndex is still in bounds of address book list - assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size()); - - EditCommand editCommand = new EditCommand(outOfBoundIndex, - new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); - - assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - @Test - public void equals() { - final EditCommand standardCommand = new EditCommand(INDEX_FIRST_PERSON, DESC_AMY); - - // same values -> returns true - EditPersonDescriptor copyDescriptor = new EditPersonDescriptor(DESC_AMY); - EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_PERSON, copyDescriptor); - assertTrue(standardCommand.equals(commandWithSameValues)); - - // same object -> returns true - assertTrue(standardCommand.equals(standardCommand)); - - // null -> returns false - assertFalse(standardCommand.equals(null)); - - // different types -> returns false - assertFalse(standardCommand.equals(new ClearCommand())); - - // different index -> returns false - assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_PERSON, DESC_AMY))); - - // different descriptor -> returns false - assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_PERSON, DESC_BOB))); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java deleted file mode 100644 index e0288792e72..00000000000 --- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.testutil.EditPersonDescriptorBuilder; - -public class EditPersonDescriptorTest { - - @Test - public void equals() { - // same values -> returns true - EditPersonDescriptor descriptorWithSameValues = new EditPersonDescriptor(DESC_AMY); - assertTrue(DESC_AMY.equals(descriptorWithSameValues)); - - // same object -> returns true - assertTrue(DESC_AMY.equals(DESC_AMY)); - - // null -> returns false - assertFalse(DESC_AMY.equals(null)); - - // different types -> returns false - assertFalse(DESC_AMY.equals(5)); - - // different values -> returns false - assertFalse(DESC_AMY.equals(DESC_BOB)); - - // different name -> returns false - EditPersonDescriptor editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withName(VALID_NAME_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different phone -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withPhone(VALID_PHONE_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different email -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withEmail(VALID_EMAIL_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different address -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different tags -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - } -} diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java deleted file mode 100644 index 5cf487d7ebb..00000000000 --- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java +++ /dev/null @@ -1,141 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; -import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalPersons.AMY; -import static seedu.address.testutil.TypicalPersons.BOB; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.testutil.PersonBuilder; - -public class AddCommandParserTest { - private AddCommandParser parser = new AddCommandParser(); - - @Test - public void parse_allFieldsPresent_success() { - Person expectedPerson = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND).build(); - - // whitespace only preamble - assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple names - last name accepted - assertParseSuccess(parser, NAME_DESC_AMY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple phones - last phone accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_AMY + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple emails - last email accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_AMY + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple addresses - last address accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_AMY - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple tags - all accepted - Person expectedPersonMultipleTags = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) - .build(); - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, new AddCommand(expectedPersonMultipleTags)); - } - - @Test - public void parse_optionalFieldsMissing_success() { - // zero tags - Person expectedPerson = new PersonBuilder(AMY).withTags().build(); - assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + ADDRESS_DESC_AMY, - new AddCommand(expectedPerson)); - } - - @Test - public void parse_compulsoryFieldMissing_failure() { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); - - // missing name prefix - assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing phone prefix - assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing email prefix - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing address prefix - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB, - expectedMessage); - - // all prefixes missing - assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_ADDRESS_BOB, - expectedMessage); - } - - @Test - public void parse_invalidValue_failure() { - // invalid name - assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS); - - // invalid phone - assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Phone.MESSAGE_CONSTRAINTS); - - // invalid email - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Email.MESSAGE_CONSTRAINTS); - - // invalid address - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Address.MESSAGE_CONSTRAINTS); - - // invalid tag - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + INVALID_TAG_DESC + VALID_TAG_FRIEND, Tag.MESSAGE_CONSTRAINTS); - - // two invalid values, only first invalid value reported - assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC, - Name.MESSAGE_CONSTRAINTS); - - // non-empty preamble - assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, - String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); - } -} diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java deleted file mode 100644 index d9659205b57..00000000000 --- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java +++ /dev/null @@ -1,101 +0,0 @@ -package seedu.address.logic.parser; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.ClearCommand; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -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; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; -import seedu.address.testutil.PersonBuilder; -import seedu.address.testutil.PersonUtil; - -public class AddressBookParserTest { - - private final AddressBookParser parser = new AddressBookParser(); - - @Test - public void parseCommand_add() throws Exception { - Person person = new PersonBuilder().build(); - AddCommand command = (AddCommand) parser.parseCommand(PersonUtil.getAddCommand(person)); - assertEquals(new AddCommand(person), command); - } - - @Test - public void parseCommand_clear() throws Exception { - assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD) instanceof ClearCommand); - assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD + " 3") instanceof ClearCommand); - } - - @Test - public void parseCommand_delete() throws Exception { - DeleteCommand command = (DeleteCommand) parser.parseCommand( - DeleteCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased()); - assertEquals(new DeleteCommand(INDEX_FIRST_PERSON), command); - } - - @Test - public void parseCommand_edit() throws Exception { - Person person = new PersonBuilder().build(); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(person).build(); - EditCommand command = (EditCommand) parser.parseCommand(EditCommand.COMMAND_WORD + " " - + INDEX_FIRST_PERSON.getOneBased() + " " + PersonUtil.getEditPersonDescriptorDetails(descriptor)); - assertEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command); - } - - @Test - public void parseCommand_exit() throws Exception { - assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD) instanceof ExitCommand); - assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD + " 3") instanceof ExitCommand); - } - - @Test - public void parseCommand_find() throws Exception { - List keywords = Arrays.asList("foo", "bar", "baz"); - FindCommand command = (FindCommand) parser.parseCommand( - FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" "))); - assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command); - } - - @Test - public void parseCommand_help() throws Exception { - assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand); - assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD + " 3") instanceof HelpCommand); - } - - @Test - public void parseCommand_list() throws Exception { - assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD) instanceof ListCommand); - assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand); - } - - @Test - public void parseCommand_unrecognisedInput_throwsParseException() { - assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), () - -> parser.parseCommand("")); - } - - @Test - public void parseCommand_unknownCommand_throwsParseException() { - assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> parser.parseCommand("unknownCommand")); - } -} diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java deleted file mode 100644 index 2ff31522486..00000000000 --- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java +++ /dev/null @@ -1,211 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_THIRD_PERSON; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.testutil.EditPersonDescriptorBuilder; - -public class EditCommandParserTest { - - private static final String TAG_EMPTY = " " + PREFIX_TAG; - - private static final String MESSAGE_INVALID_FORMAT = - String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE); - - private EditCommandParser parser = new EditCommandParser(); - - @Test - public void parse_missingParts_failure() { - // no index specified - assertParseFailure(parser, VALID_NAME_AMY, MESSAGE_INVALID_FORMAT); - - // no field specified - assertParseFailure(parser, "1", EditCommand.MESSAGE_NOT_EDITED); - - // no index and no field specified - assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); - } - - @Test - public void parse_invalidPreamble_failure() { - // negative index - assertParseFailure(parser, "-5" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT); - - // zero index - assertParseFailure(parser, "0" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT); - - // invalid arguments being parsed as preamble - assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT); - - // invalid prefix being parsed as preamble - assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT); - } - - @Test - public void parse_invalidValue_failure() { - assertParseFailure(parser, "1" + INVALID_NAME_DESC, Name.MESSAGE_CONSTRAINTS); // invalid name - assertParseFailure(parser, "1" + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); // invalid phone - assertParseFailure(parser, "1" + INVALID_EMAIL_DESC, Email.MESSAGE_CONSTRAINTS); // invalid email - assertParseFailure(parser, "1" + INVALID_ADDRESS_DESC, Address.MESSAGE_CONSTRAINTS); // invalid address - assertParseFailure(parser, "1" + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag - - // invalid phone followed by valid email - assertParseFailure(parser, "1" + INVALID_PHONE_DESC + EMAIL_DESC_AMY, Phone.MESSAGE_CONSTRAINTS); - - // valid phone followed by invalid phone. The test case for invalid phone followed by valid phone - // is tested at {@code parse_invalidValueFollowedByValidValue_success()} - assertParseFailure(parser, "1" + PHONE_DESC_BOB + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); - - // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Person} being edited, - // parsing it together with a valid tag results in error - assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS); - assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); - assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); - - // multiple invalid values, but only the first invalid value is captured - assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_ADDRESS_AMY + VALID_PHONE_AMY, - Name.MESSAGE_CONSTRAINTS); - } - - @Test - public void parse_allFieldsSpecified_success() { - Index targetIndex = INDEX_SECOND_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + TAG_DESC_HUSBAND - + EMAIL_DESC_AMY + ADDRESS_DESC_AMY + NAME_DESC_AMY + TAG_DESC_FRIEND; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) - .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) - .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_someFieldsSpecified_success() { - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + EMAIL_DESC_AMY; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_AMY).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_oneFieldSpecified_success() { - // name - Index targetIndex = INDEX_THIRD_PERSON; - String userInput = targetIndex.getOneBased() + NAME_DESC_AMY; - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // phone - userInput = targetIndex.getOneBased() + PHONE_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // email - userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withEmail(VALID_EMAIL_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // address - userInput = targetIndex.getOneBased() + ADDRESS_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withAddress(VALID_ADDRESS_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // tags - userInput = targetIndex.getOneBased() + TAG_DESC_FRIEND; - descriptor = new EditPersonDescriptorBuilder().withTags(VALID_TAG_FRIEND).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_multipleRepeatedFields_acceptsLast() { - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY - + TAG_DESC_FRIEND + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND - + PHONE_DESC_BOB + ADDRESS_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) - .build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_invalidValueFollowedByValidValue_success() { - // no other valid values specified - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + PHONE_DESC_BOB; - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // other valid values specified - userInput = targetIndex.getOneBased() + EMAIL_DESC_BOB + INVALID_PHONE_DESC + ADDRESS_DESC_BOB - + PHONE_DESC_BOB; - descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) - .withAddress(VALID_ADDRESS_BOB).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_resetTags_success() { - Index targetIndex = INDEX_THIRD_PERSON; - String userInput = targetIndex.getOneBased() + TAG_EMPTY; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withTags().build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } -} diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java deleted file mode 100644 index 4256788b1a7..00000000000 --- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java +++ /dev/null @@ -1,196 +0,0 @@ -package seedu.address.logic.parser; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -public class ParserUtilTest { - private static final String INVALID_NAME = "R@chel"; - private static final String INVALID_PHONE = "+651234"; - private static final String INVALID_ADDRESS = " "; - private static final String INVALID_EMAIL = "example.com"; - private static final String INVALID_TAG = "#friend"; - - private static final String VALID_NAME = "Rachel Walker"; - private static final String VALID_PHONE = "123456"; - private static final String VALID_ADDRESS = "123 Main Street #0505"; - private static final String VALID_EMAIL = "rachel@example.com"; - private static final String VALID_TAG_1 = "friend"; - private static final String VALID_TAG_2 = "neighbour"; - - private static final String WHITESPACE = " \t\r\n"; - - @Test - public void parseIndex_invalidInput_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseIndex("10 a")); - } - - @Test - public void parseIndex_outOfRangeInput_throwsParseException() { - assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, () - -> ParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1))); - } - - @Test - public void parseIndex_validInput_success() throws Exception { - // No whitespaces - assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex("1")); - - // Leading and trailing whitespaces - assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex(" 1 ")); - } - - @Test - public void parseName_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseName((String) null)); - } - - @Test - public void parseName_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseName(INVALID_NAME)); - } - - @Test - public void parseName_validValueWithoutWhitespace_returnsName() throws Exception { - Name expectedName = new Name(VALID_NAME); - assertEquals(expectedName, ParserUtil.parseName(VALID_NAME)); - } - - @Test - public void parseName_validValueWithWhitespace_returnsTrimmedName() throws Exception { - String nameWithWhitespace = WHITESPACE + VALID_NAME + WHITESPACE; - Name expectedName = new Name(VALID_NAME); - assertEquals(expectedName, ParserUtil.parseName(nameWithWhitespace)); - } - - @Test - public void parsePhone_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parsePhone((String) null)); - } - - @Test - public void parsePhone_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parsePhone(INVALID_PHONE)); - } - - @Test - public void parsePhone_validValueWithoutWhitespace_returnsPhone() throws Exception { - Phone expectedPhone = new Phone(VALID_PHONE); - assertEquals(expectedPhone, ParserUtil.parsePhone(VALID_PHONE)); - } - - @Test - public void parsePhone_validValueWithWhitespace_returnsTrimmedPhone() throws Exception { - String phoneWithWhitespace = WHITESPACE + VALID_PHONE + WHITESPACE; - Phone expectedPhone = new Phone(VALID_PHONE); - assertEquals(expectedPhone, ParserUtil.parsePhone(phoneWithWhitespace)); - } - - @Test - public void parseAddress_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseAddress((String) null)); - } - - @Test - public void parseAddress_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseAddress(INVALID_ADDRESS)); - } - - @Test - public void parseAddress_validValueWithoutWhitespace_returnsAddress() throws Exception { - Address expectedAddress = new Address(VALID_ADDRESS); - assertEquals(expectedAddress, ParserUtil.parseAddress(VALID_ADDRESS)); - } - - @Test - public void parseAddress_validValueWithWhitespace_returnsTrimmedAddress() throws Exception { - String addressWithWhitespace = WHITESPACE + VALID_ADDRESS + WHITESPACE; - Address expectedAddress = new Address(VALID_ADDRESS); - assertEquals(expectedAddress, ParserUtil.parseAddress(addressWithWhitespace)); - } - - @Test - public void parseEmail_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseEmail((String) null)); - } - - @Test - public void parseEmail_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseEmail(INVALID_EMAIL)); - } - - @Test - public void parseEmail_validValueWithoutWhitespace_returnsEmail() throws Exception { - Email expectedEmail = new Email(VALID_EMAIL); - assertEquals(expectedEmail, ParserUtil.parseEmail(VALID_EMAIL)); - } - - @Test - public void parseEmail_validValueWithWhitespace_returnsTrimmedEmail() throws Exception { - String emailWithWhitespace = WHITESPACE + VALID_EMAIL + WHITESPACE; - Email expectedEmail = new Email(VALID_EMAIL); - assertEquals(expectedEmail, ParserUtil.parseEmail(emailWithWhitespace)); - } - - @Test - public void parseTag_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null)); - } - - @Test - public void parseTag_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG)); - } - - @Test - public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception { - Tag expectedTag = new Tag(VALID_TAG_1); - assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1)); - } - - @Test - public void parseTag_validValueWithWhitespace_returnsTrimmedTag() throws Exception { - String tagWithWhitespace = WHITESPACE + VALID_TAG_1 + WHITESPACE; - Tag expectedTag = new Tag(VALID_TAG_1); - assertEquals(expectedTag, ParserUtil.parseTag(tagWithWhitespace)); - } - - @Test - public void parseTags_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null)); - } - - @Test - public void parseTags_collectionWithInvalidTags_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG))); - } - - @Test - public void parseTags_emptyCollection_returnsEmptySet() throws Exception { - assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty()); - } - - @Test - public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception { - Set actualTagSet = ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, VALID_TAG_2)); - Set expectedTagSet = new HashSet(Arrays.asList(new Tag(VALID_TAG_1), new Tag(VALID_TAG_2))); - - assertEquals(expectedTagSet, actualTagSet); - } -} diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java deleted file mode 100644 index 87782528ecd..00000000000 --- a/src/test/java/seedu/address/model/AddressBookTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package seedu.address.model; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.testutil.PersonBuilder; - -public class AddressBookTest { - - private final AddressBook addressBook = new AddressBook(); - - @Test - public void constructor() { - assertEquals(Collections.emptyList(), addressBook.getPersonList()); - } - - @Test - public void resetData_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> addressBook.resetData(null)); - } - - @Test - public void resetData_withValidReadOnlyAddressBook_replacesData() { - AddressBook newData = getTypicalAddressBook(); - addressBook.resetData(newData); - assertEquals(newData, addressBook); - } - - @Test - public void resetData_withDuplicatePersons_throwsDuplicatePersonException() { - // Two persons with the same identity fields - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - List newPersons = Arrays.asList(ALICE, editedAlice); - AddressBookStub newData = new AddressBookStub(newPersons); - - assertThrows(DuplicatePersonException.class, () -> addressBook.resetData(newData)); - } - - @Test - public void hasPerson_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> addressBook.hasPerson(null)); - } - - @Test - public void hasPerson_personNotInAddressBook_returnsFalse() { - assertFalse(addressBook.hasPerson(ALICE)); - } - - @Test - public void hasPerson_personInAddressBook_returnsTrue() { - addressBook.addPerson(ALICE); - assertTrue(addressBook.hasPerson(ALICE)); - } - - @Test - public void hasPerson_personWithSameIdentityFieldsInAddressBook_returnsTrue() { - addressBook.addPerson(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - assertTrue(addressBook.hasPerson(editedAlice)); - } - - @Test - public void getPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> addressBook.getPersonList().remove(0)); - } - - /** - * A stub ReadOnlyAddressBook whose persons list can violate interface constraints. - */ - private static class AddressBookStub implements ReadOnlyAddressBook { - private final ObservableList persons = FXCollections.observableArrayList(); - - AddressBookStub(Collection persons) { - this.persons.setAll(persons); - } - - @Override - public ObservableList getPersonList() { - return persons; - } - } - -} diff --git a/src/test/java/seedu/address/model/person/AddressTest.java b/src/test/java/seedu/address/model/person/AddressTest.java deleted file mode 100644 index dcd3be87b3a..00000000000 --- a/src/test/java/seedu/address/model/person/AddressTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class AddressTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Address(null)); - } - - @Test - public void constructor_invalidAddress_throwsIllegalArgumentException() { - String invalidAddress = ""; - assertThrows(IllegalArgumentException.class, () -> new Address(invalidAddress)); - } - - @Test - public void isValidAddress() { - // null address - assertThrows(NullPointerException.class, () -> Address.isValidAddress(null)); - - // invalid addresses - assertFalse(Address.isValidAddress("")); // empty string - assertFalse(Address.isValidAddress(" ")); // spaces only - - // valid addresses - assertTrue(Address.isValidAddress("Blk 456, Den Road, #01-355")); - assertTrue(Address.isValidAddress("-")); // one character - assertTrue(Address.isValidAddress("Leng Inc; 1234 Market St; San Francisco CA 2349879; USA")); // long address - } -} diff --git a/src/test/java/seedu/address/model/person/EmailTest.java b/src/test/java/seedu/address/model/person/EmailTest.java deleted file mode 100644 index bbcc6c8c98e..00000000000 --- a/src/test/java/seedu/address/model/person/EmailTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class EmailTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Email(null)); - } - - @Test - public void constructor_invalidEmail_throwsIllegalArgumentException() { - String invalidEmail = ""; - assertThrows(IllegalArgumentException.class, () -> new Email(invalidEmail)); - } - - @Test - public void isValidEmail() { - // null email - assertThrows(NullPointerException.class, () -> Email.isValidEmail(null)); - - // blank email - assertFalse(Email.isValidEmail("")); // empty string - assertFalse(Email.isValidEmail(" ")); // spaces only - - // missing parts - assertFalse(Email.isValidEmail("@example.com")); // missing local part - assertFalse(Email.isValidEmail("peterjackexample.com")); // missing '@' symbol - assertFalse(Email.isValidEmail("peterjack@")); // missing domain name - - // invalid parts - assertFalse(Email.isValidEmail("peterjack@-")); // invalid domain name - assertFalse(Email.isValidEmail("peterjack@exam_ple.com")); // underscore in domain name - assertFalse(Email.isValidEmail("peter jack@example.com")); // spaces in local part - assertFalse(Email.isValidEmail("peterjack@exam ple.com")); // spaces in domain name - assertFalse(Email.isValidEmail(" peterjack@example.com")); // leading space - assertFalse(Email.isValidEmail("peterjack@example.com ")); // trailing space - assertFalse(Email.isValidEmail("peterjack@@example.com")); // double '@' symbol - assertFalse(Email.isValidEmail("peter@jack@example.com")); // '@' symbol in local part - assertFalse(Email.isValidEmail("-peterjack@example.com")); // local part starts with a hyphen - assertFalse(Email.isValidEmail("peterjack-@example.com")); // local part ends with a hyphen - assertFalse(Email.isValidEmail("peter..jack@example.com")); // local part has two consecutive periods - assertFalse(Email.isValidEmail("peterjack@example@com")); // '@' symbol in domain name - assertFalse(Email.isValidEmail("peterjack@.example.com")); // domain name starts with a period - assertFalse(Email.isValidEmail("peterjack@example.com.")); // domain name ends with a period - assertFalse(Email.isValidEmail("peterjack@-example.com")); // domain name starts with a hyphen - assertFalse(Email.isValidEmail("peterjack@example.com-")); // domain name ends with a hyphen - assertFalse(Email.isValidEmail("peterjack@example.c")); // top level domain has less than two chars - - // valid email - assertTrue(Email.isValidEmail("PeterJack_1190@example.com")); // underscore in local part - assertTrue(Email.isValidEmail("PeterJack.1190@example.com")); // period in local part - assertTrue(Email.isValidEmail("PeterJack+1190@example.com")); // '+' symbol in local part - assertTrue(Email.isValidEmail("PeterJack-1190@example.com")); // hyphen in local part - assertTrue(Email.isValidEmail("a@bc")); // minimal - assertTrue(Email.isValidEmail("test@localhost")); // alphabets only - assertTrue(Email.isValidEmail("123@145")); // numeric local part and domain name - assertTrue(Email.isValidEmail("a1+be.d@example1.com")); // mixture of alphanumeric and special characters - assertTrue(Email.isValidEmail("peter_jack@very-very-very-long-example.com")); // long domain name - assertTrue(Email.isValidEmail("if.you.dream.it_you.can.do.it@example.com")); // long local part - assertTrue(Email.isValidEmail("e1234567@u.nus.edu")); // more than one period in domain - } -} diff --git a/src/test/java/seedu/address/model/person/NameTest.java b/src/test/java/seedu/address/model/person/NameTest.java deleted file mode 100644 index c9801392874..00000000000 --- a/src/test/java/seedu/address/model/person/NameTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class NameTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Name(null)); - } - - @Test - public void constructor_invalidName_throwsIllegalArgumentException() { - String invalidName = ""; - assertThrows(IllegalArgumentException.class, () -> new Name(invalidName)); - } - - @Test - public void isValidName() { - // null name - assertThrows(NullPointerException.class, () -> Name.isValidName(null)); - - // invalid name - assertFalse(Name.isValidName("")); // empty string - assertFalse(Name.isValidName(" ")); // spaces only - assertFalse(Name.isValidName("^")); // only non-alphanumeric characters - assertFalse(Name.isValidName("peter*")); // contains non-alphanumeric characters - - // valid name - assertTrue(Name.isValidName("peter jack")); // alphabets only - assertTrue(Name.isValidName("12345")); // numbers only - assertTrue(Name.isValidName("peter the 2nd")); // alphanumeric characters - assertTrue(Name.isValidName("Capital Tan")); // with capital letters - assertTrue(Name.isValidName("David Roger Jackson Ray Jr 2nd")); // long names - } -} diff --git a/src/test/java/seedu/address/model/person/PersonTest.java b/src/test/java/seedu/address/model/person/PersonTest.java deleted file mode 100644 index b29c097cfd4..00000000000 --- a/src/test/java/seedu/address/model/person/PersonTest.java +++ /dev/null @@ -1,91 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BOB; - -import org.junit.jupiter.api.Test; - -import seedu.address.testutil.PersonBuilder; - -public class PersonTest { - - @Test - public void asObservableList_modifyList_throwsUnsupportedOperationException() { - Person person = new PersonBuilder().build(); - assertThrows(UnsupportedOperationException.class, () -> person.getTags().remove(0)); - } - - @Test - public void isSamePerson() { - // same object -> returns true - assertTrue(ALICE.isSamePerson(ALICE)); - - // null -> returns false - assertFalse(ALICE.isSamePerson(null)); - - // same name, all other attributes different -> returns true - Person editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) - .withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND).build(); - assertTrue(ALICE.isSamePerson(editedAlice)); - - // different name, all other attributes same -> returns false - editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build(); - assertFalse(ALICE.isSamePerson(editedAlice)); - - // name differs in case, all other attributes same -> returns false - Person editedBob = new PersonBuilder(BOB).withName(VALID_NAME_BOB.toLowerCase()).build(); - assertFalse(BOB.isSamePerson(editedBob)); - - // name has trailing spaces, all other attributes same -> returns false - String nameWithTrailingSpaces = VALID_NAME_BOB + " "; - editedBob = new PersonBuilder(BOB).withName(nameWithTrailingSpaces).build(); - assertFalse(BOB.isSamePerson(editedBob)); - } - - @Test - public void equals() { - // same values -> returns true - Person aliceCopy = new PersonBuilder(ALICE).build(); - assertTrue(ALICE.equals(aliceCopy)); - - // same object -> returns true - assertTrue(ALICE.equals(ALICE)); - - // null -> returns false - assertFalse(ALICE.equals(null)); - - // different type -> returns false - assertFalse(ALICE.equals(5)); - - // different person -> returns false - assertFalse(ALICE.equals(BOB)); - - // different name -> returns false - Person editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different phone -> returns false - editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different email -> returns false - editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different address -> returns false - editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different tags -> returns false - editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND).build(); - assertFalse(ALICE.equals(editedAlice)); - } -} diff --git a/src/test/java/seedu/address/model/person/PhoneTest.java b/src/test/java/seedu/address/model/person/PhoneTest.java deleted file mode 100644 index 8dd52766a5f..00000000000 --- a/src/test/java/seedu/address/model/person/PhoneTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class PhoneTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Phone(null)); - } - - @Test - public void constructor_invalidPhone_throwsIllegalArgumentException() { - String invalidPhone = ""; - assertThrows(IllegalArgumentException.class, () -> new Phone(invalidPhone)); - } - - @Test - public void isValidPhone() { - // null phone number - assertThrows(NullPointerException.class, () -> Phone.isValidPhone(null)); - - // invalid phone numbers - assertFalse(Phone.isValidPhone("")); // empty string - assertFalse(Phone.isValidPhone(" ")); // spaces only - assertFalse(Phone.isValidPhone("91")); // less than 3 numbers - assertFalse(Phone.isValidPhone("phone")); // non-numeric - assertFalse(Phone.isValidPhone("9011p041")); // alphabets within digits - assertFalse(Phone.isValidPhone("9312 1534")); // spaces within digits - - // valid phone numbers - assertTrue(Phone.isValidPhone("911")); // exactly 3 numbers - assertTrue(Phone.isValidPhone("93121534")); - assertTrue(Phone.isValidPhone("124293842033123")); // long phone numbers - } -} diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/seedu/address/model/person/UniquePersonListTest.java deleted file mode 100644 index 1cc5fe9e0fe..00000000000 --- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java +++ /dev/null @@ -1,170 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BOB; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; -import seedu.address.testutil.PersonBuilder; - -public class UniquePersonListTest { - - private final UniquePersonList uniquePersonList = new UniquePersonList(); - - @Test - public void contains_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.contains(null)); - } - - @Test - public void contains_personNotInList_returnsFalse() { - assertFalse(uniquePersonList.contains(ALICE)); - } - - @Test - public void contains_personInList_returnsTrue() { - uniquePersonList.add(ALICE); - assertTrue(uniquePersonList.contains(ALICE)); - } - - @Test - public void contains_personWithSameIdentityFieldsInList_returnsTrue() { - uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - assertTrue(uniquePersonList.contains(editedAlice)); - } - - @Test - public void add_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.add(null)); - } - - @Test - public void add_duplicatePerson_throwsDuplicatePersonException() { - uniquePersonList.add(ALICE); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.add(ALICE)); - } - - @Test - public void setPerson_nullTargetPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(null, ALICE)); - } - - @Test - public void setPerson_nullEditedPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(ALICE, null)); - } - - @Test - public void setPerson_targetPersonNotInList_throwsPersonNotFoundException() { - assertThrows(PersonNotFoundException.class, () -> uniquePersonList.setPerson(ALICE, ALICE)); - } - - @Test - public void setPerson_editedPersonIsSamePerson_success() { - uniquePersonList.add(ALICE); - uniquePersonList.setPerson(ALICE, ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(ALICE); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasSameIdentity_success() { - uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - uniquePersonList.setPerson(ALICE, editedAlice); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(editedAlice); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasDifferentIdentity_success() { - uniquePersonList.add(ALICE); - uniquePersonList.setPerson(ALICE, BOB); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasNonUniqueIdentity_throwsDuplicatePersonException() { - uniquePersonList.add(ALICE); - uniquePersonList.add(BOB); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPerson(ALICE, BOB)); - } - - @Test - public void remove_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.remove(null)); - } - - @Test - public void remove_personDoesNotExist_throwsPersonNotFoundException() { - assertThrows(PersonNotFoundException.class, () -> uniquePersonList.remove(ALICE)); - } - - @Test - public void remove_existingPerson_removesPerson() { - uniquePersonList.add(ALICE); - uniquePersonList.remove(ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_nullUniquePersonList_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((UniquePersonList) null)); - } - - @Test - public void setPersons_uniquePersonList_replacesOwnListWithProvidedUniquePersonList() { - uniquePersonList.add(ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - uniquePersonList.setPersons(expectedUniquePersonList); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_nullList_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((List) null)); - } - - @Test - public void setPersons_list_replacesOwnListWithProvidedList() { - uniquePersonList.add(ALICE); - List personList = Collections.singletonList(BOB); - uniquePersonList.setPersons(personList); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_listWithDuplicatePersons_throwsDuplicatePersonException() { - List listWithDuplicatePersons = Arrays.asList(ALICE, ALICE); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPersons(listWithDuplicatePersons)); - } - - @Test - public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () - -> uniquePersonList.asUnmodifiableObservableList().remove(0)); - } -} diff --git a/src/test/java/seedu/address/model/tag/TagTest.java b/src/test/java/seedu/address/model/tag/TagTest.java deleted file mode 100644 index 64d07d79ee2..00000000000 --- a/src/test/java/seedu/address/model/tag/TagTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package seedu.address.model.tag; - -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class TagTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Tag(null)); - } - - @Test - public void constructor_invalidTagName_throwsIllegalArgumentException() { - String invalidTagName = ""; - assertThrows(IllegalArgumentException.class, () -> new Tag(invalidTagName)); - } - - @Test - public void isValidTagName() { - // null tag name - assertThrows(NullPointerException.class, () -> Tag.isValidTagName(null)); - } - -} diff --git a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java deleted file mode 100644 index 83b11331cdb..00000000000 --- a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.storage.JsonAdaptedPerson.MISSING_FIELD_MESSAGE_FORMAT; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.BENSON; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.Test; - -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.Phone; - -public class JsonAdaptedPersonTest { - private static final String INVALID_NAME = "R@chel"; - private static final String INVALID_PHONE = "+651234"; - private static final String INVALID_ADDRESS = " "; - private static final String INVALID_EMAIL = "example.com"; - private static final String INVALID_TAG = "#friend"; - - private static final String VALID_NAME = BENSON.getName().toString(); - private static final String VALID_PHONE = BENSON.getPhone().toString(); - private static final String VALID_EMAIL = BENSON.getEmail().toString(); - private static final String VALID_ADDRESS = BENSON.getAddress().toString(); - private static final List VALID_TAGS = BENSON.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList()); - - @Test - public void toModelType_validPersonDetails_returnsPerson() throws Exception { - JsonAdaptedPerson person = new JsonAdaptedPerson(BENSON); - assertEquals(BENSON, person.toModelType()); - } - - @Test - public void toModelType_invalidName_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Name.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullName_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(null, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidPhone_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Phone.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullPhone_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, null, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidEmail_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Email.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullEmail_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, null, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidAddress_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, VALID_TAGS); - String expectedMessage = Address.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullAddress_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, null, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidTags_throwsIllegalValueException() { - List invalidTags = new ArrayList<>(VALID_TAGS); - invalidTags.add(new JsonAdaptedTag(INVALID_TAG)); - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, invalidTags); - assertThrows(IllegalValueException.class, person::toModelType); - } - -} diff --git a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java b/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java deleted file mode 100644 index ac3c3af9566..00000000000 --- a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.HOON; -import static seedu.address.testutil.TypicalPersons.IDA; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; - -public class JsonAddressBookStorageTest { - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonAddressBookStorageTest"); - - @TempDir - public Path testFolder; - - @Test - public void readAddressBook_nullFilePath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> readAddressBook(null)); - } - - private java.util.Optional readAddressBook(String filePath) throws Exception { - return new JsonAddressBookStorage(Paths.get(filePath)).readAddressBook(addToTestDataPathIfNotNull(filePath)); - } - - private Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { - return prefsFileInTestDataFolder != null - ? TEST_DATA_FOLDER.resolve(prefsFileInTestDataFolder) - : null; - } - - @Test - public void read_missingFile_emptyResult() throws Exception { - assertFalse(readAddressBook("NonExistentFile.json").isPresent()); - } - - @Test - public void read_notJsonFormat_exceptionThrown() { - assertThrows(DataConversionException.class, () -> readAddressBook("notJsonFormatAddressBook.json")); - } - - @Test - public void readAddressBook_invalidPersonAddressBook_throwDataConversionException() { - assertThrows(DataConversionException.class, () -> readAddressBook("invalidPersonAddressBook.json")); - } - - @Test - public void readAddressBook_invalidAndValidPersonAddressBook_throwDataConversionException() { - assertThrows(DataConversionException.class, () -> readAddressBook("invalidAndValidPersonAddressBook.json")); - } - - @Test - public void readAndSaveAddressBook_allInOrder_success() throws Exception { - Path filePath = testFolder.resolve("TempAddressBook.json"); - AddressBook original = getTypicalAddressBook(); - JsonAddressBookStorage jsonAddressBookStorage = new JsonAddressBookStorage(filePath); - - // Save in new file and read back - jsonAddressBookStorage.saveAddressBook(original, filePath); - ReadOnlyAddressBook readBack = jsonAddressBookStorage.readAddressBook(filePath).get(); - assertEquals(original, new AddressBook(readBack)); - - // Modify data, overwrite exiting file, and read back - original.addPerson(HOON); - original.removePerson(ALICE); - jsonAddressBookStorage.saveAddressBook(original, filePath); - readBack = jsonAddressBookStorage.readAddressBook(filePath).get(); - assertEquals(original, new AddressBook(readBack)); - - // Save and read without specifying file path - original.addPerson(IDA); - jsonAddressBookStorage.saveAddressBook(original); // file path not specified - readBack = jsonAddressBookStorage.readAddressBook().get(); // file path not specified - assertEquals(original, new AddressBook(readBack)); - - } - - @Test - public void saveAddressBook_nullAddressBook_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> saveAddressBook(null, "SomeFile.json")); - } - - /** - * Saves {@code addressBook} at the specified {@code filePath}. - */ - private void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) { - try { - new JsonAddressBookStorage(Paths.get(filePath)) - .saveAddressBook(addressBook, addToTestDataPathIfNotNull(filePath)); - } catch (IOException ioe) { - throw new AssertionError("There should not be an error writing to the file.", ioe); - } - } - - @Test - public void saveAddressBook_nullFilePath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> saveAddressBook(new AddressBook(), null)); - } -} diff --git a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java b/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java deleted file mode 100644 index 188c9058d20..00000000000 --- a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.testutil.Assert.assertThrows; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.AddressBook; -import seedu.address.testutil.TypicalPersons; - -public class JsonSerializableAddressBookTest { - - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonSerializableAddressBookTest"); - private static final Path TYPICAL_PERSONS_FILE = TEST_DATA_FOLDER.resolve("typicalPersonsAddressBook.json"); - private static final Path INVALID_PERSON_FILE = TEST_DATA_FOLDER.resolve("invalidPersonAddressBook.json"); - private static final Path DUPLICATE_PERSON_FILE = TEST_DATA_FOLDER.resolve("duplicatePersonAddressBook.json"); - - @Test - public void toModelType_typicalPersonsFile_success() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(TYPICAL_PERSONS_FILE, - JsonSerializableAddressBook.class).get(); - AddressBook addressBookFromFile = dataFromFile.toModelType(); - AddressBook typicalPersonsAddressBook = TypicalPersons.getTypicalAddressBook(); - assertEquals(addressBookFromFile, typicalPersonsAddressBook); - } - - @Test - public void toModelType_invalidPersonFile_throwsIllegalValueException() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(INVALID_PERSON_FILE, - JsonSerializableAddressBook.class).get(); - assertThrows(IllegalValueException.class, dataFromFile::toModelType); - } - - @Test - public void toModelType_duplicatePersons_throwsIllegalValueException() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(DUPLICATE_PERSON_FILE, - JsonSerializableAddressBook.class).get(); - assertThrows(IllegalValueException.class, JsonSerializableAddressBook.MESSAGE_DUPLICATE_PERSON, - dataFromFile::toModelType); - } - -} diff --git a/src/test/java/seedu/address/testutil/AddressBookBuilder.java b/src/test/java/seedu/address/testutil/AddressBookBuilder.java deleted file mode 100644 index d53799fd110..00000000000 --- a/src/test/java/seedu/address/testutil/AddressBookBuilder.java +++ /dev/null @@ -1,34 +0,0 @@ -package seedu.address.testutil; - -import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; - -/** - * A utility class to help with building Addressbook objects. - * Example usage:
- * {@code AddressBook ab = new AddressBookBuilder().withPerson("John", "Doe").build();} - */ -public class AddressBookBuilder { - - private AddressBook addressBook; - - public AddressBookBuilder() { - addressBook = new AddressBook(); - } - - public AddressBookBuilder(AddressBook addressBook) { - this.addressBook = addressBook; - } - - /** - * Adds a new {@code Person} to the {@code AddressBook} that we are building. - */ - public AddressBookBuilder withPerson(Person person) { - addressBook.addPerson(person); - return this; - } - - public AddressBook build() { - return addressBook; - } -} diff --git a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java deleted file mode 100644 index 4584bd5044e..00000000000 --- a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java +++ /dev/null @@ -1,87 +0,0 @@ -package seedu.address.testutil; - -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -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; - -/** - * A utility class to help with building EditPersonDescriptor objects. - */ -public class EditPersonDescriptorBuilder { - - private EditPersonDescriptor descriptor; - - public EditPersonDescriptorBuilder() { - descriptor = new EditPersonDescriptor(); - } - - public EditPersonDescriptorBuilder(EditPersonDescriptor descriptor) { - this.descriptor = new EditPersonDescriptor(descriptor); - } - - /** - * Returns an {@code EditPersonDescriptor} with fields containing {@code person}'s details - */ - public EditPersonDescriptorBuilder(Person person) { - descriptor = new EditPersonDescriptor(); - descriptor.setName(person.getName()); - descriptor.setPhone(person.getPhone()); - descriptor.setEmail(person.getEmail()); - descriptor.setAddress(person.getAddress()); - descriptor.setTags(person.getTags()); - } - - /** - * Sets the {@code Name} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withName(String name) { - descriptor.setName(new Name(name)); - return this; - } - - /** - * Sets the {@code Phone} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withPhone(String phone) { - descriptor.setPhone(new Phone(phone)); - return this; - } - - /** - * Sets the {@code Email} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withEmail(String email) { - descriptor.setEmail(new Email(email)); - return this; - } - - /** - * Sets the {@code Address} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withAddress(String address) { - descriptor.setAddress(new Address(address)); - return this; - } - - /** - * Parses the {@code tags} into a {@code Set} and set it to the {@code EditPersonDescriptor} - * that we are building. - */ - public EditPersonDescriptorBuilder withTags(String... tags) { - Set tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet()); - descriptor.setTags(tagSet); - return this; - } - - public EditPersonDescriptor build() { - return descriptor; - } -} diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java deleted file mode 100644 index 6be381d39ba..00000000000 --- a/src/test/java/seedu/address/testutil/PersonBuilder.java +++ /dev/null @@ -1,96 +0,0 @@ -package seedu.address.testutil; - -import java.util.HashSet; -import java.util.Set; - -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.model.util.SampleDataUtil; - -/** - * A utility class to help with building Person objects. - */ -public class PersonBuilder { - - public static final String DEFAULT_NAME = "Amy Bee"; - public static final String DEFAULT_PHONE = "85355255"; - public static final String DEFAULT_EMAIL = "amy@gmail.com"; - public static final String DEFAULT_ADDRESS = "123, Jurong West Ave 6, #08-111"; - - private Name name; - private Phone phone; - private Email email; - private Address address; - private Set tags; - - /** - * Creates a {@code PersonBuilder} with the default details. - */ - public PersonBuilder() { - name = new Name(DEFAULT_NAME); - phone = new Phone(DEFAULT_PHONE); - email = new Email(DEFAULT_EMAIL); - address = new Address(DEFAULT_ADDRESS); - tags = new HashSet<>(); - } - - /** - * Initializes the PersonBuilder with the data of {@code personToCopy}. - */ - public PersonBuilder(Person personToCopy) { - name = personToCopy.getName(); - phone = personToCopy.getPhone(); - email = personToCopy.getEmail(); - address = personToCopy.getAddress(); - tags = new HashSet<>(personToCopy.getTags()); - } - - /** - * Sets the {@code Name} of the {@code Person} that we are building. - */ - public PersonBuilder withName(String name) { - this.name = new Name(name); - return this; - } - - /** - * Parses the {@code tags} into a {@code Set} and set it to the {@code Person} that we are building. - */ - public PersonBuilder withTags(String ... tags) { - this.tags = SampleDataUtil.getTagSet(tags); - return this; - } - - /** - * Sets the {@code Address} of the {@code Person} that we are building. - */ - public PersonBuilder withAddress(String address) { - this.address = new Address(address); - return this; - } - - /** - * Sets the {@code Phone} of the {@code Person} that we are building. - */ - public PersonBuilder withPhone(String phone) { - this.phone = new Phone(phone); - return this; - } - - /** - * Sets the {@code Email} of the {@code Person} that we are building. - */ - public PersonBuilder withEmail(String email) { - this.email = new Email(email); - return this; - } - - public Person build() { - return new Person(name, phone, email, address, tags); - } - -} diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/seedu/address/testutil/PersonUtil.java deleted file mode 100644 index 90849945183..00000000000 --- a/src/test/java/seedu/address/testutil/PersonUtil.java +++ /dev/null @@ -1,62 +0,0 @@ -package seedu.address.testutil; - -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 seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Person; -import seedu.address.model.tag.Tag; - -/** - * A utility class for Person. - */ -public class PersonUtil { - - /** - * Returns an add command string for adding the {@code person}. - */ - public static String getAddCommand(Person person) { - return AddCommand.COMMAND_WORD + " " + getPersonDetails(person); - } - - /** - * Returns the part of command string for the given {@code person}'s details. - */ - public static String getPersonDetails(Person person) { - StringBuilder sb = new StringBuilder(); - sb.append(PREFIX_NAME + person.getName().fullName + " "); - sb.append(PREFIX_PHONE + person.getPhone().value + " "); - sb.append(PREFIX_EMAIL + person.getEmail().value + " "); - sb.append(PREFIX_ADDRESS + person.getAddress().value + " "); - person.getTags().stream().forEach( - s -> sb.append(PREFIX_TAG + s.tagName + " ") - ); - return sb.toString(); - } - - /** - * Returns the part of command string for the given {@code EditPersonDescriptor}'s details. - */ - public static String getEditPersonDescriptorDetails(EditPersonDescriptor descriptor) { - StringBuilder sb = new StringBuilder(); - descriptor.getName().ifPresent(name -> sb.append(PREFIX_NAME).append(name.fullName).append(" ")); - descriptor.getPhone().ifPresent(phone -> sb.append(PREFIX_PHONE).append(phone.value).append(" ")); - descriptor.getEmail().ifPresent(email -> sb.append(PREFIX_EMAIL).append(email.value).append(" ")); - descriptor.getAddress().ifPresent(address -> sb.append(PREFIX_ADDRESS).append(address.value).append(" ")); - if (descriptor.getTags().isPresent()) { - Set tags = descriptor.getTags().get(); - if (tags.isEmpty()) { - sb.append(PREFIX_TAG); - } else { - tags.forEach(s -> sb.append(PREFIX_TAG).append(s.tagName).append(" ")); - } - } - return sb.toString(); - } -} diff --git a/src/test/java/seedu/address/testutil/TypicalIndexes.java b/src/test/java/seedu/address/testutil/TypicalIndexes.java deleted file mode 100644 index 1e613937657..00000000000 --- a/src/test/java/seedu/address/testutil/TypicalIndexes.java +++ /dev/null @@ -1,12 +0,0 @@ -package seedu.address.testutil; - -import seedu.address.commons.core.index.Index; - -/** - * A utility class containing a list of {@code Index} objects to be used in tests. - */ -public class TypicalIndexes { - public static final Index INDEX_FIRST_PERSON = Index.fromOneBased(1); - public static final Index INDEX_SECOND_PERSON = Index.fromOneBased(2); - public static final Index INDEX_THIRD_PERSON = Index.fromOneBased(3); -} diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java deleted file mode 100644 index fec76fb7129..00000000000 --- a/src/test/java/seedu/address/testutil/TypicalPersons.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.testutil; - -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; - -/** - * A utility class containing a list of {@code Person} objects to be used in tests. - */ -public class TypicalPersons { - - public static final Person ALICE = new PersonBuilder().withName("Alice Pauline") - .withAddress("123, Jurong West Ave 6, #08-111").withEmail("alice@example.com") - .withPhone("94351253") - .withTags("friends").build(); - public static final Person BENSON = new PersonBuilder().withName("Benson Meier") - .withAddress("311, Clementi Ave 2, #02-25") - .withEmail("johnd@example.com").withPhone("98765432") - .withTags("owesMoney", "friends").build(); - public static final Person CARL = new PersonBuilder().withName("Carl Kurz").withPhone("95352563") - .withEmail("heinz@example.com").withAddress("wall street").build(); - public static final Person DANIEL = new PersonBuilder().withName("Daniel Meier").withPhone("87652533") - .withEmail("cornelia@example.com").withAddress("10th street").withTags("friends").build(); - public static final Person ELLE = new PersonBuilder().withName("Elle Meyer").withPhone("9482224") - .withEmail("werner@example.com").withAddress("michegan ave").build(); - public static final Person FIONA = new PersonBuilder().withName("Fiona Kunz").withPhone("9482427") - .withEmail("lydia@example.com").withAddress("little tokyo").build(); - public static final Person GEORGE = new PersonBuilder().withName("George Best").withPhone("9482442") - .withEmail("anna@example.com").withAddress("4th street").build(); - - // Manually added - public static final Person HOON = new PersonBuilder().withName("Hoon Meier").withPhone("8482424") - .withEmail("stefan@example.com").withAddress("little india").build(); - public static final Person IDA = new PersonBuilder().withName("Ida Mueller").withPhone("8482131") - .withEmail("hans@example.com").withAddress("chicago ave").build(); - - // Manually added - Person's details found in {@code CommandTestUtil} - public static final Person AMY = new PersonBuilder().withName(VALID_NAME_AMY).withPhone(VALID_PHONE_AMY) - .withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY).withTags(VALID_TAG_FRIEND).build(); - public static final Person BOB = new PersonBuilder().withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND) - .build(); - - public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER - - private TypicalPersons() {} // prevents instantiation - - /** - * Returns an {@code AddressBook} with all the typical persons. - */ - public static AddressBook getTypicalAddressBook() { - AddressBook ab = new AddressBook(); - for (Person person : getTypicalPersons()) { - ab.addPerson(person); - } - return ab; - } - - public static List getTypicalPersons() { - return new ArrayList<>(Arrays.asList(ALICE, BENSON, CARL, DANIEL, ELLE, FIONA, GEORGE)); - } -} diff --git a/src/test/java/seedu/address/AppParametersTest.java b/src/test/java/seedu/waddle/AppParametersTest.java similarity index 98% rename from src/test/java/seedu/address/AppParametersTest.java rename to src/test/java/seedu/waddle/AppParametersTest.java index 61326b2d31a..e84b6fcf1a4 100644 --- a/src/test/java/seedu/address/AppParametersTest.java +++ b/src/test/java/seedu/waddle/AppParametersTest.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.waddle; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/seedu/address/commons/core/ConfigTest.java b/src/test/java/seedu/waddle/commons/core/ConfigTest.java similarity index 95% rename from src/test/java/seedu/address/commons/core/ConfigTest.java rename to src/test/java/seedu/waddle/commons/core/ConfigTest.java index 07cd7f73d53..bff2bf0861c 100644 --- a/src/test/java/seedu/address/commons/core/ConfigTest.java +++ b/src/test/java/seedu/waddle/commons/core/ConfigTest.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.waddle.commons.core; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/seedu/waddle/commons/core/TextTest.java b/src/test/java/seedu/waddle/commons/core/TextTest.java new file mode 100644 index 00000000000..cc43a891ec9 --- /dev/null +++ b/src/test/java/seedu/waddle/commons/core/TextTest.java @@ -0,0 +1,40 @@ +package seedu.waddle.commons.core; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class TextTest { + + @Test + public void indent_empty_string() { + String emptyString = ""; + assertEquals(emptyString, Text.indent(emptyString, Text.INDENT_FOUR)); + } + + @Test + public void indent_single_line() { + String singleLine = "This is a test string."; + String expected = " This is a test string."; + assertEquals(expected, Text.indent(singleLine, Text.INDENT_FOUR)); + } + + @Test + public void indent_multi_line() { + String multiLine = "This is a test string." + + System.lineSeparator() + "This is a test string."; + String expected = " This is a test string." + + System.lineSeparator() + " This is a test string."; + assertEquals(expected, Text.indent(multiLine, Text.INDENT_FOUR)); + } + + @Test + public void indent_indented_multiLine() { + String multiLine = "This is a test string." + + System.lineSeparator() + " This is a test string."; + String expected = " This is a test string." + + System.lineSeparator() + " This is a test string."; + assertEquals(expected, Text.indent(multiLine, Text.INDENT_FOUR)); + } + +} diff --git a/src/test/java/seedu/address/commons/core/VersionTest.java b/src/test/java/seedu/waddle/commons/core/VersionTest.java similarity index 98% rename from src/test/java/seedu/address/commons/core/VersionTest.java rename to src/test/java/seedu/waddle/commons/core/VersionTest.java index 495cd231554..1e751ea87b8 100644 --- a/src/test/java/seedu/address/commons/core/VersionTest.java +++ b/src/test/java/seedu/waddle/commons/core/VersionTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.core; +package seedu.waddle.commons.core; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/commons/core/index/IndexTest.java b/src/test/java/seedu/waddle/commons/core/index/IndexTest.java similarity index 95% rename from src/test/java/seedu/address/commons/core/index/IndexTest.java rename to src/test/java/seedu/waddle/commons/core/index/IndexTest.java index a3ec6f8e747..ba225400484 100644 --- a/src/test/java/seedu/address/commons/core/index/IndexTest.java +++ b/src/test/java/seedu/waddle/commons/core/index/IndexTest.java @@ -1,9 +1,9 @@ -package seedu.address.commons.core.index; +package seedu.waddle.commons.core.index; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/commons/util/AppUtilTest.java b/src/test/java/seedu/waddle/commons/util/AppUtilTest.java similarity index 91% rename from src/test/java/seedu/address/commons/util/AppUtilTest.java rename to src/test/java/seedu/waddle/commons/util/AppUtilTest.java index 594de1e6365..f97cf8ccc3d 100644 --- a/src/test/java/seedu/address/commons/util/AppUtilTest.java +++ b/src/test/java/seedu/waddle/commons/util/AppUtilTest.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package seedu.waddle.commons.util; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/commons/util/CollectionUtilTest.java b/src/test/java/seedu/waddle/commons/util/CollectionUtilTest.java similarity index 96% rename from src/test/java/seedu/address/commons/util/CollectionUtilTest.java rename to src/test/java/seedu/waddle/commons/util/CollectionUtilTest.java index b467a3dc025..77c7641ca77 100644 --- a/src/test/java/seedu/address/commons/util/CollectionUtilTest.java +++ b/src/test/java/seedu/waddle/commons/util/CollectionUtilTest.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package seedu.waddle.commons.util; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.waddle.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.waddle.testutil.Assert.assertThrows; import java.util.Arrays; import java.util.Collection; diff --git a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java b/src/test/java/seedu/waddle/commons/util/ConfigUtilTest.java similarity index 94% rename from src/test/java/seedu/address/commons/util/ConfigUtilTest.java rename to src/test/java/seedu/waddle/commons/util/ConfigUtilTest.java index d2ab2839a52..9bdd7e6e184 100644 --- a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java +++ b/src/test/java/seedu/waddle/commons/util/ConfigUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package seedu.waddle.commons.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.Assert.assertThrows; import java.io.IOException; import java.nio.file.Path; @@ -13,8 +13,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.waddle.commons.core.Config; +import seedu.waddle.commons.exceptions.DataConversionException; public class ConfigUtilTest { diff --git a/src/test/java/seedu/address/commons/util/FileUtilTest.java b/src/test/java/seedu/waddle/commons/util/FileUtilTest.java similarity index 84% rename from src/test/java/seedu/address/commons/util/FileUtilTest.java rename to src/test/java/seedu/waddle/commons/util/FileUtilTest.java index 1fe5478c756..3bb8dcaa262 100644 --- a/src/test/java/seedu/address/commons/util/FileUtilTest.java +++ b/src/test/java/seedu/waddle/commons/util/FileUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package seedu.waddle.commons.util; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/commons/util/JsonUtilTest.java b/src/test/java/seedu/waddle/commons/util/JsonUtilTest.java similarity index 92% rename from src/test/java/seedu/address/commons/util/JsonUtilTest.java rename to src/test/java/seedu/waddle/commons/util/JsonUtilTest.java index d4907539dee..86b4b2881f9 100644 --- a/src/test/java/seedu/address/commons/util/JsonUtilTest.java +++ b/src/test/java/seedu/waddle/commons/util/JsonUtilTest.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.waddle.commons.util; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -7,8 +7,8 @@ import org.junit.jupiter.api.Test; -import seedu.address.testutil.SerializableTestClass; -import seedu.address.testutil.TestUtil; +import seedu.waddle.testutil.SerializableTestClass; +import seedu.waddle.testutil.TestUtil; /** * Tests JSON Read and Write diff --git a/src/test/java/seedu/address/commons/util/StringUtilTest.java b/src/test/java/seedu/waddle/commons/util/StringUtilTest.java similarity index 98% rename from src/test/java/seedu/address/commons/util/StringUtilTest.java rename to src/test/java/seedu/waddle/commons/util/StringUtilTest.java index c56d407bf3f..740b5c6512c 100644 --- a/src/test/java/seedu/address/commons/util/StringUtilTest.java +++ b/src/test/java/seedu/waddle/commons/util/StringUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package seedu.waddle.commons.util; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.Assert.assertThrows; import java.io.FileNotFoundException; diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/waddle/logic/LogicManagerTest.java similarity index 57% rename from src/test/java/seedu/address/logic/LogicManagerTest.java rename to src/test/java/seedu/waddle/logic/LogicManagerTest.java index ad923ac249a..e19bf555045 100644 --- a/src/test/java/seedu/address/logic/LogicManagerTest.java +++ b/src/test/java/seedu/waddle/logic/LogicManagerTest.java @@ -1,14 +1,16 @@ -package seedu.address.logic; +package seedu.waddle.logic; import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.AMY; +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_ITINERARY_DISPLAYED_INDEX; +import static seedu.waddle.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.waddle.logic.commands.CommandTestUtil.BUDGET_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.COUNTRY_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.DURATION_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.ITINERARY_DESC_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.PEOPLE_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.START_DATE_DESC_SUMMER; +import static seedu.waddle.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.TypicalItineraries.SUMMER; import java.io.IOException; import java.nio.file.Path; @@ -17,20 +19,20 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.storage.JsonAddressBookStorage; -import seedu.address.storage.JsonUserPrefsStorage; -import seedu.address.storage.StorageManager; -import seedu.address.testutil.PersonBuilder; +import seedu.waddle.logic.commands.AddCommand; +import seedu.waddle.logic.commands.CommandResult; +import seedu.waddle.logic.commands.ListCommand; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.logic.parser.exceptions.ParseException; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.ReadOnlyWaddle; +import seedu.waddle.model.UserPrefs; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.storage.JsonUserPrefsStorage; +import seedu.waddle.storage.JsonWaddleStorage; +import seedu.waddle.storage.StorageManager; +import seedu.waddle.testutil.ItineraryBuilder; public class LogicManagerTest { private static final IOException DUMMY_IO_EXCEPTION = new IOException("dummy exception"); @@ -38,15 +40,15 @@ public class LogicManagerTest { @TempDir public Path temporaryFolder; - private Model model = new ModelManager(); + private final Model model = new ModelManager(); private Logic logic; @BeforeEach public void setUp() { - JsonAddressBookStorage addressBookStorage = - new JsonAddressBookStorage(temporaryFolder.resolve("addressBook.json")); + JsonWaddleStorage waddleStorage = + new JsonWaddleStorage(temporaryFolder.resolve("waddle.json")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); + StorageManager storage = new StorageManager(waddleStorage, userPrefsStorage); logic = new LogicManager(model, storage); } @@ -59,7 +61,7 @@ public void execute_invalidCommandFormat_throwsParseException() { @Test public void execute_commandExecutionError_throwsCommandException() { String deleteCommand = "delete 9"; - assertCommandException(deleteCommand, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + assertCommandException(deleteCommand, MESSAGE_INVALID_ITINERARY_DISPLAYED_INDEX); } @Test @@ -70,27 +72,28 @@ public void execute_validCommand_success() throws Exception { @Test public void execute_storageThrowsIoException_throwsCommandException() { - // Setup LogicManager with JsonAddressBookIoExceptionThrowingStub - JsonAddressBookStorage addressBookStorage = - new JsonAddressBookIoExceptionThrowingStub(temporaryFolder.resolve("ioExceptionAddressBook.json")); + // Setup LogicManager with JsonWaddleIoExceptionThrowingStub + JsonWaddleStorage waddleStorage = + new JsonWaddleIoExceptionThrowingStub(temporaryFolder.resolve("ioExceptionWaddle.json")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("ioExceptionUserPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); + StorageManager storage = new StorageManager(waddleStorage, userPrefsStorage); logic = new LogicManager(model, storage); // Execute add command - String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY - + ADDRESS_DESC_AMY; - Person expectedPerson = new PersonBuilder(AMY).withTags().build(); + String addCommand = AddCommand.COMMAND_WORD + ITINERARY_DESC_DESC_SUMMER + + COUNTRY_DESC_SUMMER + START_DATE_DESC_SUMMER + + DURATION_DESC_SUMMER + PEOPLE_DESC_SUMMER + BUDGET_DESC_SUMMER; + Itinerary expectedItinerary = new ItineraryBuilder(SUMMER).build(); ModelManager expectedModel = new ModelManager(); - expectedModel.addPerson(expectedPerson); + expectedModel.addItinerary(expectedItinerary); String expectedMessage = LogicManager.FILE_OPS_ERROR_MESSAGE + DUMMY_IO_EXCEPTION; assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel); } @Test - public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0)); + public void getFilteredItineraryList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredItineraryList().remove(0)); } /** @@ -98,10 +101,11 @@ public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException * - no exceptions are thrown
* - the feedback message is equal to {@code expectedMessage}
* - the internal model manager state is the same as that in {@code expectedModel}
+ * * @see #assertCommandFailure(String, Class, String, Model) */ private void assertCommandSuccess(String inputCommand, String expectedMessage, - Model expectedModel) throws CommandException, ParseException { + Model expectedModel) throws CommandException, ParseException { CommandResult result = logic.execute(inputCommand); assertEquals(expectedMessage, result.getFeedbackToUser()); assertEquals(expectedModel, model); @@ -109,6 +113,7 @@ private void assertCommandSuccess(String inputCommand, String expectedMessage, /** * Executes the command, confirms that a ParseException is thrown and that the result message is correct. + * * @see #assertCommandFailure(String, Class, String, Model) */ private void assertParseException(String inputCommand, String expectedMessage) { @@ -117,6 +122,7 @@ private void assertParseException(String inputCommand, String expectedMessage) { /** * Executes the command, confirms that a CommandException is thrown and that the result message is correct. + * * @see #assertCommandFailure(String, Class, String, Model) */ private void assertCommandException(String inputCommand, String expectedMessage) { @@ -125,11 +131,12 @@ private void assertCommandException(String inputCommand, String expectedMessage) /** * Executes the command, confirms that the exception is thrown and that the result message is correct. + * * @see #assertCommandFailure(String, Class, String, Model) */ private void assertCommandFailure(String inputCommand, Class expectedException, - String expectedMessage) { - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + String expectedMessage) { + Model expectedModel = new ModelManager(model.getWaddle(), new UserPrefs()); assertCommandFailure(inputCommand, expectedException, expectedMessage, expectedModel); } @@ -138,10 +145,11 @@ private void assertCommandFailure(String inputCommand, Class * - the resulting error message is equal to {@code expectedMessage}
* - the internal model manager state is the same as that in {@code expectedModel}
+ * * @see #assertCommandSuccess(String, String, Model) */ private void assertCommandFailure(String inputCommand, Class expectedException, - String expectedMessage, Model expectedModel) { + String expectedMessage, Model expectedModel) { assertThrows(expectedException, expectedMessage, () -> logic.execute(inputCommand)); assertEquals(expectedModel, model); } @@ -149,13 +157,13 @@ private void assertCommandFailure(String inputCommand, Class new AddCommand(null)); + } + + @Test + public void execute_itineraryAcceptedByModel_addSuccessful() throws Exception { + ModelStubAcceptingItineraryAdded modelStub = new ModelStubAcceptingItineraryAdded(); + Itinerary validItinerary = new ItineraryBuilder().build(); + + CommandResult commandResult = new AddCommand(validItinerary).execute(modelStub); + + assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, validItinerary), commandResult.getFeedbackToUser()); + assertEquals(Arrays.asList(validItinerary), modelStub.itinerariesAdded); + } + + @Test + public void execute_duplicateItinerary_throwsCommandException() { + Itinerary validItinerary = new ItineraryBuilder().build(); + AddCommand addCommand = new AddCommand(validItinerary); + ModelStub modelStub = new ModelStubWithItinerary(validItinerary); + + assertThrows(CommandException.class, + AddCommand.MESSAGE_DUPLICATE_ITINERARY, () -> addCommand.execute(modelStub)); + } + + @Test + public void equals() { + Itinerary summer = new ItineraryBuilder().withDescription("Summer").build(); + Itinerary winter = new ItineraryBuilder().withDescription("Winter").build(); + AddCommand addSummerCommand = new AddCommand(summer); + AddCommand addWinterCommand = new AddCommand(winter); + + // same object -> returns true + assertTrue(addSummerCommand.equals(addSummerCommand)); + + // same values -> returns true + AddCommand addSummerCommandCopy = new AddCommand(summer); + assertTrue(addSummerCommand.equals(addSummerCommandCopy)); + + // different types -> returns false + assertFalse(addSummerCommand.equals(1)); + + // null -> returns false + assertFalse(addSummerCommand.equals(null)); + + // different itinerary -> returns false + assertFalse(addSummerCommand.equals(addWinterCommand)); + } + + /** + * A default model stub that have all of the methods failing. + */ + private class ModelStub implements Model { + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + throw new AssertionError("This method should not be called."); + } + + @Override + public GuiSettings getGuiSettings() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + throw new AssertionError("This method should not be called."); + } + + @Override + public Path getWaddleFilePath() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setWaddleFilePath(Path addressBookFilePath) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addItinerary(Itinerary itinerary) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setWaddle(ReadOnlyWaddle newData) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyWaddle getWaddle() { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasItinerary(Itinerary itinerary) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deleteItinerary(Itinerary target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setItinerary(Itinerary target, Itinerary editedItinerary) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getFilteredItineraryList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredItineraryList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } + } + + /** + * A Model stub that contains a single itinerary. + */ + private class ModelStubWithItinerary extends ModelStub { + private final Itinerary itinerary; + + ModelStubWithItinerary(Itinerary itinerary) { + requireNonNull(itinerary); + this.itinerary = itinerary; + } + + @Override + public boolean hasItinerary(Itinerary itinerary) { + requireNonNull(itinerary); + return this.itinerary.isSameItinerary(itinerary); + } + } + + /** + * A Model stub that always accept the itinerary being added. + */ + private class ModelStubAcceptingItineraryAdded extends ModelStub { + final ArrayList itinerariesAdded = new ArrayList<>(); + + @Override + public boolean hasItinerary(Itinerary itinerary) { + requireNonNull(itinerary); + return itinerariesAdded.stream().anyMatch(itinerary::isSameItinerary); + } + + @Override + public void addItinerary(Itinerary itinerary) { + requireNonNull(itinerary); + itinerariesAdded.add(itinerary); + } + + @Override + public ReadOnlyWaddle getWaddle() { + return new Waddle(); + } + } + +} diff --git a/src/test/java/seedu/waddle/logic/commands/AddItemCommandTest.java b/src/test/java/seedu/waddle/logic/commands/AddItemCommandTest.java new file mode 100644 index 00000000000..db46400d4d7 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/AddItemCommandTest.java @@ -0,0 +1,176 @@ +package seedu.waddle.logic.commands; + +import static java.util.Objects.requireNonNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.waddle.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.TypicalItineraries.getTypicalWaddle; + +import java.nio.file.Path; +import java.util.function.Predicate; + +import org.junit.jupiter.api.Test; + +import javafx.collections.ObservableList; +import seedu.waddle.commons.core.GuiSettings; +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.ReadOnlyUserPrefs; +import seedu.waddle.model.ReadOnlyWaddle; +import seedu.waddle.model.UserPrefs; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.testutil.ItemBuilder; +import seedu.waddle.testutil.ItineraryBuilder; + + +public class AddItemCommandTest { + + @Test + public void constructor_nullItem_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new AddItemCommand(null)); + } + + @Test + public void execute_itemAcceptedByModel_addSuccessful() throws Exception { + Item validItem = new ItemBuilder().build(); + Itinerary validItinerary = new ItineraryBuilder().build(); + AddItemCommandTest.ModelStubWithItinerary modelStub = new ModelStubWithItinerary(validItinerary); + StageManager stageManager = StageManager.getInstance(); + stageManager.setWishStage(validItinerary); + CommandResult commandResult = new AddItemCommand(validItem).execute(modelStub); + + assertEquals(String.format(AddItemCommand.MESSAGE_SUCCESS, validItem), commandResult.getFeedbackToUser()); + } + + @Test + public void execute_duplicateItem_throwsDuplicateItemException() { + Item validItem = new ItemBuilder().build(); + Itinerary validItinerary = new ItineraryBuilder().build(); + validItinerary.addItem(validItem); + AddItemCommand addItemCommand = new AddItemCommand(validItem); + AddItemCommandTest.ModelStub modelStub = new ModelStubWithItinerary(validItinerary); + StageManager stageManager = StageManager.getInstance(); + stageManager.setWishStage(validItinerary); + + assertThrows(CommandException.class, + AddItemCommand.MESSAGE_DUPLICATE_ITEM, () -> addItemCommand.execute(modelStub)); + } + + // integration test with typical Waddle + @Test + public void execute_newItem_addSuccessful() throws Exception { + Item validItem = new ItemBuilder().build(); + Itinerary validItinerary = new ItineraryBuilder().build(); + Model model = new ModelManager(getTypicalWaddle(), new UserPrefs()); + StageManager stageManager = StageManager.getInstance(); + stageManager.setWishStage(validItinerary); + CommandResult commandResult = new AddItemCommand(validItem).execute(model); + + assertEquals(String.format(AddItemCommand.MESSAGE_SUCCESS, validItem), + commandResult.getFeedbackToUser()); + assertEquals(true, validItinerary.hasItem(validItem)); + } + + // integration test with typical Waddle + @Test + public void addItemCommand_duplicateItemTypicalWaddle_throwsDuplicateItemException() { + Model model = new ModelManager(getTypicalWaddle(), new UserPrefs()); + Itinerary itineraryInList = model.getWaddle().getItineraryList().get(1); + Item itemInList = itineraryInList.getItemList().get(0); + StageManager stageManager = StageManager.getInstance(); + stageManager.setWishStage(itineraryInList); + + assertCommandFailure(new AddItemCommand(itemInList), model, + AddItemCommand.MESSAGE_DUPLICATE_ITEM); + } + + /** + * A default model stub that have all of the methods failing. + */ + private class ModelStub implements Model { + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + throw new AssertionError("This method should not be called."); + } + + @Override + public GuiSettings getGuiSettings() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + throw new AssertionError("This method should not be called."); + } + + @Override + public Path getWaddleFilePath() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setWaddleFilePath(Path addressBookFilePath) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addItinerary(Itinerary itinerary) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setWaddle(ReadOnlyWaddle newData) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyWaddle getWaddle() { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasItinerary(Itinerary itinerary) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deleteItinerary(Itinerary target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setItinerary(Itinerary target, Itinerary editedItinerary) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getFilteredItineraryList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredItineraryList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } + } + + /** + * A Model stub that contains a single itinerary. + */ + private class ModelStubWithItinerary extends ModelStub { + private final Itinerary itinerary; + + ModelStubWithItinerary(Itinerary itinerary) { + requireNonNull(itinerary); + this.itinerary = itinerary; + } + } +} diff --git a/src/test/java/seedu/waddle/logic/commands/ClearCommandTest.java b/src/test/java/seedu/waddle/logic/commands/ClearCommandTest.java new file mode 100644 index 00000000000..44b44307d41 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/ClearCommandTest.java @@ -0,0 +1,32 @@ +package seedu.waddle.logic.commands; + +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.waddle.testutil.TypicalItineraries.getTypicalWaddle; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.UserPrefs; +import seedu.waddle.model.Waddle; + +public class ClearCommandTest { + + @Test + public void execute_emptyWaddle_success() { + Model model = new ModelManager(); + Model expectedModel = new ModelManager(); + + assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_nonEmptyWAddle_success() { + Model model = new ModelManager(getTypicalWaddle(), new UserPrefs()); + Model expectedModel = new ModelManager(getTypicalWaddle(), new UserPrefs()); + expectedModel.setWaddle(new Waddle()); + + assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); + } + +} diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/waddle/logic/commands/CommandResultTest.java similarity index 98% rename from src/test/java/seedu/address/logic/commands/CommandResultTest.java rename to src/test/java/seedu/waddle/logic/commands/CommandResultTest.java index 4f3eb46e9ef..3e282956ec9 100644 --- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java +++ b/src/test/java/seedu/waddle/logic/commands/CommandResultTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.waddle.logic.commands; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/src/test/java/seedu/waddle/logic/commands/CommandTestUtil.java b/src/test/java/seedu/waddle/logic/commands/CommandTestUtil.java new file mode 100644 index 00000000000..228c18b02e7 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/CommandTestUtil.java @@ -0,0 +1,253 @@ +package seedu.waddle.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_BUDGET; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_COST; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_COUNTRY; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_DAY_NUMBER; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_ITEM_DURATION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_ITINERARY_DURATION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_PEOPLE; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_PRIORITY; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_START_DATE; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_START_TIME; +import static seedu.waddle.testutil.Assert.assertThrows; + +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.Waddle; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.model.itinerary.NameContainsKeywordsPredicate; +import seedu.waddle.testutil.EditItineraryDescriptorBuilder; + +/** + * Contains helper methods for testing commands. + */ +public class CommandTestUtil { + public static final String VALID_ITINERARY_DESC_SUMMER = "Summer Trip"; + public static final String VALID_ITINERARY_DESC_WINTER = "Winter Trip"; + public static final String VALID_ITINERARY_DESC_TEST = "Test Name"; + + public static final String VALID_COUNTRY_SUMMER = "Sweden"; + public static final String VALID_COUNTRY_WINTER = "Japan"; + public static final String VALID_COUNTRY_TEST = "Test Country"; + + public static final String VALID_START_DATE_SUMMER = "2023-08-01"; + public static final String VALID_START_DATE_WINTER = "2023-01-01"; + public static final String VALID_START_DATE_TEST = "2011-11-11"; + public static final String VALID_DURATION_SUMMER = "26"; + public static final String VALID_DURATION_WINTER = "31"; + public static final String VALID_DURATION_TEST = "3"; + + public static final String VALID_PEOPLE_SUMMER = "5"; + public static final String VALID_PEOPLE_WINTER = "10"; + public static final String VALID_PEOPLE_TEST = "69"; + + public static final String VALID_BUDGET_SUMMER = "5000"; + public static final String VALID_BUDGET_WINTER = "200"; + public static final String VALID_BUDGET_TEST = "970.00"; + + public static final String ITINERARY_DESC_DESC_SUMMER = " " + PREFIX_DESCRIPTION + VALID_ITINERARY_DESC_SUMMER; + public static final String ITINERARY_DESC_DESC_WINTER = " " + PREFIX_DESCRIPTION + VALID_ITINERARY_DESC_WINTER; + public static final String ITINERARY_DESC_DESC_TEST = " " + PREFIX_DESCRIPTION + VALID_ITINERARY_DESC_TEST; + + public static final String COUNTRY_DESC_SUMMER = " " + PREFIX_COUNTRY + VALID_COUNTRY_SUMMER; + public static final String COUNTRY_DESC_WINTER = " " + PREFIX_COUNTRY + VALID_COUNTRY_WINTER; + public static final String COUNTRY_DESC_TEST = " " + PREFIX_COUNTRY + VALID_COUNTRY_TEST; + + public static final String START_DATE_DESC_SUMMER = " " + PREFIX_START_DATE + VALID_START_DATE_SUMMER; + public static final String START_DATE_DESC_WINTER = " " + PREFIX_START_DATE + VALID_START_DATE_WINTER; + public static final String START_DATE_DESC_TEST = " " + PREFIX_START_DATE + VALID_START_DATE_TEST; + public static final String DURATION_DESC_SUMMER = " " + PREFIX_ITINERARY_DURATION + VALID_DURATION_SUMMER; + public static final String DURATION_DESC_WINTER = " " + PREFIX_ITINERARY_DURATION + VALID_DURATION_WINTER; + public static final String DURATION_DESC_TEST = " " + PREFIX_ITINERARY_DURATION + VALID_DURATION_TEST; + + public static final String PEOPLE_DESC_SUMMER = " " + PREFIX_PEOPLE + VALID_PEOPLE_SUMMER; + public static final String PEOPLE_DESC_WINTER = " " + PREFIX_PEOPLE + VALID_PEOPLE_WINTER; + public static final String PEOPLE_DESC_TEST = " " + PREFIX_PEOPLE + VALID_PEOPLE_TEST; + + public static final String BUDGET_DESC_SUMMER = " " + PREFIX_BUDGET + VALID_BUDGET_SUMMER; + public static final String BUDGET_DESC_WINTER = " " + PREFIX_BUDGET + VALID_BUDGET_WINTER; + public static final String BUDGET_DESC_TEST = " " + PREFIX_BUDGET + VALID_BUDGET_TEST; + + public static final String INVALID_DESC_DESC = " " + PREFIX_DESCRIPTION + "Family Trip^"; // '&' not allowed + public static final String INVALID_COUNTRY_DESC = " " + PREFIX_COUNTRY + "Germany("; // '(' not allowed in country + public static final String INVALID_START_DATE_DESC = " " + PREFIX_START_DATE + "Jan 01"; // wrong format + public static final String INVALID_DURATION_DESC = " " + PREFIX_ITINERARY_DURATION + + "3 days"; // only numbers allowed for duration + public static final String INVALID_PEOPLE_DESC = " " + PREFIX_PEOPLE + "five"; // only numbers allowed for people + public static final String INVALID_BUDGET_DESC = " " + PREFIX_BUDGET + "$100"; // only numbers allowed for budget + + public static final String VALID_ITEM_DESC_SHOPPING = "Shopping"; + public static final String VALID_ITEM_DESC_SKINNY = "Skinny Dipping"; + public static final String VALID_ITEM_DESC_ART = "Art Museum"; + public static final String VALID_ITEM_DESC_BEACH = "Beach Party"; + public static final String VALID_ITEM_DESC_TOUR = "Visit tourist spot"; + public static final String VALID_ITEM_DESC_BREAKFAST = "Hotel breakfast"; + public static final String VALID_ITEM_DESC_LUNCH = "Eat lunch"; + + public static final String VALID_DURATION_SHOPPING = "30"; + public static final String VALID_DURATION_SKINNY = "45"; + public static final String VALID_DURATION_ART = "60"; + public static final String VALID_DURATION_BEACH = "240"; + public static final String VALID_DURATION_TOUR = "180"; + public static final String VALID_DURATION_BREAKFAST = "20"; + public static final String VALID_DURATION_LUNCH = "50"; + + public static final LocalTime VALID_START_TIME_2330 = LocalTime.parse("23:30"); + public static final LocalTime VALID_START_TIME_0000 = LocalTime.parse("00:00"); + public static final LocalTime VALID_START_TIME_1715 = LocalTime.parse("17:15"); + public static final LocalTime VALID_START_TIME_1200 = LocalTime.parse("12:00"); + + public static final String VALID_COST_SHOPPING = "0"; + public static final String VALID_COST_SKINNY = "10"; + public static final String VALID_COST_ART = "100"; + public static final String VALID_COST_BEACH = "1000"; + public static final String VALID_COST_TOUR = "200"; + public static final String VALID_COST_BREAKFAST = "15"; + public static final String VALID_COST_LUNCH = "50"; + + public static final int VALID_PRIORITY_SHOPPING = 2; + public static final int VALID_PRIORITY_SKINNY = 3; + public static final int VALID_PRIORITY_ART = 4; + public static final int VALID_PRIORITY_BEACH = 5; + public static final int VALID_PRIORITY_TOUR = 1; + public static final int VALID_PRIORITY_BREAKFAST = 2; + public static final int VALID_PRIORITY_LUNCH = 4; + + + public static final String VALID_DAY_NUMBER = "1"; + + public static final String ITEM_DESC_DESC_SHOPPING = " " + PREFIX_DESCRIPTION + VALID_ITEM_DESC_SHOPPING; + public static final String ITEM_DESC_DESC_SKINNY = " " + PREFIX_DESCRIPTION + VALID_ITEM_DESC_SKINNY; + public static final String ITEM_DESC_DESC_ART = " " + PREFIX_DESCRIPTION + VALID_ITEM_DESC_ART; + public static final String ITEM_DESC_DESC_BEACH = " " + PREFIX_DESCRIPTION + VALID_ITEM_DESC_BEACH; + public static final String ITEM_DESC_DESC_TOUR = " " + PREFIX_DESCRIPTION + VALID_ITEM_DESC_TOUR; + public static final String ITEM_DESC_DESC_BREAKFAST = " " + PREFIX_DESCRIPTION + VALID_ITEM_DESC_BREAKFAST; + public static final String ITEM_DESC_DESC_LUNCH = " " + PREFIX_DESCRIPTION + VALID_ITEM_DESC_LUNCH; + + public static final String ITEM_DURATION_DESC_SHOPPING = " " + PREFIX_ITEM_DURATION + VALID_DURATION_SHOPPING; + public static final String ITEM_DURATION_DESC_SKINNY = " " + PREFIX_ITEM_DURATION + VALID_DURATION_SKINNY; + public static final String ITEM_DURATION_DESC_ART = " " + PREFIX_ITEM_DURATION + VALID_DURATION_ART; + public static final String ITEM_DURATION_DESC_BEACH = " " + PREFIX_ITEM_DURATION + VALID_DURATION_BEACH; + public static final String ITEM_DURATION_DESC_TOUR = " " + PREFIX_ITEM_DURATION + VALID_DURATION_TOUR; + public static final String ITEM_DURATION_DESC_BREAKFAST = " " + PREFIX_ITEM_DURATION + VALID_DURATION_BREAKFAST; + public static final String ITEM_DURATION_DESC_LUNCH = " " + PREFIX_ITEM_DURATION + VALID_DURATION_LUNCH; + + public static final String START_TIME_DESC_2300 = " " + PREFIX_START_TIME + VALID_START_TIME_2330; + public static final String START_TIME_DESC_0000 = " " + PREFIX_START_TIME + VALID_START_TIME_0000; + public static final String START_TIME_DESC_1715 = " " + PREFIX_START_TIME + VALID_START_TIME_1715; + public static final String START_TIME_DESC_1200 = " " + PREFIX_START_TIME + VALID_START_TIME_1200; + + public static final String COST_DESC_SHOPPING = " " + PREFIX_COST + VALID_COST_SHOPPING; + public static final String COST_DESC_SKINNY = " " + PREFIX_COST + VALID_COST_SKINNY; + public static final String COST_DESC_ART = " " + PREFIX_COST + VALID_COST_ART; + public static final String COST_DESC_BEACH = " " + PREFIX_COST + VALID_COST_BEACH; + public static final String COST_DESC_TOUR = " " + PREFIX_COST + VALID_COST_TOUR; + public static final String COST_DESC_BREAKFAST = " " + PREFIX_COST + VALID_COST_BREAKFAST; + public static final String COST_DESC_LUNCH = " " + PREFIX_COST + VALID_COST_LUNCH; + + + public static final String PRIORITY_DESC_SHOPPING = " " + PREFIX_PRIORITY + VALID_PRIORITY_SHOPPING; + public static final String PRIORITY_DESC_SKINNY = " " + PREFIX_PRIORITY + VALID_PRIORITY_SKINNY; + public static final String PRIORITY_DESC_ART = " " + PREFIX_PRIORITY + VALID_PRIORITY_ART; + public static final String PRIORITY_DESC_BEACH = " " + PREFIX_PRIORITY + VALID_PRIORITY_BEACH; + public static final String PRIORITY_DESC_TOUR = " " + PREFIX_PRIORITY + VALID_PRIORITY_TOUR; + public static final String PRIORITY_DESC_BREAKFAST = " " + PREFIX_PRIORITY + VALID_PRIORITY_BREAKFAST; + public static final String PRIORITY_DESC_LUNCH = " " + PREFIX_PRIORITY + VALID_PRIORITY_LUNCH; + + + public static final String DAY_NUMBER_DESC = " " + PREFIX_DAY_NUMBER + VALID_DAY_NUMBER; + + public static final String INVALID_ITEM_DESC_DESC = " " + PREFIX_DESCRIPTION + "吃饭"; + public static final String INVALID_ITEM_DURATION_DESC = " " + PREFIX_ITEM_DURATION + "abc"; + public static final String INVALID_START_TIME_DESC = " " + PREFIX_START_TIME + "1200"; + public static final String INVALID_COST_DESC = " " + PREFIX_COST + "$500"; + public static final String INVALID_PRIORITY_DESC = " " + PREFIX_PRIORITY + "nice"; + public static final String INVALID_DAY_NUMBER_DESC = " " + PREFIX_DAY_NUMBER + "-1"; + + public static final String PREAMBLE_WHITESPACE = "\t \r \n"; + public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble"; + + public static final EditCommand.EditItineraryDescriptor DESC_SUMMER; + public static final EditCommand.EditItineraryDescriptor DESC_WINTER; + + static { + DESC_SUMMER = new EditItineraryDescriptorBuilder().withDescription(VALID_ITINERARY_DESC_SUMMER) + .withCountry(VALID_COUNTRY_SUMMER).withStartDate(VALID_START_DATE_SUMMER) + .withDuration(VALID_DURATION_SUMMER) + .withPeople(VALID_PEOPLE_SUMMER) + .withBudget(VALID_BUDGET_SUMMER).build(); + DESC_WINTER = new EditItineraryDescriptorBuilder().withDescription(VALID_ITINERARY_DESC_WINTER) + .withCountry(VALID_COUNTRY_WINTER).withStartDate(VALID_START_DATE_WINTER) + .withDuration(VALID_DURATION_WINTER) + .withPeople(VALID_PEOPLE_WINTER) + .withBudget(VALID_BUDGET_WINTER).build(); + } + + /** + * Executes the given {@code command}, confirms that
+ * - the returned {@link CommandResult} matches {@code expectedCommandResult}
+ * - the {@code actualModel} matches {@code expectedModel} + */ + public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult, + Model expectedModel) { + try { + CommandResult result = command.execute(actualModel); + assertEquals(expectedCommandResult, result); + assertEquals(expectedModel, actualModel); + } catch (CommandException ce) { + throw new AssertionError("Execution of command should not fail.", ce); + } + } + + /** + * Convenience wrapper to {@link #assertCommandSuccess(Command, Model, CommandResult, Model)} + * that takes a string {@code expectedMessage}. + */ + public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage, + Model expectedModel) { + CommandResult expectedCommandResult = new CommandResult(expectedMessage); + assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel); + } + + /** + * Executes the given {@code command}, confirms that
+ * - a {@code CommandException} is thrown
+ * - the CommandException message matches {@code expectedMessage}
+ * - the address book, filtered person list and selected person in {@code actualModel} remain unchanged + */ + public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) { + // we are unable to defensively copy the model for comparison later, so we can + // only do so by copying its components. + Waddle expectedAddressBook = new Waddle(actualModel.getWaddle()); + List expectedFilteredList = new ArrayList<>(actualModel.getFilteredItineraryList()); + + assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel)); + assertEquals(expectedAddressBook, actualModel.getWaddle()); + assertEquals(expectedFilteredList, actualModel.getFilteredItineraryList()); + } + + /** + * Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the + * {@code model}'s address book. + */ + public static void showItineraryAtIndex(Model model, Index targetIndex) { + assertTrue(targetIndex.getZeroBased() < model.getFilteredItineraryList().size()); + + Itinerary itinerary = model.getFilteredItineraryList().get(targetIndex.getZeroBased()); + final String[] splitName = itinerary.getDescription().description.split("\\s+"); + model.updateFilteredItineraryList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); + + assertEquals(1, model.getFilteredItineraryList().size()); + } + +} diff --git a/src/test/java/seedu/waddle/logic/commands/CopyCommandTest.java b/src/test/java/seedu/waddle/logic/commands/CopyCommandTest.java new file mode 100644 index 00000000000..f404929de1f --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/CopyCommandTest.java @@ -0,0 +1,86 @@ +package seedu.waddle.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.waddle.testutil.TypicalItineraries.getTypicalWaddle; + +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; + +import org.apache.commons.lang3.SystemUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.waddle.logic.StageManager; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.UserPrefs; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * Contains integration tests (interaction with the Model) and unit tests for ListCommand. + */ +public class CopyCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalWaddle(), new UserPrefs()); + expectedModel = new ModelManager(model.getWaddle(), new UserPrefs()); + } + + @Test + public void execute_correctStage_correctOutput() { + // copy does not work on linux + if (SystemUtils.IS_OS_LINUX) { + return; + } + + // select first itinerary + Itinerary selectedItinerary = model.getFilteredItineraryList().get(0); + StageManager.getInstance().setWishStage(selectedItinerary); + String expectedCommandResult = String.format(CopyCommand.MESSAGE_SUCCESS, + selectedItinerary.getDescription()); + + assertCommandSuccess(new CopyCommand(), model, expectedCommandResult, expectedModel); + + String actualClipboardData = getClipboardData(); + String expectedClipboardData = "Spring Trip" + System.lineSeparator() + + " Country: Australia" + System.lineSeparator() + + " Duration: 14 Days" + System.lineSeparator() + + " Dates: 2023-01-01 - 2023-01-14" + System.lineSeparator() + + " Waddlers: 1" + System.lineSeparator() + + " Budget: $300.00" + System.lineSeparator() + + System.lineSeparator() + "Day 1" + System.lineSeparator() + + System.lineSeparator() + "Day 2" + System.lineSeparator() + + System.lineSeparator() + "Day 3" + System.lineSeparator() + + System.lineSeparator() + "Day 4" + System.lineSeparator() + + System.lineSeparator() + "Day 5" + System.lineSeparator() + + System.lineSeparator() + "Day 6" + System.lineSeparator() + + System.lineSeparator() + "Day 7" + System.lineSeparator() + + System.lineSeparator() + "Day 8" + System.lineSeparator() + + System.lineSeparator() + "Day 9" + System.lineSeparator() + + System.lineSeparator() + "Day 10" + System.lineSeparator() + + System.lineSeparator() + "Day 11" + System.lineSeparator() + + System.lineSeparator() + "Day 12" + System.lineSeparator() + + System.lineSeparator() + "Day 13" + System.lineSeparator() + + System.lineSeparator() + "Day 14" + System.lineSeparator() + System.lineSeparator(); + + assertEquals(expectedClipboardData, actualClipboardData); + } + + private String getClipboardData() { + String data = ""; + try { + data = (String) Toolkit.getDefaultToolkit() + .getSystemClipboard().getData(DataFlavor.stringFlavor); + } catch (UnsupportedFlavorException | IOException e) { + assert false : e.getMessage(); + } + return data; + } +} diff --git a/src/test/java/seedu/waddle/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/waddle/logic/commands/DeleteCommandTest.java new file mode 100644 index 00000000000..88dc1a2eb23 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/DeleteCommandTest.java @@ -0,0 +1,109 @@ +package seedu.waddle.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.waddle.logic.commands.CommandTestUtil.showItineraryAtIndex; +import static seedu.waddle.testutil.TypicalIndexes.INDEX_FIRST_ITINERARY; +import static seedu.waddle.testutil.TypicalIndexes.INDEX_SECOND_ITINERARY; +import static seedu.waddle.testutil.TypicalItineraries.getTypicalWaddle; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.core.Messages; +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.UserPrefs; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code DeleteCommand}. + */ +public class DeleteCommandTest { + + private Model model = new ModelManager(getTypicalWaddle(), new UserPrefs()); + + @Test + public void execute_validIndexUnfilteredList_success() { + Itinerary itineraryToDelete = model.getFilteredItineraryList().get(INDEX_FIRST_ITINERARY.getZeroBased()); + DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_ITINERARY); + + String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_ITINERARY_SUCCESS, itineraryToDelete); + + ModelManager expectedModel = new ModelManager(model.getWaddle(), new UserPrefs()); + expectedModel.deleteItinerary(itineraryToDelete); + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredItineraryList().size() + 1); + DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); + + assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_ITINERARY_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + showItineraryAtIndex(model, INDEX_FIRST_ITINERARY); + + Itinerary itineraryToDelete = model.getFilteredItineraryList().get(INDEX_FIRST_ITINERARY.getZeroBased()); + DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_ITINERARY); + + String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_ITINERARY_SUCCESS, itineraryToDelete); + + Model expectedModel = new ModelManager(model.getWaddle(), new UserPrefs()); + expectedModel.deleteItinerary(itineraryToDelete); + showNoItinerary(expectedModel); + + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showItineraryAtIndex(model, INDEX_FIRST_ITINERARY); + + Index outOfBoundIndex = INDEX_SECOND_ITINERARY; + // ensures that outOfBoundIndex is still in bounds of Waddle list + assertTrue(outOfBoundIndex.getZeroBased() < model.getWaddle().getItineraryList().size()); + + DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); + + assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_ITINERARY_DISPLAYED_INDEX); + } + + @Test + public void equals() { + DeleteCommand deleteFirstCommand = new DeleteCommand(INDEX_FIRST_ITINERARY); + DeleteCommand deleteSecondCommand = new DeleteCommand(INDEX_SECOND_ITINERARY); + + // same object -> returns true + assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); + + // same values -> returns true + DeleteCommand deleteFirstCommandCopy = new DeleteCommand(INDEX_FIRST_ITINERARY); + assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); + + // different types -> returns false + assertFalse(deleteFirstCommand.equals(1)); + + // null -> returns false + assertFalse(deleteFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(deleteFirstCommand.equals(deleteSecondCommand)); + } + + /** + * Updates {@code model}'s filtered list to show no itinerary. + */ + private void showNoItinerary(Model model) { + model.updateFilteredItineraryList(p -> false); + + assertTrue(model.getFilteredItineraryList().isEmpty()); + } +} diff --git a/src/test/java/seedu/waddle/logic/commands/DeleteItemCommandTest.java b/src/test/java/seedu/waddle/logic/commands/DeleteItemCommandTest.java new file mode 100644 index 00000000000..b0299fec837 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/DeleteItemCommandTest.java @@ -0,0 +1,34 @@ +package seedu.waddle.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.waddle.testutil.TypicalItineraries.getTypicalWaddle; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.core.index.MultiIndex; +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.UserPrefs; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.itinerary.Itinerary; + +public class DeleteItemCommandTest { + private Model model = new ModelManager(getTypicalWaddle(), new UserPrefs()); + + @Test + public void execute_validIndex_success() throws CommandException { + Itinerary itinerary = model.getFilteredItineraryList().get(1); + MultiIndex targetIndex = new MultiIndex(); + targetIndex.appendZeroBasedIndex(0); + Item targetItem = itinerary.getItem(targetIndex); + StageManager stageManager = StageManager.getInstance(); + stageManager.setWishStage(itinerary); + CommandResult commandResult = new DeleteItemCommand(targetIndex).execute(model); + + assertEquals(String.format(DeleteItemCommand.MESSAGE_DELETE_ITEM_SUCCESS, targetItem), + commandResult.getFeedbackToUser()); + assertEquals(false, itinerary.hasItem(targetItem)); + } +} diff --git a/src/test/java/seedu/waddle/logic/commands/EditCommandTest.java b/src/test/java/seedu/waddle/logic/commands/EditCommandTest.java new file mode 100644 index 00000000000..61def4f5e9c --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/EditCommandTest.java @@ -0,0 +1,172 @@ +package seedu.waddle.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.logic.commands.CommandTestUtil.DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COUNTRY_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITINERARY_DESC_TEST; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITINERARY_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PEOPLE_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.waddle.logic.commands.CommandTestUtil.showItineraryAtIndex; +import static seedu.waddle.testutil.TypicalIndexes.INDEX_FIRST_ITINERARY; +import static seedu.waddle.testutil.TypicalIndexes.INDEX_SECOND_ITINERARY; +import static seedu.waddle.testutil.TypicalItineraries.getTypicalWaddle; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.core.Messages; +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.commands.EditCommand.EditItineraryDescriptor; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.UserPrefs; +import seedu.waddle.model.Waddle; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.testutil.EditItineraryDescriptorBuilder; +import seedu.waddle.testutil.ItineraryBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for EditCommand. + */ +public class EditCommandTest { + + private final Model model = new ModelManager(getTypicalWaddle(), new UserPrefs()); + + @Test + public void execute_allFieldsSpecifiedUnfilteredList_success() { + Itinerary editedItinerary = new ItineraryBuilder().build(); + EditItineraryDescriptor descriptor = new EditItineraryDescriptorBuilder(editedItinerary).build(); + EditCommand editCommand = new EditCommand(INDEX_FIRST_ITINERARY, descriptor); + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_ITINERARY_SUCCESS, editedItinerary); + + Model expectedModel = new ModelManager(new Waddle(model.getWaddle()), new UserPrefs()); + expectedModel.setItinerary(model.getFilteredItineraryList().get(0), editedItinerary); + + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_someFieldsSpecifiedUnfilteredList_success() { + Index indexLastItinerary = Index.fromOneBased(model.getFilteredItineraryList().size()); + Itinerary lastItinerary = model.getFilteredItineraryList().get(indexLastItinerary.getZeroBased()); + + ItineraryBuilder itineraryInList = new ItineraryBuilder(lastItinerary); + Itinerary editedItinerary = itineraryInList.withDescription(VALID_ITINERARY_DESC_TEST) + .withCountry(VALID_COUNTRY_WINTER).withPeople(VALID_PEOPLE_SUMMER).build(); + + EditItineraryDescriptor descriptor = + new EditItineraryDescriptorBuilder().withDescription(VALID_ITINERARY_DESC_TEST) + .withCountry(VALID_COUNTRY_WINTER).withPeople(VALID_PEOPLE_SUMMER).build(); + EditCommand editCommand = new EditCommand(indexLastItinerary, descriptor); + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_ITINERARY_SUCCESS, editedItinerary); + + Model expectedModel = new ModelManager(new Waddle(model.getWaddle()), new UserPrefs()); + expectedModel.setItinerary(lastItinerary, editedItinerary); + + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_noFieldSpecifiedUnfilteredList_success() { + EditCommand editCommand = new EditCommand(INDEX_FIRST_ITINERARY, new EditItineraryDescriptor()); + Itinerary editedItinerary = model.getFilteredItineraryList().get(INDEX_FIRST_ITINERARY.getZeroBased()); + + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_ITINERARY_SUCCESS, editedItinerary); + + Model expectedModel = new ModelManager(new Waddle(model.getWaddle()), new UserPrefs()); + + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_filteredList_success() { + showItineraryAtIndex(model, INDEX_FIRST_ITINERARY); + + Itinerary itineraryInFilteredList = model.getFilteredItineraryList().get(INDEX_FIRST_ITINERARY.getZeroBased()); + Itinerary editedItinerary = new ItineraryBuilder(itineraryInFilteredList) + .withDescription(VALID_ITINERARY_DESC_TEST).build(); + EditCommand editCommand = new EditCommand(INDEX_FIRST_ITINERARY, + new EditItineraryDescriptorBuilder().withDescription(VALID_ITINERARY_DESC_TEST).build()); + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_ITINERARY_SUCCESS, editedItinerary); + + Model expectedModel = new ModelManager(new Waddle(model.getWaddle()), new UserPrefs()); + expectedModel.setItinerary(model.getFilteredItineraryList().get(0), editedItinerary); + + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_duplicateItineraryUnfilteredList_failure() { + Itinerary firstItinerary = model.getFilteredItineraryList().get(INDEX_FIRST_ITINERARY.getZeroBased()); + EditItineraryDescriptor descriptor = new EditItineraryDescriptorBuilder(firstItinerary).build(); + EditCommand editCommand = new EditCommand(INDEX_SECOND_ITINERARY, descriptor); + assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_ITINERARY); + } + + @Test + public void execute_duplicateItineraryFilteredList_failure() { + showItineraryAtIndex(model, INDEX_FIRST_ITINERARY); + + // edit itinerary in filtered list into a duplicate in Waddle + Itinerary itineraryInList = model.getWaddle().getItineraryList().get(INDEX_SECOND_ITINERARY.getZeroBased()); + EditCommand editCommand = new EditCommand(INDEX_FIRST_ITINERARY, + new EditItineraryDescriptorBuilder(itineraryInList).build()); + + assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_ITINERARY); + } + + @Test + public void execute_invalidItineraryIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredItineraryList().size() + 1); + EditItineraryDescriptor descriptor = new EditItineraryDescriptorBuilder() + .withDescription(VALID_ITINERARY_DESC_WINTER).build(); + EditCommand editCommand = new EditCommand(outOfBoundIndex, descriptor); + + assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_ITINERARY_DISPLAYED_INDEX); + } + + /** + * Edit filtered list where index is larger than size of filtered list, + * but smaller than size of itinerary list + */ + @Test + public void execute_invalidItineraryIndexFilteredList_failure() { + showItineraryAtIndex(model, INDEX_FIRST_ITINERARY); + Index outOfBoundIndex = INDEX_SECOND_ITINERARY; + // ensures that outOfBoundIndex is still in bounds of address book list + assertTrue(outOfBoundIndex.getZeroBased() < model.getWaddle().getItineraryList().size()); + + EditCommand editCommand = new EditCommand(outOfBoundIndex, + new EditItineraryDescriptorBuilder().withDescription(VALID_ITINERARY_DESC_WINTER).build()); + assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_ITINERARY_DISPLAYED_INDEX); + } + + @Test + public void equals() { + final EditCommand standardCommand = new EditCommand(INDEX_FIRST_ITINERARY, DESC_SUMMER); + + // same values -> returns true + EditItineraryDescriptor copyDescriptor = new EditItineraryDescriptor(DESC_SUMMER); + EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_ITINERARY, copyDescriptor); + assertTrue(standardCommand.equals(commandWithSameValues)); + + // same object -> returns true + assertTrue(standardCommand.equals(standardCommand)); + + // null -> returns false + assertFalse(standardCommand.equals(null)); + + // different types -> returns false + assertFalse(standardCommand.equals(new ClearCommand())); + + // different index -> returns false + assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_ITINERARY, DESC_SUMMER))); + + // different descriptor -> returns false + assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_ITINERARY, DESC_WINTER))); + } + +} diff --git a/src/test/java/seedu/waddle/logic/commands/EditItemCommandTest.java b/src/test/java/seedu/waddle/logic/commands/EditItemCommandTest.java new file mode 100644 index 00000000000..2a3a8c69703 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/EditItemCommandTest.java @@ -0,0 +1,4 @@ +package seedu.waddle.logic.commands; + +public class EditItemCommandTest { +} diff --git a/src/test/java/seedu/waddle/logic/commands/EditItineraryDescriptorTest.java b/src/test/java/seedu/waddle/logic/commands/EditItineraryDescriptorTest.java new file mode 100644 index 00000000000..18e9c3ed71f --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/EditItineraryDescriptorTest.java @@ -0,0 +1,59 @@ +package seedu.waddle.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.logic.commands.CommandTestUtil.DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COUNTRY_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITINERARY_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PEOPLE_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_START_DATE_WINTER; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.logic.commands.EditCommand.EditItineraryDescriptor; +import seedu.waddle.testutil.EditItineraryDescriptorBuilder; + +public class EditItineraryDescriptorTest { + + @Test + public void equals() { + // same values -> returns true + EditItineraryDescriptor descriptorWithSameValues = new EditItineraryDescriptor(DESC_SUMMER); + assertTrue(DESC_SUMMER.equals(descriptorWithSameValues)); + + // same object -> returns true + assertTrue(DESC_SUMMER.equals(DESC_SUMMER)); + + // null -> returns false + assertFalse(DESC_SUMMER.equals(null)); + + // different types -> returns false + assertFalse(DESC_SUMMER.equals(5)); + + // different values -> returns false + assertFalse(DESC_SUMMER.equals(DESC_WINTER)); + + // different name -> returns false + EditItineraryDescriptor editedSummer = + new EditItineraryDescriptorBuilder(DESC_SUMMER).withDescription(VALID_ITINERARY_DESC_WINTER).build(); + assertFalse(DESC_SUMMER.equals(editedSummer)); + + // different country -> returns false + editedSummer = new EditItineraryDescriptorBuilder(DESC_SUMMER).withCountry(VALID_COUNTRY_WINTER).build(); + assertFalse(DESC_SUMMER.equals(editedSummer)); + + // different start date -> returns false + editedSummer = new EditItineraryDescriptorBuilder(DESC_SUMMER).withStartDate(VALID_START_DATE_WINTER).build(); + assertFalse(DESC_SUMMER.equals(editedSummer)); + + // different end date -> returns false + editedSummer = new EditItineraryDescriptorBuilder(DESC_SUMMER).withDuration(VALID_DURATION_WINTER).build(); + assertFalse(DESC_SUMMER.equals(editedSummer)); + + // different people -> returns false + editedSummer = new EditItineraryDescriptorBuilder(DESC_SUMMER).withPeople(VALID_PEOPLE_WINTER).build(); + assertFalse(DESC_SUMMER.equals(editedSummer)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/seedu/waddle/logic/commands/ExitCommandTest.java similarity index 60% rename from src/test/java/seedu/address/logic/commands/ExitCommandTest.java rename to src/test/java/seedu/waddle/logic/commands/ExitCommandTest.java index 9533c473875..4761c817ce3 100644 --- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java +++ b/src/test/java/seedu/waddle/logic/commands/ExitCommandTest.java @@ -1,12 +1,12 @@ -package seedu.address.logic.commands; +package seedu.waddle.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.waddle.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; public class ExitCommandTest { private Model model = new ModelManager(); diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/waddle/logic/commands/FindCommandTest.java similarity index 59% rename from src/test/java/seedu/address/logic/commands/FindCommandTest.java rename to src/test/java/seedu/waddle/logic/commands/FindCommandTest.java index 9b15db28bbb..8ed2c29b173 100644 --- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java +++ b/src/test/java/seedu/waddle/logic/commands/FindCommandTest.java @@ -1,31 +1,30 @@ -package seedu.address.logic.commands; +package seedu.waddle.logic.commands; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.core.Messages.MESSAGE_PERSONS_LISTED_OVERVIEW; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.CARL; -import static seedu.address.testutil.TypicalPersons.ELLE; -import static seedu.address.testutil.TypicalPersons.FIONA; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.waddle.commons.core.Messages.MESSAGE_ITINERARIES_LISTED_OVERVIEW; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.waddle.testutil.TypicalItineraries.AUTUMN; +import static seedu.waddle.testutil.TypicalItineraries.SPRING; +import static seedu.waddle.testutil.TypicalItineraries.getTypicalWaddle; import java.util.Arrays; import java.util.Collections; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.UserPrefs; +import seedu.waddle.model.itinerary.NameContainsKeywordsPredicate; /** * Contains integration tests (interaction with the Model) for {@code FindCommand}. */ public class FindCommandTest { - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + private Model model = new ModelManager(getTypicalWaddle(), new UserPrefs()); + private Model expectedModel = new ModelManager(getTypicalWaddle(), new UserPrefs()); @Test public void equals() { @@ -50,28 +49,28 @@ public void equals() { // null -> returns false assertFalse(findFirstCommand.equals(null)); - // different person -> returns false + // different itinerary -> returns false assertFalse(findFirstCommand.equals(findSecondCommand)); } @Test - public void execute_zeroKeywords_noPersonFound() { - String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0); + public void execute_zeroKeywords_noItineraryFound() { + String expectedMessage = String.format(MESSAGE_ITINERARIES_LISTED_OVERVIEW, 0); NameContainsKeywordsPredicate predicate = preparePredicate(" "); FindCommand command = new FindCommand(predicate); - expectedModel.updateFilteredPersonList(predicate); + expectedModel.updateFilteredItineraryList(predicate); assertCommandSuccess(command, model, expectedMessage, expectedModel); - assertEquals(Collections.emptyList(), model.getFilteredPersonList()); + assertEquals(Collections.emptyList(), model.getFilteredItineraryList()); } @Test - public void execute_multipleKeywords_multiplePersonsFound() { - String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3); - NameContainsKeywordsPredicate predicate = preparePredicate("Kurz Elle Kunz"); + public void execute_multipleKeywords_multipleItinerariesFound() { + String expectedMessage = String.format(MESSAGE_ITINERARIES_LISTED_OVERVIEW, 2); + NameContainsKeywordsPredicate predicate = preparePredicate("Spring Autumn"); FindCommand command = new FindCommand(predicate); - expectedModel.updateFilteredPersonList(predicate); + expectedModel.updateFilteredItineraryList(predicate); assertCommandSuccess(command, model, expectedMessage, expectedModel); - assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList()); + assertEquals(Arrays.asList(SPRING, AUTUMN), model.getFilteredItineraryList()); } /** diff --git a/src/test/java/seedu/waddle/logic/commands/FreeCommandTest.java b/src/test/java/seedu/waddle/logic/commands/FreeCommandTest.java new file mode 100644 index 00000000000..a98995510a9 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/FreeCommandTest.java @@ -0,0 +1,38 @@ +package seedu.waddle.logic.commands; + +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.waddle.testutil.TypicalItineraries.getTypicalWaddle; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.waddle.logic.StageManager; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.UserPrefs; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * Contains integration tests (interaction with the Model) and unit tests for ListCommand. + */ +public class FreeCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalWaddle(), new UserPrefs()); + expectedModel = new ModelManager(model.getWaddle(), new UserPrefs()); + } + + @Test + public void execute_correctStage_correctOutput() { + // select third itinerary + Itinerary selectedItinerary = model.getFilteredItineraryList().get(2); + StageManager.getInstance().setWishStage(selectedItinerary); + String expectedCommandResult = selectedItinerary.getVacantSlots(); + + assertCommandSuccess(new FreeCommand(), model, expectedCommandResult, expectedModel); + } +} diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/waddle/logic/commands/HelpCommandTest.java similarity index 61% rename from src/test/java/seedu/address/logic/commands/HelpCommandTest.java rename to src/test/java/seedu/waddle/logic/commands/HelpCommandTest.java index 4904fc4352e..fce0c7498c2 100644 --- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java +++ b/src/test/java/seedu/waddle/logic/commands/HelpCommandTest.java @@ -1,12 +1,12 @@ -package seedu.address.logic.commands; +package seedu.waddle.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.waddle.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; public class HelpCommandTest { private Model model = new ModelManager(); diff --git a/src/test/java/seedu/waddle/logic/commands/HomeCommandTest.java b/src/test/java/seedu/waddle/logic/commands/HomeCommandTest.java new file mode 100644 index 00000000000..32e996a84cd --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/HomeCommandTest.java @@ -0,0 +1,60 @@ +package seedu.waddle.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.logic.StageManager; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.testutil.ItineraryBuilder; + +public class HomeCommandTest { + + private final Model model = new ModelManager(); + private final Model expectedModel = new ModelManager(); + + @Test + public void execute_atHomeStage_success() { + HomeCommand homeCommand = new HomeCommand(); + + String expectedMessage = HomeCommand.MESSAGE_ALREADY_HOME_SUCCESS; + StageManager.getInstance().setHomeStage(); + + assertCommandSuccess(homeCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_notAtHomeStage_success() { + HomeCommand homeCommand = new HomeCommand(); + + String expectedMessage = HomeCommand.MESSAGE_HOME_SUCCESS; + + Itinerary itinerary = new ItineraryBuilder().build(); + StageManager.getInstance().setWishStage(itinerary); + + assertCommandSuccess(homeCommand, model, expectedMessage, expectedModel); + } + + @Test + public void equals() { + HomeCommand homeCommand = new HomeCommand(); + HomeCommand homeCommandOther = new HomeCommand(); + + // same object -> returns true + assertTrue(homeCommand.equals(homeCommand)); + + // another home command -> returns true + assertTrue(homeCommand.equals(homeCommandOther)); + + // different types -> returns false + assertFalse(homeCommand.equals(1)); + + // null -> returns false + assertFalse(homeCommand.equals(null)); + + } +} diff --git a/src/test/java/seedu/address/logic/commands/ListCommandTest.java b/src/test/java/seedu/waddle/logic/commands/ListCommandTest.java similarity index 50% rename from src/test/java/seedu/address/logic/commands/ListCommandTest.java rename to src/test/java/seedu/waddle/logic/commands/ListCommandTest.java index 435ff1f7275..ff2eaa0c5b9 100644 --- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java +++ b/src/test/java/seedu/waddle/logic/commands/ListCommandTest.java @@ -1,16 +1,16 @@ -package seedu.address.logic.commands; +package seedu.waddle.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.waddle.logic.commands.CommandTestUtil.showItineraryAtIndex; +import static seedu.waddle.testutil.TypicalIndexes.INDEX_FIRST_ITINERARY; +import static seedu.waddle.testutil.TypicalItineraries.getTypicalWaddle; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.UserPrefs; /** * Contains integration tests (interaction with the Model) and unit tests for ListCommand. @@ -22,8 +22,8 @@ public class ListCommandTest { @BeforeEach public void setUp() { - model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + model = new ModelManager(getTypicalWaddle(), new UserPrefs()); + expectedModel = new ModelManager(model.getWaddle(), new UserPrefs()); } @Test @@ -33,7 +33,7 @@ public void execute_listIsNotFiltered_showsSameList() { @Test public void execute_listIsFiltered_showsEverything() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); + showItineraryAtIndex(model, INDEX_FIRST_ITINERARY); assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); } } diff --git a/src/test/java/seedu/waddle/logic/commands/PlanCommandTest.java b/src/test/java/seedu/waddle/logic/commands/PlanCommandTest.java new file mode 100644 index 00000000000..44f85baf92a --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/PlanCommandTest.java @@ -0,0 +1,174 @@ +package seedu.waddle.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.commons.core.Messages.MESSAGE_CONFLICTING_ITEMS; +import static seedu.waddle.commons.core.Messages.MESSAGE_ITEM_PAST_MIDNIGHT; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.waddle.testutil.Assert.assertThrows; + +import java.time.LocalTime; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.UserPrefs; +import seedu.waddle.model.Waddle; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.itinerary.DayNumber; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.testutil.ItemBuilder; +import seedu.waddle.testutil.TypicalItineraries; + +public class PlanCommandTest { + private Model model; + private Itinerary validItinerary; + private String timeConflictMessage; + + @BeforeEach + private void setUp() { + this.model = getModelStub(); + + // select validItinerary + StageManager.getInstance().setWishStage(validItinerary); + } + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new PlanCommand(null, null, null)); + } + + @Test + public void execute_startAtMidnight_planSuccessful() { + PlanCommand planCommand = + new PlanCommand(Index.fromZeroBased(1), new DayNumber("1"), LocalTime.parse("00:00")); + Model expectedModel = getModelStub(); + try { + expectedModel.getFilteredItineraryList().get(0) + .planItem(Index.fromZeroBased(1), new DayNumber("1"), LocalTime.parse("00:00")); + } catch (CommandException e) { + assert false : "Planning failed"; + } + + assertCommandSuccess(planCommand, model, + String.format(PlanCommand.MESSAGE_SUCCESS, "item 3"), expectedModel); + } + + @Test + public void execute_endAtMidnight_planSuccessful() { + PlanCommand planCommand = + new PlanCommand(Index.fromZeroBased(1), new DayNumber("1"), LocalTime.parse("23:00")); + Model expectedModel = getModelStub(); + try { + expectedModel.getFilteredItineraryList().get(0) + .planItem(Index.fromZeroBased(1), new DayNumber("1"), LocalTime.parse("23:00")); + } catch (CommandException e) { + assert false : "Planning failed"; + } + + assertCommandSuccess(planCommand, model, + String.format(PlanCommand.MESSAGE_SUCCESS, "item 3"), expectedModel); + } + + @Test + public void execute_startTimeConflict_throwsCommandException() throws Exception { + PlanCommand planCommand = + new PlanCommand(Index.fromZeroBased(1), new DayNumber("1"), LocalTime.parse("11:30")); + + assertCommandFailure(planCommand, model, timeConflictMessage); + } + + @Test + public void execute_endTimeConflict_throwsCommandException() throws Exception { + PlanCommand planCommand = + new PlanCommand(Index.fromZeroBased(0), new DayNumber("1"), LocalTime.parse("12:30")); + + assertCommandFailure(planCommand, model, timeConflictMessage); + } + + @Test + public void execute_sameTimeConflict_throwsCommandException() throws Exception { + PlanCommand planCommand = + new PlanCommand(Index.fromZeroBased(0), new DayNumber("1"), LocalTime.parse("12:00")); + + assertCommandFailure(planCommand, model, timeConflictMessage); + } + + @Test + public void execute_overlapTimeConflict_throwsCommandException() throws Exception { + PlanCommand planCommand = + new PlanCommand(Index.fromZeroBased(0), new DayNumber("1"), LocalTime.parse("11:30")); + + assertCommandFailure(planCommand, model, timeConflictMessage); + } + + @Test + public void execute_pastMidnightTimeConflict_throwsCommandException() throws Exception { + PlanCommand planCommand = + new PlanCommand(Index.fromZeroBased(0), new DayNumber("1"), LocalTime.parse("23:30")); + String expectedMessage = String.format(MESSAGE_ITEM_PAST_MIDNIGHT, + "item 2"); + + assertCommandFailure(planCommand, model, expectedMessage); + } + + @Test + public void equals() { + PlanCommand planCommand1 = new PlanCommand(Index.fromZeroBased(0), new DayNumber("1"), LocalTime.NOON); + PlanCommand planCommand2 = new PlanCommand(Index.fromZeroBased(1), new DayNumber("2"), LocalTime.MIDNIGHT); + + // same object -> returns true + assertTrue(planCommand1.equals(planCommand1)); + + // same values -> returns true + PlanCommand planCommand1Copy = new PlanCommand(Index.fromZeroBased(0), new DayNumber("1"), LocalTime.NOON); + assertTrue(planCommand1.equals(planCommand1Copy)); + + // different types -> returns false + assertFalse(planCommand1.equals(1)); + + // null -> returns false + assertFalse(planCommand1.equals(null)); + + // different inputs -> returns false + assertFalse(planCommand1.equals(planCommand2)); + } + + private Model getModelStub() { + // model set up + Model model = new ModelManager(new Waddle(), new UserPrefs()); + + // itinerary set up + validItinerary = TypicalItineraries.getAutumn(); + Item validItem1 = new ItemBuilder().withDesc("item 1").build(); + Item validItem2 = new ItemBuilder().withDesc("item 2").withDuration("120").build(); + Item validItem3 = new ItemBuilder().withDesc("item 3").build(); + Item validItem4 = new ItemBuilder().withDesc("item 4").build(); + validItinerary.addItem(validItem1); + validItinerary.addItem(validItem2); + validItinerary.addItem(validItem3); + validItinerary.addItem(validItem4); + + // plan validItem1 to day 1 noon + try { + validItinerary.planItem(Index.fromZeroBased(0), new DayNumber("1"), LocalTime.NOON); + } catch (CommandException e) { + assert false : "Failed to set up valid itinerary"; + } + + // time conflict message set up + StringBuilder conflicts = new StringBuilder(); + conflicts.append(" ").append(validItem1.getDescription()).append(": ") + .append(validItem1.getStartTime()).append(" - ").append(validItem1.getEndTime()).append("\n"); + timeConflictMessage = String.format(MESSAGE_CONFLICTING_ITEMS, conflicts); + + model.addItinerary(validItinerary); + return model; + } +} diff --git a/src/test/java/seedu/waddle/logic/commands/SelectCommandTest.java b/src/test/java/seedu/waddle/logic/commands/SelectCommandTest.java new file mode 100644 index 00000000000..2c408bacc2a --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/SelectCommandTest.java @@ -0,0 +1,118 @@ +package seedu.waddle.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.waddle.logic.commands.CommandTestUtil.showItineraryAtIndex; +import static seedu.waddle.testutil.TypicalIndexes.INDEX_FIRST_ITINERARY; +import static seedu.waddle.testutil.TypicalIndexes.INDEX_SECOND_ITINERARY; +import static seedu.waddle.testutil.TypicalItineraries.getTypicalWaddle; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.core.Messages; +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.StageManager; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.UserPrefs; +import seedu.waddle.model.itinerary.Itinerary; + + +public class SelectCommandTest { + private Model model = new ModelManager(getTypicalWaddle(), new UserPrefs()); + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new SelectCommand(null)); + } + + @Test + public void execute_validIndexUnfilteredList_success() { + StageManager.getInstance().setHomeStage(); + Itinerary itineraryToSelect = model.getFilteredItineraryList().get(INDEX_FIRST_ITINERARY.getZeroBased()); + SelectCommand selectCommand = new SelectCommand(INDEX_FIRST_ITINERARY); + + String expectedMessage = String.format(SelectCommand.MESSAGE_SELECT_ITINERARY_SUCCESS, + itineraryToSelect.getDescription()); + + ModelManager expectedModel = new ModelManager(model.getWaddle(), new UserPrefs()); + StageManager.getInstance().setWishStage(itineraryToSelect); + + assertCommandSuccess(selectCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredItineraryList().size() + 1); + SelectCommand selectCommand = new SelectCommand(outOfBoundIndex); + + assertCommandFailure(selectCommand, model, Messages.MESSAGE_INVALID_ITINERARY_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + StageManager.getInstance().setHomeStage(); + showItineraryAtIndex(model, INDEX_FIRST_ITINERARY); + + Itinerary itineraryToSelect = model.getFilteredItineraryList().get(INDEX_FIRST_ITINERARY.getZeroBased()); + SelectCommand selectCommand = new SelectCommand(INDEX_FIRST_ITINERARY); + + String expectedMessage = String.format(SelectCommand.MESSAGE_SELECT_ITINERARY_SUCCESS, + itineraryToSelect.getDescription()); + + Model expectedModel = new ModelManager(model.getWaddle(), new UserPrefs()); + showFirstItinerary(expectedModel); + StageManager.getInstance().setWishStage(itineraryToSelect); + + assertCommandSuccess(selectCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showItineraryAtIndex(model, INDEX_FIRST_ITINERARY); + + Index outOfBoundIndex = INDEX_SECOND_ITINERARY; + // ensures that outOfBoundIndex is still in bounds of Waddle list + assertTrue(outOfBoundIndex.getZeroBased() < model.getWaddle().getItineraryList().size()); + + SelectCommand selectCommand = new SelectCommand(outOfBoundIndex); + + assertCommandFailure(selectCommand, model, Messages.MESSAGE_INVALID_ITINERARY_DISPLAYED_INDEX); + } + + @Test + public void equals() { + SelectCommand selectFirstCommand = new SelectCommand(INDEX_FIRST_ITINERARY); + SelectCommand selectSecondCommand = new SelectCommand(INDEX_SECOND_ITINERARY); + + // same object -> returns true + assertTrue(selectFirstCommand.equals(selectFirstCommand)); + + // same values -> returns true + SelectCommand selectFirstCommandCopy = new SelectCommand(INDEX_FIRST_ITINERARY); + assertTrue(selectFirstCommand.equals(selectFirstCommandCopy)); + + // different types -> returns false + assertFalse(selectFirstCommand.equals(1)); + + // null -> returns false + assertFalse(selectFirstCommand.equals(null)); + + // different index -> returns false + assertFalse(selectFirstCommand.equals(selectSecondCommand)); + } + + /** + * Updates {@code model}'s filtered list to show first itinerary in getTypicalWaddle(). + */ + private void showFirstItinerary(Model model) { + model.updateFilteredItineraryList(itinerary -> + itinerary.equals(getTypicalWaddle().getItineraryList().get(INDEX_FIRST_ITINERARY.getZeroBased()))); + + assertEquals(1, model.getFilteredItineraryList().size()); + } +} diff --git a/src/test/java/seedu/waddle/logic/commands/UnplanCommandTest.java b/src/test/java/seedu/waddle/logic/commands/UnplanCommandTest.java new file mode 100644 index 00000000000..dba29c40826 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/UnplanCommandTest.java @@ -0,0 +1,77 @@ +package seedu.waddle.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.waddle.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.waddle.testutil.TypicalIndexes.INDEX_SECOND_ITINERARY; +import static seedu.waddle.testutil.TypicalItineraries.getTypicalWaddle; +import static seedu.waddle.testutil.TypicalMultiIndexes.MULTI_INDEX_FIRST_DAY_FIRST_ITEM; +import static seedu.waddle.testutil.TypicalMultiIndexes.MULTI_INDEX_FIRST_DAY_SECOND_ITEM; +import static seedu.waddle.testutil.TypicalMultiIndexes.MULTI_INDEX_FIRST_UNSCHEDULED_ITEM; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.logic.StageManager; +import seedu.waddle.model.Model; +import seedu.waddle.model.ModelManager; +import seedu.waddle.model.UserPrefs; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.itinerary.Itinerary; + +public class UnplanCommandTest { + + private final Model model = new ModelManager(getTypicalWaddle(), new UserPrefs()); + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new UnplanCommand(null)); + } + + @Test + public void execute_validMultiIndexDayList_success() { + Itinerary validItinerary = getTypicalWaddle().getItineraryList().get(INDEX_SECOND_ITINERARY.getZeroBased()); + UnplanCommand unplanCommand = new UnplanCommand(MULTI_INDEX_FIRST_DAY_FIRST_ITEM); + + Model expectedModel = new ModelManager(model.getWaddle(), new UserPrefs()); + + Item validItem = expectedModel.getFilteredItineraryList() + .get(INDEX_SECOND_ITINERARY.getZeroBased()) + .unplanItem(MULTI_INDEX_FIRST_DAY_FIRST_ITEM); + + String expectedMessage = String.format(UnplanCommand.MESSAGE_SUCCESS, validItem.getDescription()); + StageManager.getInstance().setWishStage(validItinerary); + + assertCommandSuccess(unplanCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidMultiIndexDayList_throwsCommandException() { + UnplanCommand unplanCommand = new UnplanCommand(MULTI_INDEX_FIRST_UNSCHEDULED_ITEM); + + assertCommandFailure(unplanCommand, model, UnplanCommand.MESSAGE_INVALID_INDEX_NUMBER); + } + + @Test + public void equals() { + UnplanCommand unplanFirstCommand = new UnplanCommand(MULTI_INDEX_FIRST_DAY_FIRST_ITEM); + UnplanCommand unplanSecondCommand = new UnplanCommand(MULTI_INDEX_FIRST_DAY_SECOND_ITEM); + + // same object -> returns true + assertTrue(unplanFirstCommand.equals(unplanFirstCommand)); + + // same values -> returns true + UnplanCommand unplanFirstCommandCopy = new UnplanCommand(MULTI_INDEX_FIRST_DAY_FIRST_ITEM); + assertTrue(unplanFirstCommand.equals(unplanFirstCommandCopy)); + + // different types -> returns false + assertFalse(unplanFirstCommand.equals(1)); + + // null -> returns false + assertFalse(unplanFirstCommand.equals(null)); + + // different multi index -> returns false + assertFalse(unplanFirstCommand.equals(unplanSecondCommand)); + } +} diff --git a/src/test/java/seedu/waddle/logic/commands/exceptions/CommandException.java b/src/test/java/seedu/waddle/logic/commands/exceptions/CommandException.java new file mode 100644 index 00000000000..568813e0f4d --- /dev/null +++ b/src/test/java/seedu/waddle/logic/commands/exceptions/CommandException.java @@ -0,0 +1,17 @@ +package seedu.waddle.logic.commands.exceptions; + +/** + * Represents an error which occurs during execution of a {@link Command}. + */ +public class CommandException extends Exception { + public CommandException(String message) { + super(message); + } + + /** + * Constructs a new {@code CommandException} with the specified detail {@code message} and {@code cause}. + */ + public CommandException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/test/java/seedu/waddle/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/waddle/logic/parser/AddCommandParserTest.java new file mode 100644 index 00000000000..0bdbe01fc11 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/parser/AddCommandParserTest.java @@ -0,0 +1,176 @@ +package seedu.waddle.logic.parser; + +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.commands.CommandTestUtil.BUDGET_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.BUDGET_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.COUNTRY_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.COUNTRY_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.DURATION_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.DURATION_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_COUNTRY_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_DESC_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_DURATION_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_PEOPLE_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_START_DATE_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.ITINERARY_DESC_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.ITINERARY_DESC_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.PEOPLE_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.PEOPLE_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; +import static seedu.waddle.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; +import static seedu.waddle.logic.commands.CommandTestUtil.START_DATE_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_BUDGET_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COUNTRY_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITINERARY_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PEOPLE_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_START_DATE_WINTER; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.waddle.testutil.TypicalItineraries.WINTER; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.logic.commands.AddCommand; +import seedu.waddle.model.itinerary.Country; +import seedu.waddle.model.itinerary.Date; +import seedu.waddle.model.itinerary.Description; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.model.itinerary.ItineraryDuration; +import seedu.waddle.model.itinerary.People; +import seedu.waddle.testutil.ItineraryBuilder; + +public class AddCommandParserTest { + private AddCommandParser parser = new AddCommandParser(); + + @Test + public void parse_allFieldsPresent_success() { + Itinerary expectedItinerary = new ItineraryBuilder(WINTER).build(); + + // whitespace only preamble + assertParseSuccess(parser, PREAMBLE_WHITESPACE + ITINERARY_DESC_DESC_WINTER + COUNTRY_DESC_WINTER + + START_DATE_DESC_WINTER + DURATION_DESC_WINTER + PEOPLE_DESC_WINTER + BUDGET_DESC_WINTER, + new AddCommand(expectedItinerary)); + + // multiple descriptions - last description accepted + assertParseSuccess(parser, ITINERARY_DESC_DESC_SUMMER + ITINERARY_DESC_DESC_WINTER + COUNTRY_DESC_WINTER + + START_DATE_DESC_WINTER + DURATION_DESC_WINTER + PEOPLE_DESC_WINTER + BUDGET_DESC_WINTER, + new AddCommand(expectedItinerary)); + + // multiple countries - last country accepted + assertParseSuccess(parser, ITINERARY_DESC_DESC_WINTER + COUNTRY_DESC_SUMMER + COUNTRY_DESC_WINTER + + START_DATE_DESC_WINTER + DURATION_DESC_WINTER + PEOPLE_DESC_WINTER + BUDGET_DESC_WINTER, + new AddCommand(expectedItinerary)); + + // multiple people - last people accepted + assertParseSuccess(parser, ITINERARY_DESC_DESC_WINTER + PEOPLE_DESC_SUMMER + COUNTRY_DESC_WINTER + + START_DATE_DESC_WINTER + DURATION_DESC_WINTER + PEOPLE_DESC_WINTER + BUDGET_DESC_WINTER, + new AddCommand(expectedItinerary)); + + // multiple duration - last duration accepted + assertParseSuccess(parser, ITINERARY_DESC_DESC_WINTER + COUNTRY_DESC_WINTER + START_DATE_DESC_WINTER + + DURATION_DESC_SUMMER + DURATION_DESC_WINTER + PEOPLE_DESC_WINTER + BUDGET_DESC_WINTER, + new AddCommand(expectedItinerary)); + + // multiple budget - last budget accepted + assertParseSuccess(parser, ITINERARY_DESC_DESC_WINTER + COUNTRY_DESC_WINTER + START_DATE_DESC_WINTER + + DURATION_DESC_WINTER + PEOPLE_DESC_WINTER + BUDGET_DESC_SUMMER + BUDGET_DESC_WINTER, + new AddCommand(expectedItinerary)); + } + + @Test + public void parse_optionalFieldsMissing_success() { + // country field missing + Itinerary expectedItinerary = new ItineraryBuilder().withDescription(VALID_ITINERARY_DESC_WINTER) + .withCountry("default").withStartDate(VALID_START_DATE_WINTER) + .withDuration(VALID_DURATION_WINTER).withPeople(VALID_PEOPLE_WINTER) + .withBudget(VALID_BUDGET_WINTER).build(); + + assertParseSuccess(parser, ITINERARY_DESC_DESC_WINTER + START_DATE_DESC_WINTER + + DURATION_DESC_WINTER + PEOPLE_DESC_WINTER + BUDGET_DESC_WINTER, + new AddCommand(expectedItinerary)); + + // people field missing + expectedItinerary = new ItineraryBuilder().withDescription(VALID_ITINERARY_DESC_WINTER) + .withCountry(VALID_COUNTRY_WINTER).withStartDate(VALID_START_DATE_WINTER) + .withDuration(VALID_DURATION_WINTER).withPeople("1") + .withBudget(VALID_BUDGET_WINTER).build(); + + assertParseSuccess(parser, ITINERARY_DESC_DESC_WINTER + START_DATE_DESC_WINTER + + DURATION_DESC_WINTER + COUNTRY_DESC_WINTER + BUDGET_DESC_WINTER, + new AddCommand(expectedItinerary)); + + // budget field missing + expectedItinerary = new ItineraryBuilder().withDescription(VALID_ITINERARY_DESC_WINTER) + .withCountry(VALID_COUNTRY_WINTER).withStartDate(VALID_START_DATE_WINTER) + .withDuration(VALID_DURATION_WINTER).withPeople(VALID_PEOPLE_WINTER) + .withBudget("0").build(); + + assertParseSuccess(parser, ITINERARY_DESC_DESC_WINTER + START_DATE_DESC_WINTER + + DURATION_DESC_WINTER + COUNTRY_DESC_WINTER + PEOPLE_DESC_WINTER, + new AddCommand(expectedItinerary)); + + // all optional fields missing + expectedItinerary = new ItineraryBuilder().withDescription(VALID_ITINERARY_DESC_WINTER) + .withCountry("default").withStartDate(VALID_START_DATE_WINTER) + .withDuration(VALID_DURATION_WINTER).withPeople("1") + .withBudget("0").build(); + + assertParseSuccess(parser, ITINERARY_DESC_DESC_WINTER + START_DATE_DESC_WINTER + + DURATION_DESC_WINTER, new AddCommand(expectedItinerary)); + } + + + @Test + public void parse_compulsoryFieldMissing_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); + + // missing description prefix + assertParseFailure(parser, VALID_ITINERARY_DESC_WINTER + START_DATE_DESC_WINTER + + DURATION_DESC_WINTER, expectedMessage); + + // missing start date prefix + assertParseFailure(parser, ITINERARY_DESC_DESC_WINTER + VALID_START_DATE_WINTER + + DURATION_DESC_WINTER, expectedMessage); + + // missing duration prefix + assertParseFailure(parser, ITINERARY_DESC_DESC_WINTER + START_DATE_DESC_WINTER + + VALID_DURATION_WINTER, expectedMessage); + + // all prefixes missing + assertParseFailure(parser, VALID_ITINERARY_DESC_WINTER + VALID_START_DATE_WINTER + + VALID_DURATION_WINTER, expectedMessage); + } + + @Test + public void parse_invalidValue_failure() { + // invalid description + assertParseFailure(parser, INVALID_DESC_DESC + COUNTRY_DESC_WINTER + START_DATE_DESC_WINTER + + DURATION_DESC_WINTER + PEOPLE_DESC_WINTER, Description.MESSAGE_CONSTRAINTS); + + // invalid country + assertParseFailure(parser, ITINERARY_DESC_DESC_WINTER + INVALID_COUNTRY_DESC + START_DATE_DESC_WINTER + + DURATION_DESC_WINTER + PEOPLE_DESC_WINTER, Country.MESSAGE_CONSTRAINTS); + + // invalid start date + assertParseFailure(parser, ITINERARY_DESC_DESC_WINTER + COUNTRY_DESC_WINTER + INVALID_START_DATE_DESC + + DURATION_DESC_WINTER + PEOPLE_DESC_WINTER, Date.MESSAGE_CONSTRAINTS); + + // invalid duration + assertParseFailure(parser, ITINERARY_DESC_DESC_WINTER + COUNTRY_DESC_WINTER + START_DATE_DESC_WINTER + + INVALID_DURATION_DESC + PEOPLE_DESC_WINTER, ItineraryDuration.MESSAGE_CONSTRAINTS); + + // invalid people + assertParseFailure(parser, ITINERARY_DESC_DESC_WINTER + COUNTRY_DESC_WINTER + START_DATE_DESC_WINTER + + DURATION_DESC_WINTER + INVALID_PEOPLE_DESC, People.MESSAGE_CONSTRAINTS); + + // two invalid values, only first invalid value reported + assertParseFailure(parser, INVALID_DESC_DESC + COUNTRY_DESC_WINTER + START_DATE_DESC_WINTER + + INVALID_DURATION_DESC + PEOPLE_DESC_WINTER, Description.MESSAGE_CONSTRAINTS); + + // non-empty preamble + assertParseFailure(parser, PREAMBLE_NON_EMPTY + ITINERARY_DESC_DESC_WINTER + COUNTRY_DESC_WINTER + + START_DATE_DESC_WINTER + DURATION_DESC_WINTER + PEOPLE_DESC_WINTER, + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/waddle/logic/parser/AddItemCommandParserTest.java b/src/test/java/seedu/waddle/logic/parser/AddItemCommandParserTest.java new file mode 100644 index 00000000000..bed66143d6f --- /dev/null +++ b/src/test/java/seedu/waddle/logic/parser/AddItemCommandParserTest.java @@ -0,0 +1,117 @@ +package seedu.waddle.logic.parser; + +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.commands.CommandTestUtil.COST_DESC_ART; +import static seedu.waddle.logic.commands.CommandTestUtil.COST_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_COST_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_ITEM_DESC_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_ITEM_DURATION_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_PRIORITY_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.ITEM_DESC_DESC_ART; +import static seedu.waddle.logic.commands.CommandTestUtil.ITEM_DESC_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.ITEM_DURATION_DESC_ART; +import static seedu.waddle.logic.commands.CommandTestUtil.ITEM_DURATION_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; +import static seedu.waddle.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; +import static seedu.waddle.logic.commands.CommandTestUtil.PRIORITY_DESC_ART; +import static seedu.waddle.logic.commands.CommandTestUtil.PRIORITY_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COST_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITEM_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PRIORITY_SHOPPING; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.waddle.testutil.TypicalItems.getShopping; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.logic.commands.AddItemCommand; +import seedu.waddle.model.item.Cost; +import seedu.waddle.model.item.Duration; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.item.Priority; +import seedu.waddle.model.itinerary.Description; + +public class AddItemCommandParserTest { + private final AddItemCommandParser parser = new AddItemCommandParser(); + + @Test + public void parse_allFieldsPresent_success() { + Item expectedItem = getShopping(); + + // whitespace only preamble + assertParseSuccess(parser, PREAMBLE_WHITESPACE + ITEM_DESC_DESC_SHOPPING + ITEM_DURATION_DESC_SHOPPING + + COST_DESC_SHOPPING + PRIORITY_DESC_SHOPPING, new AddItemCommand(expectedItem)); + + // multiple desc - last desc accepted + assertParseSuccess(parser, PREAMBLE_WHITESPACE + ITEM_DESC_DESC_ART + ITEM_DESC_DESC_SHOPPING + + ITEM_DURATION_DESC_SHOPPING + COST_DESC_SHOPPING + + PRIORITY_DESC_SHOPPING, new AddItemCommand(expectedItem)); + + // multiple duration - last duration accepted + assertParseSuccess(parser, PREAMBLE_WHITESPACE + ITEM_DESC_DESC_SHOPPING + ITEM_DURATION_DESC_ART + + ITEM_DURATION_DESC_SHOPPING + COST_DESC_SHOPPING + + PRIORITY_DESC_SHOPPING, new AddItemCommand(expectedItem)); + + // multiple cost - last cost accepted + assertParseSuccess(parser, PREAMBLE_WHITESPACE + ITEM_DESC_DESC_SHOPPING + ITEM_DURATION_DESC_SHOPPING + + COST_DESC_ART + COST_DESC_SHOPPING + PRIORITY_DESC_SHOPPING, new AddItemCommand(expectedItem)); + + // multiple priority - last priority accepted + assertParseSuccess(parser, PREAMBLE_WHITESPACE + ITEM_DESC_DESC_SHOPPING + ITEM_DURATION_DESC_SHOPPING + + COST_DESC_SHOPPING + PRIORITY_DESC_ART + PRIORITY_DESC_SHOPPING, new AddItemCommand(expectedItem)); + } + + @Test + public void parse_compulsoryFieldMissing_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddItemCommand.MESSAGE_USAGE); + + // missing desc prefix + assertParseFailure(parser, PREAMBLE_WHITESPACE + VALID_ITEM_DESC_SHOPPING + ITEM_DURATION_DESC_SHOPPING + + COST_DESC_SHOPPING + PRIORITY_DESC_SHOPPING, expectedMessage); + + // missing duration prefix + assertParseFailure(parser, PREAMBLE_WHITESPACE + ITEM_DESC_DESC_SHOPPING + VALID_DURATION_SHOPPING + + COST_DESC_SHOPPING + PRIORITY_DESC_SHOPPING, expectedMessage); + + // missing cost prefix + assertParseFailure(parser, PREAMBLE_WHITESPACE + VALID_ITEM_DESC_SHOPPING + ITEM_DURATION_DESC_SHOPPING + + VALID_COST_SHOPPING + PRIORITY_DESC_SHOPPING, expectedMessage); + + // missing priority prefix + assertParseFailure(parser, PREAMBLE_WHITESPACE + VALID_ITEM_DESC_SHOPPING + ITEM_DURATION_DESC_SHOPPING + + COST_DESC_SHOPPING + VALID_PRIORITY_SHOPPING, expectedMessage); + + // all prefixes missing prefix + assertParseFailure(parser, PREAMBLE_WHITESPACE + VALID_ITEM_DESC_SHOPPING + VALID_DURATION_SHOPPING + + VALID_COST_SHOPPING + VALID_PRIORITY_SHOPPING, expectedMessage); + } + + @Test + public void parse_invalidValue_failure() { + // invalid desc + assertParseFailure(parser, INVALID_ITEM_DESC_DESC + ITEM_DURATION_DESC_SHOPPING + COST_DESC_SHOPPING + + PRIORITY_DESC_SHOPPING, Description.MESSAGE_CONSTRAINTS); + + // invalid duration + assertParseFailure(parser, ITEM_DESC_DESC_SHOPPING + INVALID_ITEM_DURATION_DESC + COST_DESC_SHOPPING + + PRIORITY_DESC_SHOPPING, Duration.MESSAGE_CONSTRAINTS); + + // invalid cost + assertParseFailure(parser, ITEM_DESC_DESC_SHOPPING + ITEM_DURATION_DESC_SHOPPING + INVALID_COST_DESC + + PRIORITY_DESC_SHOPPING, Cost.MESSAGE_CONSTRAINTS); + + // invalid priority + assertParseFailure(parser, ITEM_DESC_DESC_SHOPPING + ITEM_DURATION_DESC_SHOPPING + COST_DESC_SHOPPING + + INVALID_PRIORITY_DESC, Priority.MESSAGE_CONSTRAINTS); + + // two invalid values, only first invalid value reported + assertParseFailure(parser, INVALID_ITEM_DESC_DESC + ITEM_DURATION_DESC_SHOPPING + COST_DESC_SHOPPING + + INVALID_PRIORITY_DESC, Description.MESSAGE_CONSTRAINTS); + + // non-empty preamble + assertParseFailure(parser, PREAMBLE_NON_EMPTY + ITEM_DESC_DESC_SHOPPING + ITEM_DURATION_DESC_SHOPPING + + COST_DESC_SHOPPING + PRIORITY_DESC_SHOPPING, + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddItemCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java b/src/test/java/seedu/waddle/logic/parser/ArgumentTokenizerTest.java similarity index 99% rename from src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java rename to src/test/java/seedu/waddle/logic/parser/ArgumentTokenizerTest.java index c97308935f5..987278221a2 100644 --- a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java +++ b/src/test/java/seedu/waddle/logic/parser/ArgumentTokenizerTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.waddle.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java b/src/test/java/seedu/waddle/logic/parser/CommandParserTestUtil.java similarity index 89% rename from src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java rename to src/test/java/seedu/waddle/logic/parser/CommandParserTestUtil.java index 9bf1ccf1cef..e7f239f528f 100644 --- a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java +++ b/src/test/java/seedu/waddle/logic/parser/CommandParserTestUtil.java @@ -1,9 +1,9 @@ -package seedu.address.logic.parser; +package seedu.waddle.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.waddle.logic.commands.Command; +import seedu.waddle.logic.parser.exceptions.ParseException; /** * Contains helper methods for testing command parsers. diff --git a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java b/src/test/java/seedu/waddle/logic/parser/DeleteCommandParserTest.java similarity index 67% rename from src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java rename to src/test/java/seedu/waddle/logic/parser/DeleteCommandParserTest.java index 27eaec84450..d46bf1d447b 100644 --- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java +++ b/src/test/java/seedu/waddle/logic/parser/DeleteCommandParserTest.java @@ -1,13 +1,13 @@ -package seedu.address.logic.parser; +package seedu.waddle.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.waddle.testutil.TypicalIndexes.INDEX_FIRST_ITINERARY; import org.junit.jupiter.api.Test; -import seedu.address.logic.commands.DeleteCommand; +import seedu.waddle.logic.commands.DeleteCommand; /** * As we are only doing white-box testing, our test cases do not cover path variations @@ -22,7 +22,7 @@ public class DeleteCommandParserTest { @Test public void parse_validArgs_returnsDeleteCommand() { - assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_PERSON)); + assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_ITINERARY)); } @Test diff --git a/src/test/java/seedu/waddle/logic/parser/DeleteItemCommandParserTest.java b/src/test/java/seedu/waddle/logic/parser/DeleteItemCommandParserTest.java new file mode 100644 index 00000000000..c01c8bfbf84 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/parser/DeleteItemCommandParserTest.java @@ -0,0 +1,40 @@ +package seedu.waddle.logic.parser; + +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.commons.core.index.MultiIndex; +import seedu.waddle.logic.commands.DeleteItemCommand; + +public class DeleteItemCommandParserTest { + private final DeleteItemCommandParser parser = new DeleteItemCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteItemCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "-1", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteItemCommand.MESSAGE_USAGE)); + + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteItemCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsSelectCommand() { + MultiIndex multiIndex = new MultiIndex(); + multiIndex.addIndex(Index.fromZeroBased(0)); + multiIndex.addIndex(Index.fromZeroBased(0)); + // no leading and trailing whitespaces + DeleteItemCommand expectedDeleteItemCommand = new DeleteItemCommand(multiIndex); + assertParseSuccess(parser, "1.1", expectedDeleteItemCommand); + } +} diff --git a/src/test/java/seedu/waddle/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/waddle/logic/parser/EditCommandParserTest.java new file mode 100644 index 00000000000..c9445676315 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/parser/EditCommandParserTest.java @@ -0,0 +1,214 @@ +package seedu.waddle.logic.parser; + +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.commands.CommandTestUtil.BUDGET_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.COUNTRY_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.COUNTRY_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.DURATION_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.DURATION_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_BUDGET_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_COUNTRY_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_DESC_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_DURATION_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_PEOPLE_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_START_DATE_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.ITINERARY_DESC_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.PEOPLE_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.PEOPLE_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.START_DATE_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.START_DATE_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_BUDGET_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COUNTRY_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COUNTRY_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITINERARY_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PEOPLE_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PEOPLE_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_START_DATE_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_START_DATE_WINTER; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_PEOPLE; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.waddle.testutil.TypicalIndexes.INDEX_FIRST_ITINERARY; +import static seedu.waddle.testutil.TypicalIndexes.INDEX_SECOND_ITINERARY; +import static seedu.waddle.testutil.TypicalIndexes.INDEX_THIRD_ITINERARY; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.commands.EditCommand; +import seedu.waddle.logic.commands.EditCommand.EditItineraryDescriptor; +import seedu.waddle.model.itinerary.Budget; +import seedu.waddle.model.itinerary.Country; +import seedu.waddle.model.itinerary.Date; +import seedu.waddle.model.itinerary.Description; +import seedu.waddle.model.itinerary.ItineraryDuration; +import seedu.waddle.model.itinerary.People; +import seedu.waddle.testutil.EditItineraryDescriptorBuilder; + +public class EditCommandParserTest { + + private static final String TAG_EMPTY = " " + PREFIX_PEOPLE; + + private static final String MESSAGE_INVALID_FORMAT = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE); + + private EditCommandParser parser = new EditCommandParser(); + + @Test + public void parse_missingParts_failure() { + // no index specified + assertParseFailure(parser, VALID_ITINERARY_DESC_SUMMER, MESSAGE_INVALID_FORMAT); + + // no field specified + assertParseFailure(parser, "1", EditCommand.MESSAGE_NOT_EDITED); + + // no index and no field specified + assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidPreamble_failure() { + // negative index + assertParseFailure(parser, "-5" + ITINERARY_DESC_DESC_SUMMER, MESSAGE_INVALID_FORMAT); + + // zero index + assertParseFailure(parser, "0" + ITINERARY_DESC_DESC_SUMMER, MESSAGE_INVALID_FORMAT); + + // invalid arguments being parsed as preamble + assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT); + + // invalid prefix being parsed as preamble + assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidValue_failure() { + assertParseFailure(parser, "1" + INVALID_DESC_DESC, Description.MESSAGE_CONSTRAINTS); // invalid name + assertParseFailure(parser, "1" + INVALID_COUNTRY_DESC, Country.MESSAGE_CONSTRAINTS); // invalid country + assertParseFailure(parser, "1" + INVALID_START_DATE_DESC, Date.MESSAGE_CONSTRAINTS); // invalid start date + //invalid duration + assertParseFailure(parser, "1" + INVALID_DURATION_DESC, ItineraryDuration.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + INVALID_PEOPLE_DESC, People.MESSAGE_CONSTRAINTS); // invalid people + assertParseFailure(parser, "1" + INVALID_BUDGET_DESC, Budget.MESSAGE_CONSTRAINTS); // invalid budget + + // invalid country followed by valid start date + assertParseFailure(parser, "1" + INVALID_COUNTRY_DESC + INVALID_START_DATE_DESC, + Country.MESSAGE_CONSTRAINTS); + + // valid country followed by invalid country. The test case for invalid country followed by valid country + // is tested at {@code parse_invalidValueFollowedByValidValue_success()} + assertParseFailure(parser, "1" + COUNTRY_DESC_WINTER + INVALID_COUNTRY_DESC, + Country.MESSAGE_CONSTRAINTS); + + // TODO + /* + // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Person} being edited, + // parsing it together with a valid tag results in error + assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); + */ + + // multiple invalid values, but only the first invalid value is captured + assertParseFailure(parser, "1" + INVALID_DESC_DESC + INVALID_START_DATE_DESC + + VALID_DURATION_SUMMER + VALID_COUNTRY_SUMMER + VALID_BUDGET_SUMMER, Description.MESSAGE_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + Index targetIndex = INDEX_SECOND_ITINERARY; + String userInput = targetIndex.getOneBased() + COUNTRY_DESC_SUMMER + PEOPLE_DESC_SUMMER + + START_DATE_DESC_SUMMER + DURATION_DESC_SUMMER + ITINERARY_DESC_DESC_SUMMER + BUDGET_DESC_SUMMER; + EditItineraryDescriptor descriptor = + new EditItineraryDescriptorBuilder().withDescription(VALID_ITINERARY_DESC_SUMMER) + .withCountry(VALID_COUNTRY_SUMMER).withStartDate(VALID_START_DATE_SUMMER) + .withDuration(VALID_DURATION_SUMMER).withPeople(VALID_PEOPLE_SUMMER) + .withBudget(VALID_BUDGET_SUMMER).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_someFieldsSpecified_success() { + Index targetIndex = INDEX_FIRST_ITINERARY; + String userInput = targetIndex.getOneBased() + COUNTRY_DESC_WINTER + START_DATE_DESC_SUMMER; + EditItineraryDescriptor descriptor = new EditItineraryDescriptorBuilder().withCountry(VALID_COUNTRY_WINTER) + .withStartDate(VALID_START_DATE_SUMMER).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_oneFieldSpecified_success() { + // name + Index targetIndex = INDEX_THIRD_ITINERARY; + String userInput = targetIndex.getOneBased() + ITINERARY_DESC_DESC_SUMMER; + EditItineraryDescriptor descriptor = new EditItineraryDescriptorBuilder() + .withDescription(VALID_ITINERARY_DESC_SUMMER).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // phone + userInput = targetIndex.getOneBased() + COUNTRY_DESC_SUMMER; + descriptor = new EditItineraryDescriptorBuilder().withCountry(VALID_COUNTRY_SUMMER).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // email + userInput = targetIndex.getOneBased() + START_DATE_DESC_SUMMER; + descriptor = new EditItineraryDescriptorBuilder().withStartDate(VALID_START_DATE_SUMMER).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // address + userInput = targetIndex.getOneBased() + DURATION_DESC_SUMMER; + descriptor = new EditItineraryDescriptorBuilder().withDuration(VALID_DURATION_SUMMER).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // tags + userInput = targetIndex.getOneBased() + PEOPLE_DESC_SUMMER; + descriptor = new EditItineraryDescriptorBuilder().withPeople(VALID_PEOPLE_SUMMER).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_multipleRepeatedFields_acceptsLast() { + Index targetIndex = INDEX_FIRST_ITINERARY; + String userInput = targetIndex.getOneBased() + COUNTRY_DESC_SUMMER + DURATION_DESC_SUMMER + + START_DATE_DESC_SUMMER + PEOPLE_DESC_SUMMER + COUNTRY_DESC_SUMMER + DURATION_DESC_SUMMER + + START_DATE_DESC_SUMMER + PEOPLE_DESC_SUMMER + COUNTRY_DESC_WINTER + DURATION_DESC_WINTER + + START_DATE_DESC_WINTER + PEOPLE_DESC_WINTER; + + EditItineraryDescriptor descriptor = new EditItineraryDescriptorBuilder().withCountry(VALID_COUNTRY_WINTER) + .withStartDate(VALID_START_DATE_WINTER).withDuration(VALID_DURATION_WINTER) + .withPeople(VALID_PEOPLE_WINTER).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_invalidValueFollowedByValidValue_success() { + // no other valid values specified + Index targetIndex = INDEX_FIRST_ITINERARY; + String userInput = targetIndex.getOneBased() + INVALID_COUNTRY_DESC + COUNTRY_DESC_WINTER; + EditItineraryDescriptor descriptor = new EditItineraryDescriptorBuilder() + .withCountry(VALID_COUNTRY_WINTER).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // other valid values specified + userInput = targetIndex.getOneBased() + START_DATE_DESC_WINTER + INVALID_COUNTRY_DESC + DURATION_DESC_WINTER + + COUNTRY_DESC_WINTER; + descriptor = new EditItineraryDescriptorBuilder().withCountry(VALID_COUNTRY_WINTER) + .withStartDate(VALID_START_DATE_WINTER).withDuration(VALID_DURATION_WINTER).build(); + expectedCommand = new EditCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } +} diff --git a/src/test/java/seedu/waddle/logic/parser/EditItemCommandParserTest.java b/src/test/java/seedu/waddle/logic/parser/EditItemCommandParserTest.java new file mode 100644 index 00000000000..510c15c47c0 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/parser/EditItemCommandParserTest.java @@ -0,0 +1,195 @@ +package seedu.waddle.logic.parser; + +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.commands.CommandTestUtil.COST_DESC_ART; +import static seedu.waddle.logic.commands.CommandTestUtil.COST_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.COST_DESC_SKINNY; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_COST_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_ITEM_DESC_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_ITEM_DURATION_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_PRIORITY_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.ITEM_DESC_DESC_ART; +import static seedu.waddle.logic.commands.CommandTestUtil.ITEM_DESC_DESC_BREAKFAST; +import static seedu.waddle.logic.commands.CommandTestUtil.ITEM_DESC_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.ITEM_DURATION_DESC_ART; +import static seedu.waddle.logic.commands.CommandTestUtil.ITEM_DURATION_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.PRIORITY_DESC_BEACH; +import static seedu.waddle.logic.commands.CommandTestUtil.PRIORITY_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.PRIORITY_DESC_TOUR; +import static seedu.waddle.logic.commands.CommandTestUtil.START_TIME_DESC_1715; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COST_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COST_SKINNY; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_ART; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITEM_DESC_ART; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITEM_DESC_BREAKFAST; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITEM_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PRIORITY_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PRIORITY_TOUR; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_START_TIME_1715; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.commons.core.index.MultiIndex; +import seedu.waddle.logic.commands.EditItemCommand; +import seedu.waddle.model.item.Cost; +import seedu.waddle.model.item.Duration; +import seedu.waddle.model.item.Priority; +import seedu.waddle.model.itinerary.Description; +import seedu.waddle.testutil.EditItemDescriptorBuilder; + +public class EditItemCommandParserTest { + private static final String MESSAGE_INVALID_FORMAT = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditItemCommand.MESSAGE_USAGE); + + private final EditItemCommandParser parser = new EditItemCommandParser(); + + @Test + public void parse_missingParts_failure() { + // no index specified + assertParseFailure(parser, VALID_ITEM_DESC_ART, MESSAGE_INVALID_FORMAT); + + // no field specified + assertParseFailure(parser, "1", EditItemCommand.MESSAGE_NOT_EDITED); + + // no index and no field specified + assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidPreamble_failure() { + // negative index + assertParseFailure(parser, "-5" + ITEM_DESC_DESC_ART, MESSAGE_INVALID_FORMAT); + + // zero index + assertParseFailure(parser, "0" + ITEM_DESC_DESC_ART, MESSAGE_INVALID_FORMAT); + + // invalid arguments being parsed as preamble + assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT); + + // invalid prefix being parsed as preamble + assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidValue_failure() { + assertParseFailure(parser, "1" + INVALID_ITEM_DESC_DESC, Description.MESSAGE_CONSTRAINTS); // invalid desc + assertParseFailure(parser, "1" + INVALID_ITEM_DURATION_DESC, Duration.MESSAGE_CONSTRAINTS); // invalid duration + assertParseFailure(parser, "1" + INVALID_COST_DESC, Cost.MESSAGE_CONSTRAINTS); // invalid cost + assertParseFailure(parser, "1" + INVALID_PRIORITY_DESC, Priority.MESSAGE_CONSTRAINTS); // invalid priority + + // invalid desc followed by valid duration + assertParseFailure(parser, "1" + INVALID_ITEM_DESC_DESC + ITEM_DURATION_DESC_SHOPPING, + Description.MESSAGE_CONSTRAINTS); + + // valid duration followed by invalid duration. + assertParseFailure(parser, "1" + ITEM_DURATION_DESC_SHOPPING + INVALID_ITEM_DURATION_DESC, + Duration.MESSAGE_CONSTRAINTS); + + // multiple invalid values, but only the first invalid value is captured + assertParseFailure(parser, "1" + INVALID_ITEM_DESC_DESC + INVALID_ITEM_DURATION_DESC + + INVALID_COST_DESC + INVALID_PRIORITY_DESC, Description.MESSAGE_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + MultiIndex targetIndex = new MultiIndex(); + targetIndex.addIndex(Index.fromZeroBased(0)); + String userInput = Index.fromZeroBased(0).getOneBased() + ITEM_DESC_DESC_SHOPPING + ITEM_DURATION_DESC_SHOPPING + + COST_DESC_SHOPPING + PRIORITY_DESC_SHOPPING; + EditItemCommand.EditItemDescriptor descriptor = new EditItemDescriptorBuilder() + .withDescription(VALID_ITEM_DESC_SHOPPING).withDuration(VALID_DURATION_SHOPPING) + .withCost(VALID_COST_SHOPPING).withPriority(VALID_PRIORITY_SHOPPING).build(); + EditItemCommand expectedCommand = new EditItemCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_someFieldsSpecified_success() { + MultiIndex targetIndex = new MultiIndex(); + targetIndex.addIndex(Index.fromZeroBased(0)); + String userInput = Index.fromZeroBased(0).getOneBased() + ITEM_DESC_DESC_SHOPPING + ITEM_DURATION_DESC_SHOPPING; + EditItemCommand.EditItemDescriptor descriptor = new EditItemDescriptorBuilder() + .withDescription(VALID_ITEM_DESC_SHOPPING).withDuration(VALID_DURATION_SHOPPING).build(); + EditItemCommand expectedCommand = new EditItemCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_oneFieldSpecified_success() { + MultiIndex targetIndex = new MultiIndex(); + targetIndex.addIndex(Index.fromZeroBased(0)); + + // desc + String userInput = Index.fromZeroBased(0).getOneBased() + ITEM_DESC_DESC_SHOPPING; + EditItemCommand.EditItemDescriptor descriptor = new EditItemDescriptorBuilder() + .withDescription(VALID_ITEM_DESC_SHOPPING).build(); + EditItemCommand expectedCommand = new EditItemCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // duration + userInput = Index.fromZeroBased(0).getOneBased() + ITEM_DURATION_DESC_SHOPPING; + descriptor = new EditItemDescriptorBuilder().withDuration(VALID_DURATION_SHOPPING).build(); + expectedCommand = new EditItemCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // cost + userInput = Index.fromZeroBased(0).getOneBased() + COST_DESC_SHOPPING; + descriptor = new EditItemDescriptorBuilder().withCost(VALID_COST_SHOPPING).build(); + expectedCommand = new EditItemCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // priority + userInput = Index.fromZeroBased(0).getOneBased() + PRIORITY_DESC_SHOPPING; + descriptor = new EditItemDescriptorBuilder().withPriority(VALID_PRIORITY_SHOPPING).build(); + expectedCommand = new EditItemCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // startTime + targetIndex.addIndex(Index.fromZeroBased(0)); + userInput = targetIndex + START_TIME_DESC_1715; + descriptor = new EditItemDescriptorBuilder().withStartTime(VALID_START_TIME_1715.toString()).build(); + expectedCommand = new EditItemCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_multipleRepeatedFields_acceptsLast() { + MultiIndex targetIndex = new MultiIndex(); + targetIndex.addIndex(Index.fromZeroBased(0)); + String userInput = Index.fromZeroBased(0).getOneBased() + ITEM_DESC_DESC_SHOPPING + ITEM_DURATION_DESC_SHOPPING + + ITEM_DURATION_DESC_ART + ITEM_DESC_DESC_BREAKFAST + COST_DESC_ART + PRIORITY_DESC_BEACH + + COST_DESC_SKINNY + PRIORITY_DESC_TOUR; + EditItemCommand.EditItemDescriptor descriptor = new EditItemDescriptorBuilder() + .withDescription(VALID_ITEM_DESC_BREAKFAST).withDuration(VALID_DURATION_ART) + .withCost(VALID_COST_SKINNY).withPriority(VALID_PRIORITY_TOUR).build(); + EditItemCommand expectedCommand = new EditItemCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_invalidValueFollowedByValidValue_success() { + MultiIndex targetIndex = new MultiIndex(); + targetIndex.addIndex(Index.fromZeroBased(0)); + String userInput = Index.fromZeroBased(0).getOneBased() + INVALID_ITEM_DESC_DESC + ITEM_DESC_DESC_SHOPPING; + EditItemCommand.EditItemDescriptor descriptor = new EditItemDescriptorBuilder() + .withDescription(VALID_ITEM_DESC_SHOPPING).build(); + EditItemCommand expectedCommand = new EditItemCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + + //priority + userInput = Index.fromZeroBased(0).getOneBased() + ITEM_DESC_DESC_SHOPPING + INVALID_ITEM_DURATION_DESC + + COST_DESC_SHOPPING + ITEM_DURATION_DESC_SHOPPING; + descriptor = new EditItemDescriptorBuilder().withDescription(VALID_ITEM_DESC_SHOPPING) + .withCost(VALID_COST_SHOPPING).withDuration(VALID_DURATION_SHOPPING).build(); + expectedCommand = new EditItemCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } +} diff --git a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java b/src/test/java/seedu/waddle/logic/parser/FindCommandParserTest.java similarity index 67% rename from src/test/java/seedu/address/logic/parser/FindCommandParserTest.java rename to src/test/java/seedu/waddle/logic/parser/FindCommandParserTest.java index 70f4f0e79c4..54527b8f2b4 100644 --- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java +++ b/src/test/java/seedu/waddle/logic/parser/FindCommandParserTest.java @@ -1,15 +1,15 @@ -package seedu.address.logic.parser; +package seedu.waddle.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseSuccess; import java.util.Arrays; import org.junit.jupiter.api.Test; -import seedu.address.logic.commands.FindCommand; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.waddle.logic.commands.FindCommand; +import seedu.waddle.model.itinerary.NameContainsKeywordsPredicate; public class FindCommandParserTest { diff --git a/src/test/java/seedu/waddle/logic/parser/ParserUtilTest.java b/src/test/java/seedu/waddle/logic/parser/ParserUtilTest.java new file mode 100644 index 00000000000..341f60eac48 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/parser/ParserUtilTest.java @@ -0,0 +1,166 @@ +package seedu.waddle.logic.parser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.waddle.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; +import static seedu.waddle.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.TypicalIndexes.INDEX_FIRST_ITINERARY; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.logic.parser.exceptions.ParseException; +import seedu.waddle.model.itinerary.Country; +import seedu.waddle.model.itinerary.Date; +import seedu.waddle.model.itinerary.Description; +import seedu.waddle.model.itinerary.People; + +public class ParserUtilTest { + private static final String INVALID_DESCRIPTION = "W**nter"; + private static final String INVALID_COUNTRY = "+651234"; + private static final String INVALID_START_DATE = "2022/03/15"; + private static final String INVALID_END_DATE = "202-04-15"; + private static final String INVALID_PEOPLE = "five"; + + private static final String VALID_DESCRIPTION = "Winter Trip"; + private static final String VALID_COUNTRY = "Finland"; + private static final String VALID_START_DATE = "2023-03-15"; + private static final String VALID_END_DATE = "2023-04-15"; + private static final String VALID_PEOPLE = "5"; + + private static final String WHITESPACE = " \t\r\n"; + + @Test + public void parseIndex_invalidInput_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseIndex("10 a")); + } + + @Test + public void parseIndex_outOfRangeInput_throwsParseException() { + assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, () + -> ParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1))); + } + + @Test + public void parseIndex_validInput_success() throws Exception { + // No whitespaces + assertEquals(INDEX_FIRST_ITINERARY, ParserUtil.parseIndex("1")); + + // Leading and trailing whitespaces + assertEquals(INDEX_FIRST_ITINERARY, ParserUtil.parseIndex(" 1 ")); + } + + @Test + public void parseName_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseDescription((String) null)); + } + + @Test + public void parseName_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseDescription(INVALID_DESCRIPTION)); + } + + @Test + public void parseName_validValueWithoutWhitespace_returnsName() throws Exception { + Description expectedName = new Description(VALID_DESCRIPTION); + assertEquals(expectedName, ParserUtil.parseDescription(VALID_DESCRIPTION)); + } + + @Test + public void parseName_validValueWithWhitespace_returnsTrimmedName() throws Exception { + String nameWithWhitespace = WHITESPACE + VALID_DESCRIPTION + WHITESPACE; + Description expectedName = new Description(VALID_DESCRIPTION); + assertEquals(expectedName, ParserUtil.parseDescription(nameWithWhitespace)); + } + + @Test + public void parseCountry_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseCountry((String) null)); + } + + @Test + public void parseCountry_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseCountry(INVALID_COUNTRY)); + } + + @Test + public void parseCountry_validValueWithoutWhitespace_returnsCountry() throws Exception { + Country expectedCountry = new Country(VALID_COUNTRY); + assertEquals(expectedCountry, ParserUtil.parseCountry(VALID_COUNTRY)); + } + + @Test + public void parseCountry_validValueWithWhitespace_returnsTrimmedCountry() throws Exception { + String phoneWithWhitespace = WHITESPACE + VALID_COUNTRY + WHITESPACE; + Country expectedCountry = new Country(VALID_COUNTRY); + assertEquals(expectedCountry, ParserUtil.parseCountry(phoneWithWhitespace)); + } + + @Test + public void parseStartDate_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseDate((String) null)); + } + + @Test + public void parseStartDate_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseDate(INVALID_START_DATE)); + } + + @Test + public void parseStartDate_validValueWithoutWhitespace_returnsStartDate() throws Exception { + Date expectedStartDate = new Date(VALID_START_DATE); + assertEquals(expectedStartDate, ParserUtil.parseDate(VALID_START_DATE)); + } + + @Test + public void parseStartDate_validValueWithWhitespace_returnsTrimmedStartDate() throws Exception { + String startDateWithWhitespace = WHITESPACE + VALID_START_DATE + WHITESPACE; + Date expectedStartDate = new Date(VALID_START_DATE); + assertEquals(expectedStartDate, ParserUtil.parseDate(startDateWithWhitespace)); + } + + @Test + public void parseEndDate_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseDate((String) null)); + } + + @Test + public void parseEndDate_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseDate(INVALID_END_DATE)); + } + + @Test + public void parseEndDate_validValueWithoutWhitespace_returnsEndDate() throws Exception { + Date expectedEndDate = new Date(VALID_END_DATE); + assertEquals(expectedEndDate, ParserUtil.parseDate(VALID_END_DATE)); + } + + @Test + public void parseEndDate_validValueWithWhitespace_returnsTrimmedEndDate() throws Exception { + String endDateWithWhitespace = WHITESPACE + VALID_END_DATE + WHITESPACE; + Date expectedEndDate = new Date(VALID_END_DATE); + assertEquals(expectedEndDate, ParserUtil.parseDate(endDateWithWhitespace)); + } + + + @Test + public void parsePeople_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parsePeople(null)); + } + + @Test + public void parsePeople_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parsePeople(INVALID_PEOPLE)); + } + + @Test + public void parsePeople_validValueWithoutWhitespace_returnsPeople() throws Exception { + People expectedPeople = new People(VALID_PEOPLE); + assertEquals(expectedPeople, ParserUtil.parsePeople(VALID_PEOPLE)); + } + + @Test + public void parsePeople_validValueWithWhitespace_returnsTrimmedPeople() throws Exception { + String tagWithWhitespace = WHITESPACE + VALID_PEOPLE + WHITESPACE; + People expectedPeople = new People(VALID_PEOPLE); + assertEquals(expectedPeople, ParserUtil.parsePeople(tagWithWhitespace)); + } +} diff --git a/src/test/java/seedu/waddle/logic/parser/PlanCommandParserTest.java b/src/test/java/seedu/waddle/logic/parser/PlanCommandParserTest.java new file mode 100644 index 00000000000..ddcfea5eb1a --- /dev/null +++ b/src/test/java/seedu/waddle/logic/parser/PlanCommandParserTest.java @@ -0,0 +1,70 @@ +package seedu.waddle.logic.parser; + +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.commands.CommandTestUtil.DAY_NUMBER_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_DAY_NUMBER_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.INVALID_START_TIME_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.START_TIME_DESC_1200; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DAY_NUMBER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_START_TIME_1200; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.commands.PlanCommand; +import seedu.waddle.model.item.StartTime; +import seedu.waddle.model.itinerary.DayNumber; + +public class PlanCommandParserTest { + private static final String MESSAGE_INVALID_FORMAT = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, PlanCommand.MESSAGE_USAGE); + + private final PlanCommandParser parser = new PlanCommandParser(); + + @Test + public void parse_missingParts_failure() { + // no index specified + assertParseFailure(parser, "12:00", MESSAGE_INVALID_FORMAT); + + // no field specified + assertParseFailure(parser, "1", MESSAGE_INVALID_FORMAT); + + // no index and no field specified + assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidPreamble_failure() { + // negative index + assertParseFailure(parser, "-5" + DAY_NUMBER_DESC + START_TIME_DESC_1200, MESSAGE_INVALID_FORMAT); + + // zero index + assertParseFailure(parser, "0" + DAY_NUMBER_DESC + START_TIME_DESC_1200, MESSAGE_INVALID_FORMAT); + + // invalid arguments being parsed as preamble + assertParseFailure(parser, "invalid" + DAY_NUMBER_DESC + START_TIME_DESC_1200, MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidValue_failure() { + assertParseFailure(parser, "1" + INVALID_DAY_NUMBER_DESC + START_TIME_DESC_1200, + DayNumber.MESSAGE_CONSTRAINTS); // invalid day number + assertParseFailure(parser, "1" + DAY_NUMBER_DESC + INVALID_START_TIME_DESC, + StartTime.MESSAGE_CONSTRAINTS); // invalid start time + + // multiple invalid values, but only the first invalid value is captured + assertParseFailure(parser, "1" + INVALID_DAY_NUMBER_DESC + INVALID_START_TIME_DESC, + DayNumber.MESSAGE_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + String userInput = Index.fromZeroBased(0).getOneBased() + DAY_NUMBER_DESC + START_TIME_DESC_1200; + PlanCommand expectedCommand = new PlanCommand(Index.fromZeroBased(0), + new DayNumber(VALID_DAY_NUMBER), VALID_START_TIME_1200); + + assertParseSuccess(parser, userInput, expectedCommand); + } +} diff --git a/src/test/java/seedu/waddle/logic/parser/SelectCommandParserTest.java b/src/test/java/seedu/waddle/logic/parser/SelectCommandParserTest.java new file mode 100644 index 00000000000..ded43931369 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/parser/SelectCommandParserTest.java @@ -0,0 +1,36 @@ +package seedu.waddle.logic.parser; + +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.commands.SelectCommand; + +public class SelectCommandParserTest { + private final SelectCommandParser parser = new SelectCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, SelectCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "-1", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, SelectCommand.MESSAGE_USAGE)); + + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, SelectCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsSelectCommand() { + // no leading and trailing whitespaces + SelectCommand expectedSelectCommand = new SelectCommand(Index.fromZeroBased(0)); + assertParseSuccess(parser, "1", expectedSelectCommand); + } +} diff --git a/src/test/java/seedu/waddle/logic/parser/UnplanCommandParserTest.java b/src/test/java/seedu/waddle/logic/parser/UnplanCommandParserTest.java new file mode 100644 index 00000000000..9c3c1cdaf14 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/parser/UnplanCommandParserTest.java @@ -0,0 +1,40 @@ +package seedu.waddle.logic.parser; + +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.waddle.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.commons.core.index.MultiIndex; +import seedu.waddle.logic.commands.UnplanCommand; + +public class UnplanCommandParserTest { + private final UnplanCommandParser parser = new UnplanCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnplanCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "-1", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnplanCommand.MESSAGE_USAGE)); + + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnplanCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsSelectCommand() { + MultiIndex multiIndex = new MultiIndex(); + multiIndex.addIndex(Index.fromZeroBased(0)); + multiIndex.addIndex(Index.fromZeroBased(0)); + // no leading and trailing whitespaces + UnplanCommand expectedUnplanCommand = new UnplanCommand(multiIndex); + assertParseSuccess(parser, "1.1", expectedUnplanCommand); + } +} diff --git a/src/test/java/seedu/waddle/logic/parser/WaddleParserTest.java b/src/test/java/seedu/waddle/logic/parser/WaddleParserTest.java new file mode 100644 index 00000000000..b29b7dd1b16 --- /dev/null +++ b/src/test/java/seedu/waddle/logic/parser/WaddleParserTest.java @@ -0,0 +1,334 @@ +package seedu.waddle.logic.parser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.waddle.commons.core.Messages.MESSAGE_UNAVAILABLE_COMMAND_HOME; +import static seedu.waddle.commons.core.Messages.MESSAGE_UNAVAILABLE_COMMAND_ITINERARY; +import static seedu.waddle.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.waddle.logic.commands.CommandTestUtil.COST_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.DAY_NUMBER_DESC; +import static seedu.waddle.logic.commands.CommandTestUtil.ITEM_DESC_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.ITEM_DURATION_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.PRIORITY_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.START_TIME_DESC_1200; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DAY_NUMBER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITEM_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_START_TIME_1200; +import static seedu.waddle.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.TypicalIndexes.INDEX_FIRST_ITINERARY; +import static seedu.waddle.testutil.TypicalItems.getShopping; +import static seedu.waddle.testutil.TypicalItineraries.getAutumn; +import static seedu.waddle.testutil.TypicalItineraries.getGraduation; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.commons.core.index.MultiIndex; +import seedu.waddle.logic.StageManager; +import seedu.waddle.logic.commands.AddCommand; +import seedu.waddle.logic.commands.AddItemCommand; +import seedu.waddle.logic.commands.ClearCommand; +import seedu.waddle.logic.commands.CopyCommand; +import seedu.waddle.logic.commands.DeleteCommand; +import seedu.waddle.logic.commands.DeleteItemCommand; +import seedu.waddle.logic.commands.EditCommand; +import seedu.waddle.logic.commands.EditCommand.EditItineraryDescriptor; +import seedu.waddle.logic.commands.EditItemCommand; +import seedu.waddle.logic.commands.ExitCommand; +import seedu.waddle.logic.commands.FindCommand; +import seedu.waddle.logic.commands.FreeCommand; +import seedu.waddle.logic.commands.HelpCommand; +import seedu.waddle.logic.commands.HomeCommand; +import seedu.waddle.logic.commands.ListCommand; +import seedu.waddle.logic.commands.PdfCommand; +import seedu.waddle.logic.commands.PlanCommand; +import seedu.waddle.logic.commands.UnplanCommand; +import seedu.waddle.logic.parser.exceptions.ParseException; +import seedu.waddle.model.itinerary.DayNumber; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.model.itinerary.NameContainsKeywordsPredicate; +import seedu.waddle.testutil.EditItemDescriptorBuilder; +import seedu.waddle.testutil.EditItineraryDescriptorBuilder; +import seedu.waddle.testutil.ItineraryBuilder; +import seedu.waddle.testutil.ItineraryUtil; + +public class WaddleParserTest { + + private final WaddleParser parser = new WaddleParser(); + + @Test + public void parseCommand_add() throws Exception { + // switch to home page + StageManager.getInstance().setHomeStage(); + + Itinerary itinerary = new ItineraryBuilder().build(); + AddCommand command = (AddCommand) parser.parseCommand(ItineraryUtil.getAddCommand(itinerary)); + assertEquals(new AddCommand(itinerary), command); + } + + @Test + public void parseCommand_clear() throws Exception { + // switch to home page + StageManager.getInstance().setHomeStage(); + + assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD) instanceof ClearCommand); + assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD + " 3") instanceof ClearCommand); + + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + assertThrows(ParseException.class, MESSAGE_UNAVAILABLE_COMMAND_ITINERARY, () + -> parser.parseCommand(ClearCommand.COMMAND_WORD)); + assertThrows(ParseException.class, MESSAGE_UNAVAILABLE_COMMAND_ITINERARY, () + -> parser.parseCommand(ClearCommand.COMMAND_WORD + " 3")); + } + + @Test + public void parseCommand_delete() throws Exception { + // switch to home page + StageManager.getInstance().setHomeStage(); + + DeleteCommand command = (DeleteCommand) parser.parseCommand( + DeleteCommand.COMMAND_WORD + " " + INDEX_FIRST_ITINERARY.getOneBased()); + assertEquals(new DeleteCommand(INDEX_FIRST_ITINERARY), command); + } + + @Test + public void parseCommand_edit() throws Exception { + // switch to home page + StageManager.getInstance().setHomeStage(); + + Itinerary itinerary = new ItineraryBuilder().build(); + EditItineraryDescriptor descriptor = new EditItineraryDescriptorBuilder(itinerary).build(); + EditCommand command = (EditCommand) parser.parseCommand(EditCommand.COMMAND_WORD + " " + + INDEX_FIRST_ITINERARY.getOneBased() + " " + + ItineraryUtil.getEditItineraryDescriptorDetails(descriptor)); + assertEquals(new EditCommand(INDEX_FIRST_ITINERARY, descriptor), command); + } + + @Test + public void parseCommand_exit() throws Exception { + // switch to home page + StageManager.getInstance().setHomeStage(); + assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD) instanceof ExitCommand); + assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD + " 3") instanceof ExitCommand); + + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD) instanceof ExitCommand); + assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD + " 3") instanceof ExitCommand); + } + + @Test + public void parseCommand_find() throws Exception { + // switch to home page + StageManager.getInstance().setHomeStage(); + + List keywords = Arrays.asList("foo", "bar", "baz"); + FindCommand command = (FindCommand) parser.parseCommand( + FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" "))); + assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command); + + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + assertThrows(ParseException.class, MESSAGE_UNAVAILABLE_COMMAND_ITINERARY, () + -> parser.parseCommand(FindCommand.COMMAND_WORD + + " " + keywords.stream().collect(Collectors.joining(" ")))); + } + + @Test + public void parseCommand_help() throws Exception { + // switch to home page + StageManager.getInstance().setHomeStage(); + assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand); + assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD + " 3") instanceof HelpCommand); + + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand); + assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD + " 3") instanceof HelpCommand); + } + + @Test + public void parseCommand_list() throws Exception { + // switch to home page + StageManager.getInstance().setHomeStage(); + + assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD) instanceof ListCommand); + assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand); + + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + assertThrows(ParseException.class, MESSAGE_UNAVAILABLE_COMMAND_ITINERARY, () + -> parser.parseCommand(ListCommand.COMMAND_WORD)); + assertThrows(ParseException.class, MESSAGE_UNAVAILABLE_COMMAND_ITINERARY, () + -> parser.parseCommand(ListCommand.COMMAND_WORD + " 3")); + } + + @Test + public void parseCommand_unrecognisedInput_throwsParseException() { + // switch to home page + StageManager.getInstance().setHomeStage(); + + assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), () + -> parser.parseCommand("")); + + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + + assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), () + -> parser.parseCommand("")); + } + + @Test + public void parseCommand_unknownCommand_throwsParseException() { + // switch to home page + StageManager.getInstance().setHomeStage(); + + assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () + -> parser.parseCommand("unknownCommand")); + + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + + assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () + -> parser.parseCommand("unknownCommand")); + } + + @Test + public void parseCommand_addItem() throws Exception { + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + + AddItemCommand command = (AddItemCommand) parser.parseCommand( + AddItemCommand.COMMAND_WORD + " " + ITEM_DESC_DESC_SHOPPING + ITEM_DURATION_DESC_SHOPPING + + COST_DESC_SHOPPING + PRIORITY_DESC_SHOPPING); + assertEquals(new AddItemCommand(getShopping()), command); + } + + @Test + public void parseCommand_deleteItem() throws Exception { + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + MultiIndex multiIndex = new MultiIndex(); + multiIndex.addIndex(Index.fromZeroBased(0)); + + DeleteItemCommand command = (DeleteItemCommand) parser.parseCommand( + DeleteItemCommand.COMMAND_WORD + " " + multiIndex); + assertEquals(new DeleteItemCommand(multiIndex), command); + } + + @Test + public void parseCommand_editItem() throws Exception { + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + MultiIndex multiIndex = new MultiIndex(); + multiIndex.addIndex(Index.fromZeroBased(0)); + + EditItemCommand command = (EditItemCommand) parser.parseCommand( + EditItemCommand.COMMAND_WORD + " " + multiIndex + ITEM_DESC_DESC_SHOPPING); + EditItemCommand.EditItemDescriptor descriptor = new EditItemDescriptorBuilder() + .withDescription(VALID_ITEM_DESC_SHOPPING).build(); + assertEquals(new EditItemCommand(multiIndex, descriptor), command); + } + + @Test + public void parseCommand_free() throws Exception { + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + + FreeCommand command = (FreeCommand) parser.parseCommand(FreeCommand.COMMAND_WORD); + assertEquals(new FreeCommand(), command); + + // switch to home page + StageManager.getInstance().setHomeStage(); + assertThrows(ParseException.class, MESSAGE_UNAVAILABLE_COMMAND_HOME, () + -> parser.parseCommand(FreeCommand.COMMAND_WORD)); + } + + @Test + public void parseCommand_planItem() throws Exception { + // switch to itinerary page + StageManager.getInstance().setWishStage(getAutumn()); + MultiIndex multiIndex = new MultiIndex(); + multiIndex.addIndex(Index.fromZeroBased(0)); + + PlanCommand command = (PlanCommand) parser.parseCommand( + PlanCommand.COMMAND_WORD + " " + multiIndex + DAY_NUMBER_DESC + START_TIME_DESC_1200); + assertEquals(new PlanCommand(multiIndex.getTaskIndex(), + new DayNumber(VALID_DAY_NUMBER), VALID_START_TIME_1200), command); + + // switch to home page + StageManager.getInstance().setHomeStage(); + assertThrows(ParseException.class, MESSAGE_UNAVAILABLE_COMMAND_HOME, () + -> parser.parseCommand(PlanCommand.COMMAND_WORD + " " + + multiIndex + DAY_NUMBER_DESC + START_TIME_DESC_1200)); + } + + @Test + public void parseCommand_unplanItem() throws Exception { + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + MultiIndex multiIndex = new MultiIndex(); + multiIndex.addIndex(Index.fromZeroBased(0)); + multiIndex.addIndex(Index.fromZeroBased(0)); + + UnplanCommand command = (UnplanCommand) parser.parseCommand( + UnplanCommand.COMMAND_WORD + " " + multiIndex); + assertEquals(new UnplanCommand(multiIndex), command); + + // switch to home page + StageManager.getInstance().setHomeStage(); + assertThrows(ParseException.class, MESSAGE_UNAVAILABLE_COMMAND_HOME, () + -> parser.parseCommand(UnplanCommand.COMMAND_WORD + " " + multiIndex)); + } + + @Test + public void parseCommand_copy() throws Exception { + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + + CopyCommand command = (CopyCommand) parser.parseCommand(CopyCommand.COMMAND_WORD); + assertEquals(new CopyCommand(), command); + + // switch to home page + StageManager.getInstance().setHomeStage(); + assertThrows(ParseException.class, MESSAGE_UNAVAILABLE_COMMAND_HOME, () + -> parser.parseCommand(CopyCommand.COMMAND_WORD)); + } + + @Test + public void parseCommand_pdf() throws Exception { + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + + PdfCommand command = (PdfCommand) parser.parseCommand(PdfCommand.COMMAND_WORD); + assertEquals(new PdfCommand(), command); + + // switch to home page + StageManager.getInstance().setHomeStage(); + assertThrows(ParseException.class, MESSAGE_UNAVAILABLE_COMMAND_HOME, () + -> parser.parseCommand(PdfCommand.COMMAND_WORD)); + } + + @Test + public void parseCommand_home() throws Exception { + // switch to home page + StageManager.getInstance().setHomeStage(); + + HomeCommand command = (HomeCommand) parser.parseCommand( + HomeCommand.COMMAND_WORD); + assertEquals(new HomeCommand(), command); + + // switch to itinerary page + StageManager.getInstance().setWishStage(getGraduation()); + + command = (HomeCommand) parser.parseCommand( + HomeCommand.COMMAND_WORD); + assertEquals(new HomeCommand(), command); + } +} diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/waddle/model/ModelManagerTest.java similarity index 52% rename from src/test/java/seedu/address/model/ModelManagerTest.java rename to src/test/java/seedu/waddle/model/ModelManagerTest.java index 2cf1418d116..d866f0fa2d5 100644 --- a/src/test/java/seedu/address/model/ModelManagerTest.java +++ b/src/test/java/seedu/waddle/model/ModelManagerTest.java @@ -1,12 +1,12 @@ -package seedu.address.model; +package seedu.waddle.model; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BENSON; +import static seedu.waddle.model.Model.PREDICATE_SHOW_ALL_ITINERARIES; +import static seedu.waddle.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.TypicalItineraries.AUTUMN; +import static seedu.waddle.testutil.TypicalItineraries.SPRING; import java.nio.file.Path; import java.nio.file.Paths; @@ -14,9 +14,9 @@ import org.junit.jupiter.api.Test; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.testutil.AddressBookBuilder; +import seedu.waddle.commons.core.GuiSettings; +import seedu.waddle.model.itinerary.NameContainsKeywordsPredicate; +import seedu.waddle.testutil.WaddleBuilder; public class ModelManagerTest { @@ -26,7 +26,7 @@ public class ModelManagerTest { public void constructor() { assertEquals(new UserPrefs(), modelManager.getUserPrefs()); assertEquals(new GuiSettings(), modelManager.getGuiSettings()); - assertEquals(new AddressBook(), new AddressBook(modelManager.getAddressBook())); + assertEquals(new Waddle(), new Waddle(modelManager.getWaddle())); } @Test @@ -37,14 +37,14 @@ public void setUserPrefs_nullUserPrefs_throwsNullPointerException() { @Test public void setUserPrefs_validUserPrefs_copiesUserPrefs() { UserPrefs userPrefs = new UserPrefs(); - userPrefs.setAddressBookFilePath(Paths.get("address/book/file/path")); + userPrefs.setWaddleFilePath(Paths.get("waddle/file/path")); userPrefs.setGuiSettings(new GuiSettings(1, 2, 3, 4)); modelManager.setUserPrefs(userPrefs); assertEquals(userPrefs, modelManager.getUserPrefs()); // Modifying userPrefs should not modify modelManager's userPrefs UserPrefs oldUserPrefs = new UserPrefs(userPrefs); - userPrefs.setAddressBookFilePath(Paths.get("new/address/book/file/path")); + userPrefs.setWaddleFilePath(Paths.get("new/waddle/file/path")); assertEquals(oldUserPrefs, modelManager.getUserPrefs()); } @@ -61,47 +61,47 @@ public void setGuiSettings_validGuiSettings_setsGuiSettings() { } @Test - public void setAddressBookFilePath_nullPath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.setAddressBookFilePath(null)); + public void setWaddleFilePath_nullPath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.setWaddleFilePath(null)); } @Test - public void setAddressBookFilePath_validPath_setsAddressBookFilePath() { - Path path = Paths.get("address/book/file/path"); - modelManager.setAddressBookFilePath(path); - assertEquals(path, modelManager.getAddressBookFilePath()); + public void setWaddleFilePath_validPath_setsWaddleFilePath() { + Path path = Paths.get("waddle/file/path"); + modelManager.setWaddleFilePath(path); + assertEquals(path, modelManager.getWaddleFilePath()); } @Test - public void hasPerson_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.hasPerson(null)); + public void hasItinerary_nullItinerary_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.hasItinerary(null)); } @Test - public void hasPerson_personNotInAddressBook_returnsFalse() { - assertFalse(modelManager.hasPerson(ALICE)); + public void hasItinerary_itineraryNotInWaddle_returnsFalse() { + assertFalse(modelManager.hasItinerary(SPRING)); } @Test - public void hasPerson_personInAddressBook_returnsTrue() { - modelManager.addPerson(ALICE); - assertTrue(modelManager.hasPerson(ALICE)); + public void hasItinerary_itineraryInWaddle_returnsTrue() { + modelManager.addItinerary(SPRING); + assertTrue(modelManager.hasItinerary(SPRING)); } @Test - public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredPersonList().remove(0)); + public void getFilteredItineraryList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredItineraryList().remove(0)); } @Test public void equals() { - AddressBook addressBook = new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build(); - AddressBook differentAddressBook = new AddressBook(); + Waddle waddle = new WaddleBuilder().withItinerary(SPRING).withItinerary(AUTUMN).build(); + Waddle differentWaddle = new Waddle(); UserPrefs userPrefs = new UserPrefs(); // same values -> returns true - modelManager = new ModelManager(addressBook, userPrefs); - ModelManager modelManagerCopy = new ModelManager(addressBook, userPrefs); + modelManager = new ModelManager(waddle, userPrefs); + ModelManager modelManagerCopy = new ModelManager(waddle, userPrefs); assertTrue(modelManager.equals(modelManagerCopy)); // same object -> returns true @@ -113,20 +113,20 @@ public void equals() { // different types -> returns false assertFalse(modelManager.equals(5)); - // different addressBook -> returns false - assertFalse(modelManager.equals(new ModelManager(differentAddressBook, userPrefs))); + // different waddle -> returns false + assertFalse(modelManager.equals(new ModelManager(differentWaddle, userPrefs))); // different filteredList -> returns false - String[] keywords = ALICE.getName().fullName.split("\\s+"); - modelManager.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(keywords))); - assertFalse(modelManager.equals(new ModelManager(addressBook, userPrefs))); + String[] keywords = SPRING.getDescription().description.split("\\s+"); + modelManager.updateFilteredItineraryList(new NameContainsKeywordsPredicate(Arrays.asList(keywords))); + assertFalse(modelManager.equals(new ModelManager(waddle, userPrefs))); // resets modelManager to initial state for upcoming tests - modelManager.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + modelManager.updateFilteredItineraryList(PREDICATE_SHOW_ALL_ITINERARIES); // different userPrefs -> returns false UserPrefs differentUserPrefs = new UserPrefs(); - differentUserPrefs.setAddressBookFilePath(Paths.get("differentFilePath")); - assertFalse(modelManager.equals(new ModelManager(addressBook, differentUserPrefs))); + differentUserPrefs.setWaddleFilePath(Paths.get("differentFilePath")); + assertFalse(modelManager.equals(new ModelManager(waddle, differentUserPrefs))); } } diff --git a/src/test/java/seedu/address/model/UserPrefsTest.java b/src/test/java/seedu/waddle/model/UserPrefsTest.java similarity index 68% rename from src/test/java/seedu/address/model/UserPrefsTest.java rename to src/test/java/seedu/waddle/model/UserPrefsTest.java index b1307a70d52..777f15567fa 100644 --- a/src/test/java/seedu/address/model/UserPrefsTest.java +++ b/src/test/java/seedu/waddle/model/UserPrefsTest.java @@ -1,6 +1,6 @@ -package seedu.address.model; +package seedu.waddle.model; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; @@ -13,9 +13,9 @@ public void setGuiSettings_nullGuiSettings_throwsNullPointerException() { } @Test - public void setAddressBookFilePath_nullPath_throwsNullPointerException() { + public void setWaddleFilePath_nullPath_throwsNullPointerException() { UserPrefs userPrefs = new UserPrefs(); - assertThrows(NullPointerException.class, () -> userPrefs.setAddressBookFilePath(null)); + assertThrows(NullPointerException.class, () -> userPrefs.setWaddleFilePath(null)); } } diff --git a/src/test/java/seedu/waddle/model/WaddleTest.java b/src/test/java/seedu/waddle/model/WaddleTest.java new file mode 100644 index 00000000000..e914c1b72e4 --- /dev/null +++ b/src/test/java/seedu/waddle/model/WaddleTest.java @@ -0,0 +1,102 @@ +package seedu.waddle.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COUNTRY_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PEOPLE_WINTER; +import static seedu.waddle.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.TypicalItineraries.SUMMER; +import static seedu.waddle.testutil.TypicalItineraries.getTypicalWaddle; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.model.itinerary.exceptions.DuplicateItineraryException; +import seedu.waddle.testutil.ItineraryBuilder; + + +public class WaddleTest { + + private final Waddle waddle = new Waddle(); + + @Test + public void constructor() { + assertEquals(Collections.emptyList(), waddle.getItineraryList()); + } + + @Test + public void resetData_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> waddle.resetData(null)); + } + + @Test + public void resetData_withValidReadOnlyWaddle_replacesData() { + Waddle newData = getTypicalWaddle(); + waddle.resetData(newData); + assertEquals(newData, waddle); + } + + @Test + public void resetData_withDuplicateItineraries_throwsDuplicateItineraryException() { + Itinerary editedSummer = new ItineraryBuilder(SUMMER).withCountry(VALID_COUNTRY_WINTER) + .withPeople(VALID_PEOPLE_WINTER).build(); + List newItineraries = Arrays.asList(SUMMER, editedSummer); + WaddleStub newData = new WaddleStub(newItineraries); + + assertThrows(DuplicateItineraryException.class, () -> waddle.resetData(newData)); + } + + @Test + public void hasItinerary_nullItinerary_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> waddle.hasItinerary(null)); + } + + @Test + public void hasItinerary_itineraryNotInWaddle_returnsFalse() { + assertFalse(waddle.hasItinerary(SUMMER)); + } + + @Test + public void hasItinerary_itineraryInWaddle_returnsTrue() { + waddle.addItinerary(SUMMER); + assertTrue(waddle.hasItinerary(SUMMER)); + } + + @Test + public void hasItinerary_itineraryWithSameIdentityFieldsInWaddle_returnsTrue() { + waddle.addItinerary(SUMMER); + Itinerary editedSummer = new ItineraryBuilder(SUMMER).withCountry(VALID_COUNTRY_WINTER) + .withPeople(VALID_PEOPLE_WINTER).build(); + assertTrue(waddle.hasItinerary(editedSummer)); + } + + @Test + public void getItineraryList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> waddle.getItineraryList().remove(0)); + } + + /** + * A stub ReadOnlyWaddle whose itinerary list can violate interface constraints. + */ + private static class WaddleStub implements ReadOnlyWaddle { + private final ObservableList itineraries = FXCollections.observableArrayList(); + + WaddleStub(Collection itineraries) { + this.itineraries.setAll(itineraries); + } + + @Override + public ObservableList getItineraryList() { + return itineraries; + } + } + +} diff --git a/src/test/java/seedu/waddle/model/item/CostTest.java b/src/test/java/seedu/waddle/model/item/CostTest.java new file mode 100644 index 00000000000..b007fc38c78 --- /dev/null +++ b/src/test/java/seedu/waddle/model/item/CostTest.java @@ -0,0 +1,39 @@ +package seedu.waddle.model.item; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class CostTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Cost(null)); + } + + @Test + public void constructor_invalidCost_throwsIllegalArgumentException() { + String invalidCost = ""; + assertThrows(IllegalArgumentException.class, () -> new Cost(invalidCost)); + } + + @Test + public void isValidCost() { + // null cost + assertThrows(NullPointerException.class, () -> Cost.isValidCost(null)); + + // invalid cost + assertFalse(Cost.isValidCost("")); // empty string + assertFalse(Cost.isValidCost(" ")); // spaces only + assertFalse(Cost.isValidCost("one")); + assertFalse(Cost.isValidCost("1,000,000")); + assertFalse(Cost.isValidCost("2000000")); // greater than 1,000,000 + + // valid cost + assertTrue(Cost.isValidCost("2")); + assertTrue(Cost.isValidCost("999999")); + assertTrue(Cost.isValidCost("0")); + } +} diff --git a/src/test/java/seedu/waddle/model/item/DayTest.java b/src/test/java/seedu/waddle/model/item/DayTest.java new file mode 100644 index 00000000000..8cc2aea4147 --- /dev/null +++ b/src/test/java/seedu/waddle/model/item/DayTest.java @@ -0,0 +1,178 @@ +package seedu.waddle.model.item; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.waddle.commons.core.Messages.MESSAGE_CONFLICTING_ITEMS; +import static seedu.waddle.commons.core.Messages.MESSAGE_ITEM_PAST_MIDNIGHT; + +import java.time.LocalTime; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.testutil.ItemBuilder; + +public class DayTest { + private Day dayStub; + private Item noonOneHour; + private String expectedConflictMessage; + + @BeforeEach + public void setUp() { + dayStub = new Day(0); + noonOneHour = new ItemBuilder().build(); + noonOneHour.setStartTime(LocalTime.NOON); + StringBuilder conflicts = new StringBuilder(); + conflicts.append(" ").append(noonOneHour.getDescription()).append(": ") + .append(noonOneHour.getStartTime()).append(" - ").append(noonOneHour.getEndTime()).append("\n"); + expectedConflictMessage = String.format(MESSAGE_CONFLICTING_ITEMS, conflicts); + + try { + dayStub.addItem(noonOneHour); + } catch (CommandException e) { + assert false : "Failed to create Day stub"; + } + } + + @Test + public void addItem_startTimeConflict_emptyList() { + Item startTimeConflictItem = new ItemBuilder().withDesc("start time conflict").build(); + startTimeConflictItem.setStartTime(LocalTime.parse("11:30")); + + try { + dayStub.addItem(startTimeConflictItem); + } catch (CommandException actualCommandException) { + assertEquals(actualCommandException.getLocalizedMessage(), expectedConflictMessage); + return; + } + assert false : "A time conflict CommandException should have been thrown."; + } + + @Test + public void addItem_endTimeConflict_emptyList() { + Item endTimeConflictItem = new ItemBuilder().withDesc("end time conflict").build(); + endTimeConflictItem.setStartTime(LocalTime.parse("12:30")); + + try { + dayStub.addItem(endTimeConflictItem); + } catch (CommandException actualCommandException) { + assertEquals(actualCommandException.getLocalizedMessage(), expectedConflictMessage); + return; + } + assert false : "A time conflict CommandException should have been thrown."; + } + + @Test + public void addItem_sameTimeConflict_emptyList() { + Item sameTimeConflictItem = new ItemBuilder().withDesc("same time conflict").build(); + sameTimeConflictItem.setStartTime(LocalTime.parse("12:00")); + + try { + dayStub.addItem(sameTimeConflictItem); + } catch (CommandException actualCommandException) { + assertEquals(actualCommandException.getLocalizedMessage(), expectedConflictMessage); + return; + } + assert false : "A time conflict CommandException should have been thrown."; + } + + @Test + public void addItem_overlapTimeConflict_emptyList() { + Item overlapTimeConflictItem = new ItemBuilder().withDesc("overlap time conflict") + .withDuration("120").build(); + overlapTimeConflictItem.setStartTime(LocalTime.parse("11:30")); + + try { + dayStub.addItem(overlapTimeConflictItem); + } catch (CommandException actualCommandException) { + assertEquals(actualCommandException.getLocalizedMessage(), expectedConflictMessage); + return; + } + assert false : "A time conflict CommandException should have been thrown."; + } + + @Test + public void addItem_pastMidnightTimeConflict_emptyList() { + Item pastMidnightTimeConflictItem = new ItemBuilder().withDesc("past midnight time conflict").build(); + pastMidnightTimeConflictItem.setStartTime(LocalTime.parse("23:30")); + String expectedMidnightMessage = String.format(MESSAGE_ITEM_PAST_MIDNIGHT, + pastMidnightTimeConflictItem.getDescription()); + + try { + dayStub.addItem(pastMidnightTimeConflictItem); + } catch (CommandException actualCommandException) { + assertEquals(actualCommandException.getLocalizedMessage(), expectedMidnightMessage); + return; + } + assert false : "A time conflict CommandException should have been thrown."; + } + + @Test + public void getVacantSlots_correctOutput() { + modifyDayStub(); + String expectedString = "Day 1:" + System.lineSeparator() + + " 02:00 - 11:00" + System.lineSeparator() + + " 13:00 - 23:00" + System.lineSeparator(); + String actualString = dayStub.getVacantSlots(); + + assertEquals(expectedString, actualString); + } + + @Test + public void getTextRepresentation_correctOutput() { + modifyDayStub(); + String expectedString = "Day 1" + System.lineSeparator() + + " 1. start at midnight" + System.lineSeparator() + + " ★★★★★" + System.lineSeparator() + + " Cost $100.00" + System.lineSeparator() + + " Duration 60 mins" + System.lineSeparator() + + " Time: 00:00 - 01:00" + System.lineSeparator() + + " " + System.lineSeparator() + + " 2. start joined with previous item" + System.lineSeparator() + + " ★★★★★" + System.lineSeparator() + + " Cost $100.00" + System.lineSeparator() + + " Duration 60 mins" + System.lineSeparator() + + " Time: 01:00 - 02:00" + System.lineSeparator() + + " " + System.lineSeparator() + + " 3. end joined with next item" + System.lineSeparator() + + " ★★★★★" + System.lineSeparator() + + " Cost $100.00" + System.lineSeparator() + + " Duration 60 mins" + System.lineSeparator() + + " Time: 11:00 - 12:00" + System.lineSeparator() + + " " + System.lineSeparator() + + " 4. Airport" + System.lineSeparator() + + " ★★★★★" + System.lineSeparator() + + " Cost $100.00" + System.lineSeparator() + + " Duration 60 mins" + System.lineSeparator() + + " Time: 12:00 - 13:00" + System.lineSeparator() + + " " + System.lineSeparator() + + " 5. end at midnight" + System.lineSeparator() + + " ★★★★★" + System.lineSeparator() + + " Cost $100.00" + System.lineSeparator() + + " Duration 60 mins" + System.lineSeparator() + + " Time: 23:00 - 00:00 (next day)" + System.lineSeparator() + + " " + System.lineSeparator(); + String actualString = dayStub.getTextRepresentation(); + assertEquals(expectedString, actualString); + } + + private void modifyDayStub() { + Item startAtMidnight = new ItemBuilder().withDesc("start at midnight").build(); + startAtMidnight.setStartTime(LocalTime.MIDNIGHT); + Item startJoined = new ItemBuilder().withDesc("start joined with previous item").build(); + startJoined.setStartTime(LocalTime.parse("01:00")); + Item endJoined = new ItemBuilder().withDesc("end joined with next item").build(); + endJoined.setStartTime(LocalTime.parse("11:00")); + Item endAtMidnight = new ItemBuilder().withDesc("end at midnight").build(); + endAtMidnight.setStartTime(LocalTime.parse("23:00")); + + try { + dayStub.addItem(startAtMidnight); + dayStub.addItem(startJoined); + dayStub.addItem(endJoined); + dayStub.addItem(endAtMidnight); + } catch (CommandException e) { + assert false : "Failed to modify Day stub"; + } + } +} diff --git a/src/test/java/seedu/waddle/model/item/DurationTest.java b/src/test/java/seedu/waddle/model/item/DurationTest.java new file mode 100644 index 00000000000..d8d82c4e6ba --- /dev/null +++ b/src/test/java/seedu/waddle/model/item/DurationTest.java @@ -0,0 +1,37 @@ +package seedu.waddle.model.item; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class DurationTest { + + @Test + public void constructor_null_throwsNumberFormatExceptionException() { + assertThrows(NumberFormatException.class, () -> new Duration(null)); + } + + @Test + public void constructor_invalidDuration_throwsIllegalArgumentException() { + String invalidDuration = ""; + assertThrows(IllegalArgumentException.class, () -> new Duration(invalidDuration)); + } + + @Test + public void isValidDuration() { + // null duration, but it doesn't throw anything + // assertThrows(NullPointerException.class, () -> Duration.isValidDuration(null)); + + // invalid duration + assertFalse(Duration.isValidDuration("")); // empty string + assertFalse(Duration.isValidDuration(" ")); // spaces only + assertFalse(Duration.isValidDuration("one")); + assertFalse(Duration.isValidDuration("0")); + + // valid duration + assertTrue(Duration.isValidDuration("2")); + assertTrue(Duration.isValidDuration("30")); + } +} diff --git a/src/test/java/seedu/waddle/model/item/ItemTest.java b/src/test/java/seedu/waddle/model/item/ItemTest.java new file mode 100644 index 00000000000..dad63bb9caa --- /dev/null +++ b/src/test/java/seedu/waddle/model/item/ItemTest.java @@ -0,0 +1,128 @@ +package seedu.waddle.model.item; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COST_SKINNY; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_SKINNY; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITEM_DESC_SKINNY; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PRIORITY_SKINNY; +import static seedu.waddle.testutil.TypicalItems.getShopping; +import static seedu.waddle.testutil.TypicalItems.getSkinny; + +import java.time.LocalTime; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.core.Text; +import seedu.waddle.testutil.ItemBuilder; + +public class ItemTest { + + @Test + public void isSameItem() { + // same object -> returns true + assertTrue(getShopping().isSameItem(getShopping())); + + // null -> returns false + assertFalse(getShopping().isSameItem(null)); + + // same name, all other attributes different -> returns true + Item editedShopping = new ItemBuilder(getShopping()) + .withPriority(VALID_PRIORITY_SKINNY).withCost(VALID_COST_SKINNY) + .withDuration(VALID_DURATION_SKINNY).build(); + assertTrue(getShopping().isSameItem(editedShopping)); + + // different name, all other attributes same -> returns false + editedShopping = new ItemBuilder(getShopping()).withDesc(VALID_ITEM_DESC_SKINNY).build(); + assertFalse(getShopping().isSameItem(editedShopping)); + + // name differs in case, all other attributes same -> returns false + Item editedSkinny = new ItemBuilder(getSkinny()).withDesc(VALID_ITEM_DESC_SKINNY + .toLowerCase()).build(); + assertFalse(getSkinny().isSameItem(editedSkinny)); + + // name has trailing spaces, all other attributes same -> returns false + String nameWithTrailingSpaces = VALID_ITEM_DESC_SKINNY + " "; + editedSkinny = new ItemBuilder(getSkinny()).withDesc(nameWithTrailingSpaces).build(); + assertFalse(getSkinny().isSameItem(editedSkinny)); + } + + @Test + public void getTimeString_notPlanned() { + String expectedString = "Time: (Not planned)"; + String actualString = new ItemBuilder().build().getTimeString(Text.INDENT_NONE); + assertEquals(expectedString, actualString); + } + + @Test + public void getTimeString_planned() { + // middle of the day + Item plannedItem = new ItemBuilder().withDesc("planned item").build(); + plannedItem.setStartTime(LocalTime.NOON); + String expectedString = "Time: 12:00 - 13:00"; + String actualString = plannedItem.getTimeString(Text.INDENT_NONE); + assertEquals(expectedString, actualString); + + // start at midnight + Item plannedItem2 = new ItemBuilder().withDesc("planned item2").build(); + plannedItem2.setStartTime(LocalTime.MIDNIGHT); + expectedString = "Time: 00:00 - 01:00"; + actualString = plannedItem2.getTimeString(Text.INDENT_NONE); + assertEquals(expectedString, actualString); + + // end at midnight + Item plannedItem3 = new ItemBuilder().withDesc("planned item3").build(); + plannedItem3.setStartTime(LocalTime.parse("23:00")); + expectedString = "Time: 23:00 - 00:00 (next day)"; + actualString = plannedItem3.getTimeString(Text.INDENT_NONE); + assertEquals(expectedString, actualString); + } + + @Test + public void toString_correctOutput() { + String expectedString = "Airport" + System.lineSeparator() + + " ★★★★★" + System.lineSeparator() + + " Cost $100.00" + System.lineSeparator() + + " Duration 60 mins" + System.lineSeparator() + + " Time: (Not planned)"; + String actualString = new ItemBuilder().build().toString(); + assertEquals(expectedString, actualString); + } + + @Test + public void equals() { + Item shopping = getShopping(); + // same values -> returns true, test does not work + Item shoppingCopy = new ItemBuilder(getShopping()).build(); + assertTrue(shopping.equals(shoppingCopy)); + + // same object -> returns true + assertTrue(shopping.equals(shopping)); + + // null -> returns false + assertFalse(shopping.equals(null)); + + // different type -> returns false + assertFalse(shopping.equals(5)); + + // different item -> returns false + assertFalse(shopping.equals(getSkinny())); + + // different name -> returns false + Item editedShopping = new ItemBuilder(getShopping()).withDesc(VALID_ITEM_DESC_SKINNY).build(); + assertFalse(shopping.equals(editedShopping)); + + // different duration -> returns false + editedShopping = new ItemBuilder(getShopping()).withDuration(VALID_DURATION_SKINNY).build(); + assertFalse(shopping.equals(editedShopping)); + + // different priority -> returns false + editedShopping = new ItemBuilder(getShopping()).withPriority(VALID_PRIORITY_SKINNY).build(); + assertFalse(shopping.equals(editedShopping)); + + // different cost -> returns false + editedShopping = new ItemBuilder(getShopping()).withCost(VALID_COST_SKINNY).build(); + assertFalse(shopping.equals(editedShopping)); + } +} diff --git a/src/test/java/seedu/waddle/model/item/PriorityTest.java b/src/test/java/seedu/waddle/model/item/PriorityTest.java new file mode 100644 index 00000000000..3f5307d954e --- /dev/null +++ b/src/test/java/seedu/waddle/model/item/PriorityTest.java @@ -0,0 +1,34 @@ +package seedu.waddle.model.item; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class PriorityTest { + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Priority(null)); + } + + @Test + public void constructor_invalidPriority_throwsIllegalArgumentException() { + int invalidPriority = 0; + assertThrows(IllegalArgumentException.class, () -> new Priority(invalidPriority)); + } + + @Test + public void isValidDuration() { + // null priority + assertThrows(NullPointerException.class, () -> Priority.isValidPriority(null)); + + // invalid priority + assertFalse(Priority.isValidPriority(0)); + assertFalse(Priority.isValidPriority(6)); + + // valid priority + assertTrue(Priority.isValidPriority(1)); + assertTrue(Priority.isValidPriority(5)); + } +} diff --git a/src/test/java/seedu/waddle/model/item/UniqueItemListTest.java b/src/test/java/seedu/waddle/model/item/UniqueItemListTest.java new file mode 100644 index 00000000000..0824a64eff3 --- /dev/null +++ b/src/test/java/seedu/waddle/model/item/UniqueItemListTest.java @@ -0,0 +1,181 @@ +package seedu.waddle.model.item; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COST_SKINNY; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_SKINNY; +import static seedu.waddle.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.TypicalItems.getShopping; +import static seedu.waddle.testutil.TypicalItems.getSkinny; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.model.item.exceptions.DuplicateItemException; +import seedu.waddle.model.item.exceptions.ItemNotFoundException; +import seedu.waddle.testutil.ItemBuilder; + +public class UniqueItemListTest { + private final UniqueItemList uniqueItemList = new UniqueItemList(); + + @Test + public void contains_nullItem_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueItemList.contains(null)); + } + + @Test + public void contains_itemNotInList_returnsFalse() { + assertFalse(uniqueItemList.contains(getShopping())); + } + + @Test + public void contains_itemInList_returnsTrue() { + Item shopping = getShopping(); + uniqueItemList.add(shopping); + assertTrue(uniqueItemList.contains(shopping)); + } + + @Test + public void contains_itemWithSameIdentityFieldsInList_returnsTrue() { + Item shopping = getShopping(); + uniqueItemList.add(shopping); + Item editedShopping = new ItemBuilder(getShopping()).withDuration(VALID_DURATION_SKINNY) + .withCost(VALID_COST_SKINNY).build(); + assertTrue(uniqueItemList.contains(editedShopping)); + } + + @Test + public void add_nullItem_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueItemList.add(null)); + } + + @Test + public void add_duplicateItem_throwsDuplicateItineraryException() { + Item shopping = getShopping(); + uniqueItemList.add(shopping); + assertThrows(DuplicateItemException.class, () -> uniqueItemList.add(shopping)); + } + + @Test + public void setItinerary_nullTargetItem_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueItemList.setItem(null, getShopping())); + } + + @Test + public void setItem_nullEditedItem_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueItemList.setItem(getShopping(), null)); + } + + @Test + public void setItem_targetItemNotInList_throwsItemNotFoundException() { + Item shopping = getShopping(); + assertThrows(ItemNotFoundException.class, () -> uniqueItemList.setItem(shopping, shopping)); + } + + @Test + public void setItem_editedItemIsSameItem_success() { + Item shopping = getShopping(); + uniqueItemList.add(shopping); + uniqueItemList.setItem(shopping, shopping); + UniqueItemList expectedUniqueItemList = new UniqueItemList(); + expectedUniqueItemList.add(shopping); + assertEquals(expectedUniqueItemList, uniqueItemList); + } + + @Test + public void setItem_editedItemHasSameIdentity_success() { + Item shopping = getShopping(); + uniqueItemList.add(shopping); + Item editedShopping = new ItemBuilder(getShopping()).withDuration(VALID_DURATION_SKINNY) + .withCost(VALID_COST_SKINNY).build(); + uniqueItemList.setItem(shopping, editedShopping); + UniqueItemList expectedUniqueItemList = new UniqueItemList(); + expectedUniqueItemList.add(editedShopping); + assertEquals(expectedUniqueItemList, uniqueItemList); + } + + @Test + public void setItem_editedItemHasDifferentIdentity_success() { + Item shopping = getShopping(); + Item skinny = getSkinny(); + uniqueItemList.add(shopping); + uniqueItemList.setItem(shopping, skinny); + UniqueItemList expectedUniqueItemList = new UniqueItemList(); + expectedUniqueItemList.add(skinny); + assertEquals(expectedUniqueItemList, uniqueItemList); + } + + @Test + public void setItem_editedItemHasNonUniqueIdentity_throwsDuplicateItemException() { + Item shopping = getShopping(); + Item skinny = getSkinny(); + uniqueItemList.add(shopping); + uniqueItemList.add(skinny); + assertThrows(DuplicateItemException.class, () -> uniqueItemList.setItem(shopping, skinny)); + } + + @Test + public void remove_itemDoesNotExist_throwsIndexOutOfBoundsException() { + assertThrows(IndexOutOfBoundsException.class, () -> uniqueItemList.remove(4)); + } + + @Test + public void remove_existingItem_removesItem() { + Item shopping = getShopping(); + uniqueItemList.add(shopping); + uniqueItemList.remove(0); + UniqueItemList expectedUniqueItemList = new UniqueItemList(); + assertEquals(expectedUniqueItemList, uniqueItemList); + } + + @Test + public void setItem_nullUniqueItemList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueItemList.setItems((UniqueItemList) null)); + } + + @Test + public void setItem_uniqueItemList_replacesOwnListWithProvidedUniqueItemList() { + Item shopping = getShopping(); + Item skinny = getSkinny(); + uniqueItemList.add(shopping); + UniqueItemList expectedUniqueItemList = new UniqueItemList(); + expectedUniqueItemList.add(skinny); + uniqueItemList.setItems(expectedUniqueItemList); + assertEquals(expectedUniqueItemList, uniqueItemList); + } + + @Test + public void setItem_nullList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueItemList.setItems((List) null)); + } + + @Test + public void setItem_list_replacesOwnListWithProvidedList() { + Item shopping = getShopping(); + Item skinny = getSkinny(); + uniqueItemList.add(shopping); + List itemList = Collections.singletonList(skinny); + uniqueItemList.setItems(itemList); + UniqueItemList expectedUniqueItemList = new UniqueItemList(); + expectedUniqueItemList.add(skinny); + assertEquals(expectedUniqueItemList, uniqueItemList); + } + + @Test + public void setItem_listWithDuplicateItem_throwsDuplicateItemException() { + Item shopping = getShopping(); + List listWithDuplicateItem = Arrays.asList(shopping, shopping); + assertThrows(DuplicateItemException.class, () + -> uniqueItemList.setItems(listWithDuplicateItem)); + } + + @Test + public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () + -> uniqueItemList.asUnmodifiableObservableList().remove(0)); + } +} diff --git a/src/test/java/seedu/waddle/model/itinerary/BudgetTest.java b/src/test/java/seedu/waddle/model/itinerary/BudgetTest.java new file mode 100644 index 00000000000..db4746048ad --- /dev/null +++ b/src/test/java/seedu/waddle/model/itinerary/BudgetTest.java @@ -0,0 +1,40 @@ +package seedu.waddle.model.itinerary; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class BudgetTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Budget(null)); + } + + @Test + public void constructor_invalidBudget_throwsIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> new Budget("-0.50")); + assertThrows(IllegalArgumentException.class, () -> new Budget("1000000.50")); + + } + + @Test + public void isValidBudgetTest() { + // null budget + assertThrows(NullPointerException.class, () -> Budget.isValidBudget(null)); + + // invalid budget + assertFalse(Budget.isValidBudget("")); // empty string + assertFalse(Budget.isValidBudget(" ")); // spaces only + assertFalse(Budget.isValidBudget("$50")); // special characters + assertFalse(Budget.isValidBudget("-1")); // negative budget + assertFalse(Budget.isValidBudget("1000001")); // budget too high + + // valid budget + assertTrue(Budget.isValidBudget("0")); + assertTrue(Budget.isValidBudget("1000000")); + assertTrue(Budget.isValidBudget("500.50")); + } +} diff --git a/src/test/java/seedu/waddle/model/itinerary/CountryTest.java b/src/test/java/seedu/waddle/model/itinerary/CountryTest.java new file mode 100644 index 00000000000..20f4b2605dc --- /dev/null +++ b/src/test/java/seedu/waddle/model/itinerary/CountryTest.java @@ -0,0 +1,37 @@ +package seedu.waddle.model.itinerary; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class CountryTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Country(null)); + } + + @Test + public void constructor_invalidCountry_throwsIllegalArgumentException() { + String invalidCountry = ""; + assertThrows(IllegalArgumentException.class, () -> new Country(invalidCountry)); + } + + @Test + public void isValidCountry() { + // null country + assertThrows(NullPointerException.class, () -> Country.isValidCountry(null)); + + // invalid country + assertFalse(Country.isValidCountry("")); // empty string + assertFalse(Country.isValidCountry(" ")); // spaces only + assertFalse(Country.isValidCountry("Singa*pore")); + + // valid country + assertTrue(Country.isValidCountry("Singapore")); + assertTrue(Country.isValidCountry("Peru")); + assertTrue(Country.isValidCountry("United States")); + } +} diff --git a/src/test/java/seedu/waddle/model/itinerary/DateTest.java b/src/test/java/seedu/waddle/model/itinerary/DateTest.java new file mode 100644 index 00000000000..1c31b1606a6 --- /dev/null +++ b/src/test/java/seedu/waddle/model/itinerary/DateTest.java @@ -0,0 +1,39 @@ +package seedu.waddle.model.itinerary; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class DateTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Date(null)); + } + + @Test + public void constructor_invalidDate_throwsIllegalArgumentException() { + String invalidDate = ""; + assertThrows(IllegalArgumentException.class, () -> new Date(invalidDate)); + } + + @Test + public void isValidDate() { + // null date + assertThrows(NullPointerException.class, () -> Date.isValidDate(null)); + + // invalid date + assertFalse(Date.isValidDate("")); // empty string + assertFalse(Date.isValidDate("06-08-2000")); + assertFalse(Date.isValidDate("06-08")); + assertFalse(Date.isValidDate("6 Aug")); + assertFalse(Date.isValidDate("6 Aug 2020")); + + // valid date + assertTrue(Date.isValidDate("2023-08-20")); + assertTrue(Date.isValidDate("2023-09-30")); + + } +} diff --git a/src/test/java/seedu/waddle/model/itinerary/DescriptionTest.java b/src/test/java/seedu/waddle/model/itinerary/DescriptionTest.java new file mode 100644 index 00000000000..30746268939 --- /dev/null +++ b/src/test/java/seedu/waddle/model/itinerary/DescriptionTest.java @@ -0,0 +1,39 @@ +package seedu.waddle.model.itinerary; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class DescriptionTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Description(null)); + } + + @Test + public void constructor_invalidName_throwsIllegalArgumentException() { + String invalidName = ""; + assertThrows(IllegalArgumentException.class, () -> new Description(invalidName)); + } + + @Test + public void isValidName() { + // null name + assertThrows(NullPointerException.class, () -> Description.isValidDescription(null)); + + // invalid name + assertFalse(Description.isValidDescription("")); // empty string + assertFalse(Description.isValidDescription("^")); // only non-alphanumeric characters + assertFalse(Description.isValidDescription("summer*")); // contains non-alphanumeric characters + + // valid name + assertTrue(Description.isValidDescription("summer trip")); // alphabets only + assertTrue(Description.isValidDescription("12345")); // numbers only + assertTrue(Description.isValidDescription("2nd grad trip")); // alphanumeric characters + assertTrue(Description.isValidDescription("Capital City Trip")); // with capital letters + assertTrue(Description.isValidDescription("a wonderful trip with my beloved friends")); // long names + } +} diff --git a/src/test/java/seedu/waddle/model/itinerary/ItineraryTest.java b/src/test/java/seedu/waddle/model/itinerary/ItineraryTest.java new file mode 100644 index 00000000000..7a5a82e1141 --- /dev/null +++ b/src/test/java/seedu/waddle/model/itinerary/ItineraryTest.java @@ -0,0 +1,98 @@ +package seedu.waddle.model.itinerary; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COUNTRY_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITINERARY_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PEOPLE_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_START_DATE_WINTER; +import static seedu.waddle.testutil.TypicalItineraries.AUTUMN; +import static seedu.waddle.testutil.TypicalItineraries.SUMMER; +import static seedu.waddle.testutil.TypicalItineraries.WINTER; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.testutil.ItineraryBuilder; + +public class ItineraryTest { + + @Test + public void isSameItinerary() { + // same object -> returns true + assertTrue(SUMMER.isSameItinerary(SUMMER)); + + // null -> returns false + assertFalse(SUMMER.isSameItinerary(null)); + + // same name, all other attributes different -> returns true + Itinerary editedSummer = new ItineraryBuilder(SUMMER) + .withCountry(VALID_COUNTRY_WINTER).withStartDate(VALID_START_DATE_WINTER) + .withDuration(VALID_DURATION_WINTER).withPeople(VALID_PEOPLE_WINTER).build(); + assertTrue(SUMMER.isSameItinerary(editedSummer)); + + // different name, all other attributes same -> returns false + editedSummer = new ItineraryBuilder(SUMMER).withDescription(VALID_ITINERARY_DESC_WINTER).build(); + assertFalse(SUMMER.isSameItinerary(editedSummer)); + + // name differs in case, all other attributes same -> returns false + Itinerary editedWinter = new ItineraryBuilder(WINTER).withDescription(VALID_ITINERARY_DESC_WINTER + .toLowerCase()).build(); + assertFalse(WINTER.isSameItinerary(editedWinter)); + + // name has trailing spaces, all other attributes same -> returns false + String nameWithTrailingSpaces = VALID_ITINERARY_DESC_WINTER + " "; + editedWinter = new ItineraryBuilder(WINTER).withDescription(nameWithTrailingSpaces).build(); + assertFalse(WINTER.isSameItinerary(editedWinter)); + } + + @Test + public void getVacantSlots_correctOutput() { + String expectedString = "Day 1:" + System.lineSeparator() + + " Free!" + System.lineSeparator() + System.lineSeparator() + + "Day 2:" + System.lineSeparator() + + " Free!" + System.lineSeparator() + System.lineSeparator(); + String actualString = new ItineraryBuilder(AUTUMN).withDuration("2").build().getVacantSlots(); + assertEquals(expectedString, actualString); + } + + @Test + public void equals() { + // same values -> returns true + Itinerary summerCopy = new ItineraryBuilder(SUMMER).build(); + assertTrue(SUMMER.equals(summerCopy)); + + // same object -> returns true + assertTrue(SUMMER.equals(SUMMER)); + + // null -> returns false + assertFalse(SUMMER.equals(null)); + + // different type -> returns false + assertFalse(SUMMER.equals(5)); + + // different itinerary -> returns false + assertFalse(SUMMER.equals(WINTER)); + + // different name -> returns false + Itinerary editedSummer = new ItineraryBuilder(SUMMER).withDescription(VALID_ITINERARY_DESC_WINTER).build(); + assertFalse(SUMMER.equals(editedSummer)); + + // different country -> returns false + editedSummer = new ItineraryBuilder(SUMMER).withCountry(VALID_COUNTRY_WINTER).build(); + assertFalse(SUMMER.equals(editedSummer)); + + // different start date -> returns false + editedSummer = new ItineraryBuilder(SUMMER).withStartDate(VALID_START_DATE_WINTER).build(); + assertFalse(SUMMER.equals(editedSummer)); + + // different end date -> returns false + editedSummer = new ItineraryBuilder(SUMMER).withDuration(VALID_DURATION_WINTER).build(); + assertFalse(SUMMER.equals(editedSummer)); + + // different people -> returns false + editedSummer = new ItineraryBuilder(SUMMER).withPeople(VALID_PEOPLE_WINTER).build(); + assertFalse(SUMMER.equals(editedSummer)); + } +} diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/seedu/waddle/model/itinerary/NameContainsKeywordsPredicateTest.java similarity index 64% rename from src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java rename to src/test/java/seedu/waddle/model/itinerary/NameContainsKeywordsPredicateTest.java index f136664e017..cc6527fe94a 100644 --- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java +++ b/src/test/java/seedu/waddle/model/itinerary/NameContainsKeywordsPredicateTest.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.waddle.model.itinerary; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test; -import seedu.address.testutil.PersonBuilder; +import seedu.waddle.testutil.ItineraryBuilder; public class NameContainsKeywordsPredicateTest { @@ -34,42 +34,43 @@ public void equals() { // null -> returns false assertFalse(firstPredicate.equals(null)); - // different person -> returns false + // different itinerary -> returns false assertFalse(firstPredicate.equals(secondPredicate)); } @Test public void test_nameContainsKeywords_returnsTrue() { // One keyword - NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.singletonList("Alice")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); + NameContainsKeywordsPredicate predicate = + new NameContainsKeywordsPredicate(Collections.singletonList("Summer")); + assertTrue(predicate.test(new ItineraryBuilder().withDescription("Summer Winter").build())); // Multiple keywords - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); + predicate = new NameContainsKeywordsPredicate(Arrays.asList("Summer", "Winter")); + assertTrue(predicate.test(new ItineraryBuilder().withDescription("Summer Winter").build())); // Only one matching keyword - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Bob", "Carol")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Carol").build())); + predicate = new NameContainsKeywordsPredicate(Arrays.asList("Winter", "Carol")); + assertTrue(predicate.test(new ItineraryBuilder().withDescription("Summer Carol").build())); // Mixed-case keywords - predicate = new NameContainsKeywordsPredicate(Arrays.asList("aLIce", "bOB")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); + predicate = new NameContainsKeywordsPredicate(Arrays.asList("sUMmer", "winTEr")); + assertTrue(predicate.test(new ItineraryBuilder().withDescription("Summer Winter").build())); } @Test public void test_nameDoesNotContainKeywords_returnsFalse() { // Zero keywords NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.emptyList()); - assertFalse(predicate.test(new PersonBuilder().withName("Alice").build())); + assertFalse(predicate.test(new ItineraryBuilder().withDescription("Summer").build())); // Non-matching keyword predicate = new NameContainsKeywordsPredicate(Arrays.asList("Carol")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice Bob").build())); + assertFalse(predicate.test(new ItineraryBuilder().withDescription("Summer Winter").build())); // Keywords match phone, email and address, but does not match name - predicate = new NameContainsKeywordsPredicate(Arrays.asList("12345", "alice@email.com", "Main", "Street")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345") - .withEmail("alice@email.com").withAddress("Main Street").build())); + predicate = new NameContainsKeywordsPredicate(Arrays.asList("Singapore", "2023-06-30", "2023-07-30", "5")); + assertFalse(predicate.test(new ItineraryBuilder().withDescription("Summer").withCountry("Singapore") + .withStartDate("2023-06-30").withDuration("20").withPeople("5").build())); } } diff --git a/src/test/java/seedu/waddle/model/itinerary/PeopleTest.java b/src/test/java/seedu/waddle/model/itinerary/PeopleTest.java new file mode 100644 index 00000000000..4a4963bd9ba --- /dev/null +++ b/src/test/java/seedu/waddle/model/itinerary/PeopleTest.java @@ -0,0 +1,37 @@ +package seedu.waddle.model.itinerary; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class PeopleTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new People(null)); + } + + @Test + public void constructor_invalidPeople_throwsIllegalArgumentException() { + String invalidPeople = ""; + assertThrows(IllegalArgumentException.class, () -> new People(invalidPeople)); + } + + @Test + public void isValidPeople() { + // null people + assertThrows(NullPointerException.class, () -> People.isValidPeople(null)); + + // invalid people + assertFalse(People.isValidPeople("")); // empty string + assertFalse(People.isValidPeople(" ")); // spaces only + assertFalse(People.isValidPeople("one")); + assertFalse(People.isValidPeople("seven")); + + // valid people + assertTrue(People.isValidPeople("2")); + assertTrue(People.isValidPeople("19")); + } +} diff --git a/src/test/java/seedu/waddle/model/itinerary/UniqueItineraryListTest.java b/src/test/java/seedu/waddle/model/itinerary/UniqueItineraryListTest.java new file mode 100644 index 00000000000..7e989b87715 --- /dev/null +++ b/src/test/java/seedu/waddle/model/itinerary/UniqueItineraryListTest.java @@ -0,0 +1,171 @@ +package seedu.waddle.model.itinerary; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COUNTRY_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PEOPLE_WINTER; +import static seedu.waddle.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.TypicalItineraries.SUMMER; +import static seedu.waddle.testutil.TypicalItineraries.WINTER; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.model.itinerary.exceptions.DuplicateItineraryException; +import seedu.waddle.model.itinerary.exceptions.ItineraryNotFoundException; +import seedu.waddle.testutil.ItineraryBuilder; + +public class UniqueItineraryListTest { + + private final UniqueItineraryList uniqueItineraryList = new UniqueItineraryList(); + + @Test + public void contains_nullItinerary_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueItineraryList.contains(null)); + } + + @Test + public void contains_itineraryNotInList_returnsFalse() { + assertFalse(uniqueItineraryList.contains(SUMMER)); + } + + @Test + public void contains_itineraryInList_returnsTrue() { + uniqueItineraryList.add(SUMMER); + assertTrue(uniqueItineraryList.contains(SUMMER)); + } + + @Test + public void contains_itineraryWithSameIdentityFieldsInList_returnsTrue() { + uniqueItineraryList.add(SUMMER); + Itinerary editedSummer = new ItineraryBuilder(SUMMER).withCountry(VALID_COUNTRY_WINTER) + .withPeople(VALID_PEOPLE_WINTER).build(); + assertTrue(uniqueItineraryList.contains(editedSummer)); + } + + @Test + public void add_nullItinerary_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueItineraryList.add(null)); + } + + @Test + public void add_duplicateItinerary_throwsDuplicateItineraryException() { + uniqueItineraryList.add(SUMMER); + assertThrows(DuplicateItineraryException.class, () -> uniqueItineraryList.add(SUMMER)); + } + + @Test + public void setItinerary_nullTargetItinerary_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueItineraryList.setItinerary(null, SUMMER)); + } + + @Test + public void setItinerary_nullEditedItinerary_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueItineraryList.setItinerary(SUMMER, null)); + } + + @Test + public void setItinerary_targetItineraryNotInList_throwsItineraryNotFoundException() { + assertThrows(ItineraryNotFoundException.class, () -> uniqueItineraryList.setItinerary(SUMMER, SUMMER)); + } + + @Test + public void setItinerary_editedItineraryIsSameItinerary_success() { + uniqueItineraryList.add(SUMMER); + uniqueItineraryList.setItinerary(SUMMER, SUMMER); + UniqueItineraryList expectedUniqueItineraryList = new UniqueItineraryList(); + expectedUniqueItineraryList.add(SUMMER); + assertEquals(expectedUniqueItineraryList, uniqueItineraryList); + } + + @Test + public void setItinerary_editedItineraryHasSameIdentity_success() { + uniqueItineraryList.add(SUMMER); + Itinerary editedSummer = new ItineraryBuilder(SUMMER).withCountry(VALID_COUNTRY_WINTER) + .withPeople(VALID_PEOPLE_WINTER).build(); + uniqueItineraryList.setItinerary(SUMMER, editedSummer); + UniqueItineraryList expectedUniqueItineraryList = new UniqueItineraryList(); + expectedUniqueItineraryList.add(editedSummer); + assertEquals(expectedUniqueItineraryList, uniqueItineraryList); + } + + @Test + public void setItinerary_editedItineraryHasDifferentIdentity_success() { + uniqueItineraryList.add(SUMMER); + uniqueItineraryList.setItinerary(SUMMER, WINTER); + UniqueItineraryList expectedUniqueItineraryList = new UniqueItineraryList(); + expectedUniqueItineraryList.add(WINTER); + assertEquals(expectedUniqueItineraryList, uniqueItineraryList); + } + + @Test + public void setItinerary_editedItineraryHasNonUniqueIdentity_throwsDuplicateItineraryException() { + uniqueItineraryList.add(SUMMER); + uniqueItineraryList.add(WINTER); + assertThrows(DuplicateItineraryException.class, () -> uniqueItineraryList.setItinerary(SUMMER, WINTER)); + } + + @Test + public void remove_nullItinerary_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueItineraryList.remove(null)); + } + + @Test + public void remove_itineraryDoesNotExist_throwsItineraryNotFoundException() { + assertThrows(ItineraryNotFoundException.class, () -> uniqueItineraryList.remove(SUMMER)); + } + + @Test + public void remove_existingItinerary_removesItinerary() { + uniqueItineraryList.add(SUMMER); + uniqueItineraryList.remove(SUMMER); + UniqueItineraryList expectedUniqueItineraryList = new UniqueItineraryList(); + assertEquals(expectedUniqueItineraryList, uniqueItineraryList); + } + + @Test + public void setItinerary_nullUniqueItineraryList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueItineraryList.setItineraries((UniqueItineraryList) null)); + } + + @Test + public void setItinerary_uniqueItineraryList_replacesOwnListWithProvidedUniqueItineraryList() { + uniqueItineraryList.add(SUMMER); + UniqueItineraryList expectedUniqueItineraryList = new UniqueItineraryList(); + expectedUniqueItineraryList.add(WINTER); + uniqueItineraryList.setItineraries(expectedUniqueItineraryList); + assertEquals(expectedUniqueItineraryList, uniqueItineraryList); + } + + @Test + public void setItinerary_nullList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueItineraryList.setItineraries((List) null)); + } + + @Test + public void setItinerary_list_replacesOwnListWithProvidedList() { + uniqueItineraryList.add(SUMMER); + List itineraryList = Collections.singletonList(WINTER); + uniqueItineraryList.setItineraries(itineraryList); + UniqueItineraryList expectedUniqueItineraryList = new UniqueItineraryList(); + expectedUniqueItineraryList.add(WINTER); + assertEquals(expectedUniqueItineraryList, uniqueItineraryList); + } + + @Test + public void setItinerary_listWithDuplicateItinerary_throwsDuplicateItineraryException() { + List listWithDuplicateItinerary = Arrays.asList(SUMMER, SUMMER); + assertThrows(DuplicateItineraryException.class, () + -> uniqueItineraryList.setItineraries(listWithDuplicateItinerary)); + } + + @Test + public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () + -> uniqueItineraryList.asUnmodifiableObservableList().remove(0)); + } +} diff --git a/src/test/java/seedu/waddle/storage/JsonAdaptedItemTest.java b/src/test/java/seedu/waddle/storage/JsonAdaptedItemTest.java new file mode 100644 index 00000000000..1e9de90a60d --- /dev/null +++ b/src/test/java/seedu/waddle/storage/JsonAdaptedItemTest.java @@ -0,0 +1,121 @@ +package seedu.waddle.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.waddle.storage.JsonAdaptedItem.MISSING_FIELD_MESSAGE_FORMAT; +import static seedu.waddle.testutil.Assert.assertThrows; + +import java.time.format.DateTimeParseException; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.exceptions.IllegalValueException; +import seedu.waddle.model.item.Cost; +import seedu.waddle.model.item.Duration; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.item.Priority; +import seedu.waddle.model.itinerary.Date; +import seedu.waddle.model.itinerary.Description; +import seedu.waddle.testutil.TypicalItems; + +public class JsonAdaptedItemTest { + private static final String INVALID_DESC = "Exchange $$$"; + private static final int INVALID_PRIORITY = 0; + private static final String INVALID_COST = "-4"; + private static final String INVALID_DURATION = "1 week"; + private static final String INVALID_START_TIME = "12.00"; + + private static final String VALID_DESC = TypicalItems.getArt().getDescription().toString(); + private static final int VALID_PRIORITY = TypicalItems.getArt().getPriority().getValue(); + private static final String VALID_COST = TypicalItems.getArt().getCost().toString(); + private static final String VALID_DURATION = TypicalItems.getArt().getDuration().toString(); + private static final String VALID_START_TIME = "10:30"; + + + @Test + public void toModelType_validItemDetails_returnsItem() throws IllegalValueException { + Item itemModel = TypicalItems.getArt(); + JsonAdaptedItem item = new JsonAdaptedItem(itemModel); + assertEquals(itemModel, item.toModelType()); + } + + @Test + public void toModelType_invalidDescription_throwsIllegalValueException() { + JsonAdaptedItem item = new JsonAdaptedItem(INVALID_DESC, VALID_PRIORITY, VALID_COST, + VALID_DURATION, VALID_START_TIME); + String expectedMessage = Description.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, item::toModelType); + } + + @Test + public void toModelType_nullDescription_throwsIllegalValueException() { + JsonAdaptedItem item = new JsonAdaptedItem(null, VALID_PRIORITY, VALID_COST, + VALID_DURATION, VALID_START_TIME); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Description.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, item::toModelType); + } + + @Test + public void toModelType_invalidPriority_throwsIllegalValueException() { + JsonAdaptedItem item = new JsonAdaptedItem(VALID_DESC, INVALID_PRIORITY, VALID_COST, + VALID_DURATION, VALID_START_TIME); + String expectedMessage = Priority.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, item::toModelType); + } + + @Test + public void toModelType_nullPriority_throwsIllegalValueException() { + JsonAdaptedItem item = new JsonAdaptedItem(VALID_DESC, null, VALID_COST, + VALID_DURATION, VALID_START_TIME); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Priority.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, item::toModelType); + } + + @Test + public void toModelType_invalidCost_throwsIllegalValueException() { + JsonAdaptedItem item = new JsonAdaptedItem(VALID_DESC, VALID_PRIORITY, INVALID_COST, + VALID_DURATION, VALID_START_TIME); + String expectedMessage = Cost.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, item::toModelType); + } + + @Test + public void toModelType_nullCost_throwsIllegalValueException() { + JsonAdaptedItem item = new JsonAdaptedItem(VALID_DESC, VALID_PRIORITY, null, + VALID_DURATION, VALID_START_TIME); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Cost.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, item::toModelType); + } + + @Test + public void toModelType_invalidDuration_throwsIllegalValueException() { + JsonAdaptedItem item = new JsonAdaptedItem(VALID_DESC, VALID_PRIORITY, VALID_COST, + INVALID_DURATION, VALID_START_TIME); + String expectedMessage = Duration.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, item::toModelType); + } + + @Test + public void toModelType_nullDuration_throwsIllegalValueException() { + JsonAdaptedItem item = new JsonAdaptedItem(VALID_DESC, VALID_PRIORITY, VALID_COST, + null, VALID_START_TIME); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Duration.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, item::toModelType); + } + + @Test + public void toModelType_invalidStartTime_throwsDateTimeParseException() { + JsonAdaptedItem item = new JsonAdaptedItem(VALID_DESC, VALID_PRIORITY, VALID_COST, + VALID_DURATION, INVALID_START_TIME); + String expectedMessage = Date.MESSAGE_CONSTRAINTS; + assertThrows(DateTimeParseException.class, item::toModelType); + } + + @Test + public void toModelType_nullStartTime_returnsItem() throws IllegalValueException { + JsonAdaptedItem item = new JsonAdaptedItem(VALID_DESC, VALID_PRIORITY, VALID_COST, + VALID_DURATION, null); + Item itemModel = TypicalItems.getArt(); + assertEquals(itemModel, item.toModelType()); + } + +} diff --git a/src/test/java/seedu/waddle/storage/JsonAdaptedItineraryTest.java b/src/test/java/seedu/waddle/storage/JsonAdaptedItineraryTest.java new file mode 100644 index 00000000000..c740cd25bfb --- /dev/null +++ b/src/test/java/seedu/waddle/storage/JsonAdaptedItineraryTest.java @@ -0,0 +1,124 @@ +package seedu.waddle.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.waddle.storage.JsonAdaptedItinerary.MISSING_FIELD_MESSAGE_FORMAT; +import static seedu.waddle.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.TypicalItineraries.SUMMER; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.exceptions.IllegalValueException; +import seedu.waddle.model.itinerary.Country; +import seedu.waddle.model.itinerary.Date; +import seedu.waddle.model.itinerary.Description; +import seedu.waddle.model.itinerary.ItineraryDuration; +import seedu.waddle.model.itinerary.People; + +public class JsonAdaptedItineraryTest { + private static final String INVALID_NAME = "S@mmer Trip"; + private static final String INVALID_COUNTRY = "Austr@lia"; + private static final String INVALID_START_DATE = "1-2-3"; + private static final String INVALID_DURATION = "1 week"; + private static final String INVALID_PEOPLE = "three"; + private static final String INVALID_BUDGET = "$4000"; + + private static final String VALID_NAME = SUMMER.getDescription().toString(); + private static final String VALID_COUNTRY = SUMMER.getCountry().toString(); + private static final String VALID_START_DATE = SUMMER.getStartDate().toString(); + private static final String VALID_DURATION = SUMMER.getDuration().toString(); + private static final String VALID_PEOPLE = SUMMER.getPeople().toString(); + private static final String VALID_BUDGET = SUMMER.getBudget().toString(); + private static final List VALID_EMPTY_ITEM_LIST = new ArrayList<>(); + private static final List VALID_EMPTY_DAY_LIST = new ArrayList<>(); + + + @Test + public void toModelType_validItineraryDetails_returnsItinerary() throws Exception { + JsonAdaptedItinerary itinerary = new JsonAdaptedItinerary(SUMMER); + assertEquals(SUMMER, itinerary.toModelType()); + } + + @Test + public void toModelType_invalidName_throwsIllegalValueException() { + JsonAdaptedItinerary itinerary = new JsonAdaptedItinerary(INVALID_NAME, VALID_COUNTRY, VALID_START_DATE, + VALID_DURATION, VALID_PEOPLE, VALID_BUDGET, VALID_EMPTY_ITEM_LIST, VALID_EMPTY_DAY_LIST); + String expectedMessage = Description.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, itinerary::toModelType); + } + + @Test + public void toModelType_nullName_throwsIllegalValueException() { + JsonAdaptedItinerary itinerary = new JsonAdaptedItinerary(null, VALID_COUNTRY, VALID_START_DATE, + VALID_DURATION, VALID_PEOPLE, VALID_BUDGET, VALID_EMPTY_ITEM_LIST, VALID_EMPTY_DAY_LIST); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Description.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, itinerary::toModelType); + } + + @Test + public void toModelType_invalidCountry_throwsIllegalValueException() { + JsonAdaptedItinerary itinerary = new JsonAdaptedItinerary(VALID_NAME, INVALID_COUNTRY, VALID_START_DATE, + VALID_DURATION, VALID_PEOPLE, VALID_BUDGET, VALID_EMPTY_ITEM_LIST, VALID_EMPTY_DAY_LIST); + String expectedMessage = Country.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, itinerary::toModelType); + } + + @Test + public void toModelType_nullCountry_throwsIllegalValueException() { + JsonAdaptedItinerary itinerary = new JsonAdaptedItinerary(VALID_NAME, null, VALID_START_DATE, + VALID_DURATION, VALID_PEOPLE, VALID_BUDGET, VALID_EMPTY_ITEM_LIST, VALID_EMPTY_DAY_LIST); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Country.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, itinerary::toModelType); + } + + @Test + public void toModelType_invalidStartDate_throwsIllegalValueException() { + JsonAdaptedItinerary itinerary = new JsonAdaptedItinerary(VALID_NAME, VALID_COUNTRY, INVALID_START_DATE, + VALID_DURATION, VALID_PEOPLE, VALID_BUDGET, VALID_EMPTY_ITEM_LIST, VALID_EMPTY_DAY_LIST); + String expectedMessage = Date.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, itinerary::toModelType); + } + + @Test + public void toModelType_nullStartDate_throwsIllegalValueException() { + JsonAdaptedItinerary itinerary = new JsonAdaptedItinerary(VALID_NAME, VALID_COUNTRY, null, + VALID_DURATION, VALID_PEOPLE, VALID_BUDGET, VALID_EMPTY_ITEM_LIST, VALID_EMPTY_DAY_LIST); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Date.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, itinerary::toModelType); + } + + @Test + public void toModelType_invalidDuration_throwsIllegalValueException() { + JsonAdaptedItinerary itinerary = + new JsonAdaptedItinerary(VALID_NAME, VALID_COUNTRY, VALID_START_DATE, INVALID_DURATION, VALID_PEOPLE, + VALID_BUDGET, VALID_EMPTY_ITEM_LIST, VALID_EMPTY_DAY_LIST); + String expectedMessage = Date.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, itinerary::toModelType); + } + + @Test + public void toModelType_nullDuration_throwsIllegalValueException() { + JsonAdaptedItinerary itinerary = new JsonAdaptedItinerary(VALID_NAME, VALID_COUNTRY, VALID_START_DATE, + null, VALID_PEOPLE, VALID_BUDGET, VALID_EMPTY_ITEM_LIST, VALID_EMPTY_DAY_LIST); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, ItineraryDuration.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, itinerary::toModelType); + } + + @Test + public void toModelType_invalidPeople_throwsIllegalValueException() { + JsonAdaptedItinerary itinerary = new JsonAdaptedItinerary(VALID_NAME, VALID_COUNTRY, VALID_START_DATE, + VALID_DURATION, INVALID_PEOPLE, VALID_BUDGET, VALID_EMPTY_ITEM_LIST, VALID_EMPTY_DAY_LIST); + String expectedMessage = People.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, itinerary::toModelType); + } + + @Test + public void toModelType_nullPeople_throwsIllegalValueException() { + JsonAdaptedItinerary itinerary = new JsonAdaptedItinerary(VALID_NAME, VALID_COUNTRY, VALID_START_DATE, + VALID_DURATION, null, VALID_BUDGET, VALID_EMPTY_ITEM_LIST, VALID_EMPTY_DAY_LIST); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, People.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, itinerary::toModelType); + } +} diff --git a/src/test/java/seedu/waddle/storage/JsonSerializableWaddleTest.java b/src/test/java/seedu/waddle/storage/JsonSerializableWaddleTest.java new file mode 100644 index 00000000000..9f4807d8b30 --- /dev/null +++ b/src/test/java/seedu/waddle/storage/JsonSerializableWaddleTest.java @@ -0,0 +1,47 @@ +package seedu.waddle.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.waddle.testutil.Assert.assertThrows; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +import seedu.waddle.commons.exceptions.IllegalValueException; +import seedu.waddle.commons.util.JsonUtil; +import seedu.waddle.model.Waddle; +import seedu.waddle.testutil.TypicalItineraries; + +public class JsonSerializableWaddleTest { + + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonSerializableWaddleTest"); + private static final Path TYPICAL_ITINERARIES_FILE = TEST_DATA_FOLDER.resolve("typicalItinerariesWaddle.json"); + private static final Path INVALID_ITINERARY_FILE = TEST_DATA_FOLDER.resolve("invalidItineraryWaddle.json"); + private static final Path DUPLICATE_ITINERARY_FILE = TEST_DATA_FOLDER.resolve("duplicateItineraryWaddle.json"); + + @Test + public void toModelType_typicalItinerariesFile_success() throws Exception { + JsonSerializableWaddle dataFromFile = JsonUtil.readJsonFile(TYPICAL_ITINERARIES_FILE, + JsonSerializableWaddle.class).get(); + Waddle waddleFromFile = dataFromFile.toModelType(); + Waddle typicalItinerariesWaddle = TypicalItineraries.getTypicalWaddle(); + assertEquals(waddleFromFile, typicalItinerariesWaddle); + } + + @Test + public void toModelType_invalidItineraryFile_throwsIllegalValueException() throws Exception { + JsonSerializableWaddle dataFromFile = JsonUtil.readJsonFile(INVALID_ITINERARY_FILE, + JsonSerializableWaddle.class).get(); + assertThrows(IllegalValueException.class, dataFromFile::toModelType); + } + + @Test + public void toModelType_duplicateItinerary_throwsIllegalValueException() throws Exception { + JsonSerializableWaddle dataFromFile = JsonUtil.readJsonFile(DUPLICATE_ITINERARY_FILE, + JsonSerializableWaddle.class).get(); + assertThrows(IllegalValueException.class, JsonSerializableWaddle.MESSAGE_DUPLICATE_ITINERARY, + dataFromFile::toModelType); + } + +} diff --git a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java b/src/test/java/seedu/waddle/storage/JsonUserPrefsStorageTest.java similarity index 93% rename from src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java rename to src/test/java/seedu/waddle/storage/JsonUserPrefsStorageTest.java index 16f33f4a6bb..ef1972328a9 100644 --- a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java +++ b/src/test/java/seedu/waddle/storage/JsonUserPrefsStorageTest.java @@ -1,8 +1,8 @@ -package seedu.address.storage; +package seedu.waddle.storage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.Assert.assertThrows; import java.io.IOException; import java.nio.file.Path; @@ -12,9 +12,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.UserPrefs; +import seedu.waddle.commons.core.GuiSettings; +import seedu.waddle.commons.exceptions.DataConversionException; +import seedu.waddle.model.UserPrefs; public class JsonUserPrefsStorageTest { @@ -73,7 +73,7 @@ public void readUserPrefs_extraValuesInFile_extraValuesIgnored() throws DataConv private UserPrefs getTypicalUserPrefs() { UserPrefs userPrefs = new UserPrefs(); userPrefs.setGuiSettings(new GuiSettings(1000, 500, 300, 100)); - userPrefs.setAddressBookFilePath(Paths.get("addressbook.json")); + userPrefs.setWaddleFilePath(Paths.get("data/waddle.json")); return userPrefs; } diff --git a/src/test/java/seedu/waddle/storage/JsonWaddleStorageTest.java b/src/test/java/seedu/waddle/storage/JsonWaddleStorageTest.java new file mode 100644 index 00000000000..addbf777cf6 --- /dev/null +++ b/src/test/java/seedu/waddle/storage/JsonWaddleStorageTest.java @@ -0,0 +1,110 @@ +package seedu.waddle.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static seedu.waddle.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.TypicalItineraries.SPRING; +import static seedu.waddle.testutil.TypicalItineraries.SUMMER; +import static seedu.waddle.testutil.TypicalItineraries.WINTER; +import static seedu.waddle.testutil.TypicalItineraries.getTypicalWaddle; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import seedu.waddle.commons.exceptions.DataConversionException; +import seedu.waddle.model.ReadOnlyWaddle; +import seedu.waddle.model.Waddle; + +public class JsonWaddleStorageTest { + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonWaddleStorageTest"); + + @TempDir + public Path testFolder; + + @Test + public void readWaddle_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> readWaddle(null)); + } + + private java.util.Optional readWaddle(String filePath) throws Exception { + return new JsonWaddleStorage(Paths.get(filePath)).readWaddle(addToTestDataPathIfNotNull(filePath)); + } + + private Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { + return prefsFileInTestDataFolder != null + ? TEST_DATA_FOLDER.resolve(prefsFileInTestDataFolder) + : null; + } + + @Test + public void read_missingFile_emptyResult() throws Exception { + assertFalse(readWaddle("NonExistentFile.json").isPresent()); + } + + @Test + public void read_notJsonFormat_exceptionThrown() { + assertThrows(DataConversionException.class, () -> readWaddle("notJsonFormatWaddle.json")); + } + + @Test + public void readWaddle_invalidItineraryWaddle_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readWaddle("invalidItineraryWaddle.json")); + } + + @Test + public void readWaddle_invalidAndValidItineraryWaddle_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readWaddle("invalidAndValidItineraryWaddle.json")); + } + + @Test + public void readAndSaveWaddle_allInOrder_success() throws Exception { + Path filePath = testFolder.resolve("TempWaddle.json"); + Waddle original = getTypicalWaddle(); + JsonWaddleStorage jsonWaddleStorage = new JsonWaddleStorage(filePath); + + // Save in new file and read back + jsonWaddleStorage.saveWaddle(original, filePath); + ReadOnlyWaddle readBack = jsonWaddleStorage.readWaddle(filePath).get(); + assertEquals(original, new Waddle(readBack)); + + // Modify data, overwrite exiting file, and read back + original.addItinerary(SUMMER); + original.removeItinerary(SPRING); + jsonWaddleStorage.saveWaddle(original, filePath); + readBack = jsonWaddleStorage.readWaddle(filePath).get(); + assertEquals(original, new Waddle(readBack)); + + // Save and read without specifying file path + original.addItinerary(WINTER); + jsonWaddleStorage.saveWaddle(original); // file path not specified + readBack = jsonWaddleStorage.readWaddle().get(); // file path not specified + assertEquals(original, new Waddle(readBack)); + + } + + @Test + public void saveWaddle_nullWaddle_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveWaddle(null, "SomeFile.json")); + } + + /** + * Saves {@code addressBook} at the specified {@code filePath}. + */ + private void saveWaddle(ReadOnlyWaddle waddle, String filePath) { + try { + new JsonWaddleStorage(Paths.get(filePath)) + .saveWaddle(waddle, addToTestDataPathIfNotNull(filePath)); + } catch (IOException ioe) { + throw new AssertionError("There should not be an error writing to the file.", ioe); + } + } + + @Test + public void saveWaddle_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveWaddle(new Waddle(), null)); + } +} diff --git a/src/test/java/seedu/address/storage/StorageManagerTest.java b/src/test/java/seedu/waddle/storage/StorageManagerTest.java similarity index 60% rename from src/test/java/seedu/address/storage/StorageManagerTest.java rename to src/test/java/seedu/waddle/storage/StorageManagerTest.java index 99a16548970..ab5f840c795 100644 --- a/src/test/java/seedu/address/storage/StorageManagerTest.java +++ b/src/test/java/seedu/waddle/storage/StorageManagerTest.java @@ -1,8 +1,8 @@ -package seedu.address.storage; +package seedu.waddle.storage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.waddle.testutil.TypicalItineraries.getTypicalWaddle; import java.nio.file.Path; @@ -10,10 +10,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; +import seedu.waddle.commons.core.GuiSettings; +import seedu.waddle.model.ReadOnlyWaddle; +import seedu.waddle.model.UserPrefs; +import seedu.waddle.model.Waddle; public class StorageManagerTest { @@ -24,9 +24,9 @@ public class StorageManagerTest { @BeforeEach public void setUp() { - JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(getTempFilePath("ab")); + JsonWaddleStorage waddleStorage = new JsonWaddleStorage(getTempFilePath("waddle")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(getTempFilePath("prefs")); - storageManager = new StorageManager(addressBookStorage, userPrefsStorage); + storageManager = new StorageManager(waddleStorage, userPrefsStorage); } private Path getTempFilePath(String fileName) { @@ -48,21 +48,21 @@ public void prefsReadSave() throws Exception { } @Test - public void addressBookReadSave() throws Exception { + public void waddleReadSave() throws Exception { /* * Note: This is an integration test that verifies the StorageManager is properly wired to the - * {@link JsonAddressBookStorage} class. - * More extensive testing of UserPref saving/reading is done in {@link JsonAddressBookStorageTest} class. + * {@link JsonWaddleStorage} class. + * More extensive testing of UserPref saving/reading is done in {@link JsonWaddleStorageTest} class. */ - AddressBook original = getTypicalAddressBook(); - storageManager.saveAddressBook(original); - ReadOnlyAddressBook retrieved = storageManager.readAddressBook().get(); - assertEquals(original, new AddressBook(retrieved)); + Waddle original = getTypicalWaddle(); + storageManager.saveWaddle(original); + ReadOnlyWaddle retrieved = storageManager.readWaddle().get(); + assertEquals(original, new Waddle(retrieved)); } @Test - public void getAddressBookFilePath() { - assertNotNull(storageManager.getAddressBookFilePath()); + public void getWaddleFilePath() { + assertNotNull(storageManager.getWaddleFilePath()); } } diff --git a/src/test/java/seedu/address/testutil/Assert.java b/src/test/java/seedu/waddle/testutil/Assert.java similarity index 97% rename from src/test/java/seedu/address/testutil/Assert.java rename to src/test/java/seedu/waddle/testutil/Assert.java index 9863093bd6e..f34d073974c 100644 --- a/src/test/java/seedu/address/testutil/Assert.java +++ b/src/test/java/seedu/waddle/testutil/Assert.java @@ -1,4 +1,4 @@ -package seedu.address.testutil; +package seedu.waddle.testutil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.function.Executable; diff --git a/src/test/java/seedu/waddle/testutil/EditItemDescriptorBuilder.java b/src/test/java/seedu/waddle/testutil/EditItemDescriptorBuilder.java new file mode 100644 index 00000000000..7da8873db13 --- /dev/null +++ b/src/test/java/seedu/waddle/testutil/EditItemDescriptorBuilder.java @@ -0,0 +1,82 @@ +package seedu.waddle.testutil; + +import java.time.LocalTime; + +import seedu.waddle.logic.commands.EditItemCommand.EditItemDescriptor; +import seedu.waddle.model.item.Cost; +import seedu.waddle.model.item.Duration; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.item.Priority; +import seedu.waddle.model.itinerary.Description; + +/** + * A utility class to help with building EditItemDescriptor objects. + */ +public class EditItemDescriptorBuilder { + + private EditItemDescriptor descriptor; + + public EditItemDescriptorBuilder() { + descriptor = new EditItemDescriptor(); + } + + public EditItemDescriptorBuilder(EditItemDescriptor descriptor) { + this.descriptor = new EditItemDescriptor(descriptor); + } + + /** + * Returns an {@code EditItemDescriptor} with fields containing an {@code item}'s details + */ + public EditItemDescriptorBuilder(Item item) { + descriptor = new EditItemDescriptor(); + descriptor.setDescription(item.getDescription()); + descriptor.setPriority(item.getPriority()); + descriptor.setCost(item.getCost()); + descriptor.setDuration(item.getDuration()); + descriptor.setStartTime(item.getStartTime()); + } + + /** + * Sets the {@code Description} of the {@code EditItemDescriptor} that we are building. + */ + public EditItemDescriptorBuilder withDescription(String description) { + descriptor.setDescription(new Description(description)); + return this; + } + + /** + * Sets the {@code Priority} of the {@code EditItemDescriptor} that we are building. + */ + public EditItemDescriptorBuilder withPriority(int priority) { + descriptor.setPriority(new Priority(priority)); + return this; + } + + /** + * Sets the {@code Cost} of the {@code EditItemDescriptor} that we are building. + */ + public EditItemDescriptorBuilder withCost(String cost) { + descriptor.setCost(new Cost(cost)); + return this; + } + + /** + * Sets the {@code Duration} of the {@code EditItemDescriptor} that we are building. + */ + public EditItemDescriptorBuilder withDuration(String duration) { + descriptor.setDuration(new Duration(duration)); + return this; + } + + /** + * Sets the {@code StartTime} of the {@code EditItemDescriptor} that we are building. + */ + public EditItemDescriptorBuilder withStartTime(String startTime) { + descriptor.setStartTime(LocalTime.parse(startTime)); + return this; + } + + public EditItemDescriptor build() { + return descriptor; + } +} diff --git a/src/test/java/seedu/waddle/testutil/EditItineraryDescriptorBuilder.java b/src/test/java/seedu/waddle/testutil/EditItineraryDescriptorBuilder.java new file mode 100644 index 00000000000..6f1e5e021f6 --- /dev/null +++ b/src/test/java/seedu/waddle/testutil/EditItineraryDescriptorBuilder.java @@ -0,0 +1,91 @@ +package seedu.waddle.testutil; + +import seedu.waddle.logic.commands.EditCommand.EditItineraryDescriptor; +import seedu.waddle.model.itinerary.Budget; +import seedu.waddle.model.itinerary.Country; +import seedu.waddle.model.itinerary.Date; +import seedu.waddle.model.itinerary.Description; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.model.itinerary.ItineraryDuration; +import seedu.waddle.model.itinerary.People; + +/** + * A utility class to help with building EditItineraryDescriptor objects. + */ +public class EditItineraryDescriptorBuilder { + + private EditItineraryDescriptor descriptor; + + public EditItineraryDescriptorBuilder() { + descriptor = new EditItineraryDescriptor(); + } + + public EditItineraryDescriptorBuilder(EditItineraryDescriptor descriptor) { + this.descriptor = new EditItineraryDescriptor(descriptor); + } + + /** + * Returns an {@code EditItineraryDescriptor} with fields containing an {@code itinerary}'s details + */ + public EditItineraryDescriptorBuilder(Itinerary itinerary) { + descriptor = new EditItineraryDescriptor(); + descriptor.setDescription(itinerary.getDescription()); + descriptor.setCountry(itinerary.getCountry()); + descriptor.setStartDate(itinerary.getStartDate()); + descriptor.setDuration(itinerary.getDuration()); + descriptor.setPeople(itinerary.getPeople()); + descriptor.setBudget(itinerary.getBudget()); + } + + /** + * Sets the {@code Description} of the {@code EditItineraryDescriptor} that we are building. + */ + public EditItineraryDescriptorBuilder withDescription(String description) { + descriptor.setDescription(new Description(description)); + return this; + } + + /** + * Sets the {@code Country} of the {@code EditItineraryDescriptor} that we are building. + */ + public EditItineraryDescriptorBuilder withCountry(String country) { + descriptor.setCountry(new Country(country)); + return this; + } + + /** + * Sets the {@code Start Date} of the {@code EditItineraryDescriptor} that we are building. + */ + public EditItineraryDescriptorBuilder withStartDate(String startDate) { + descriptor.setStartDate(new Date(startDate)); + return this; + } + + /** + * Sets the {@code Duration} of the {@code EditItineraryDescriptor} that we are building. + */ + public EditItineraryDescriptorBuilder withDuration(String duration) { + descriptor.setDuration(new ItineraryDuration(duration)); + return this; + } + + /** + * Sets the {@code People} of the {@code EditItineraryDescriptor} that we are building. + */ + public EditItineraryDescriptorBuilder withPeople(String people) { + descriptor.setPeople(new People(people)); + return this; + } + + /** + * Sets the {@code Budget} of the {@code EditItineraryDescriptor} that we are building. + */ + public EditItineraryDescriptorBuilder withBudget(String budget) { + descriptor.setBudget(new Budget(budget)); + return this; + } + + public EditItineraryDescriptor build() { + return descriptor; + } +} diff --git a/src/test/java/seedu/waddle/testutil/ItemBuilder.java b/src/test/java/seedu/waddle/testutil/ItemBuilder.java new file mode 100644 index 00000000000..dc599763277 --- /dev/null +++ b/src/test/java/seedu/waddle/testutil/ItemBuilder.java @@ -0,0 +1,78 @@ +package seedu.waddle.testutil; + +import seedu.waddle.model.item.Cost; +import seedu.waddle.model.item.Duration; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.item.Priority; +import seedu.waddle.model.itinerary.Description; + +/** + * A utility class to help with building Item objects. + */ +public class ItemBuilder { + public static final String DEFAULT_ITEM_DESC = "Airport"; + public static final String DEFAULT_DURATION = "60"; + public static final String DEFAULT_COST = "100"; + public static final int DEFAULT_PRIORITY = 5; + + private Description description; + private Duration duration; + private Cost cost; + private Priority priority; + + /** + * Creates a {@code ItemBuilder} with the default details. + */ + public ItemBuilder() { + this.description = new Description(DEFAULT_ITEM_DESC); + this.duration = new Duration(DEFAULT_DURATION); + this.cost = new Cost(DEFAULT_COST); + this.priority = new Priority(DEFAULT_PRIORITY); + } + + /** + * Initializes the ItemBuilder with the data of {@code itemToCopy}. + */ + public ItemBuilder(Item itemToCopy) { + description = itemToCopy.getDescription(); + duration = itemToCopy.getDuration(); + cost = itemToCopy.getCost(); + priority = itemToCopy.getPriority(); + } + + /** + * Sets the {@code description} of the {@code Item} that we are building. + */ + public ItemBuilder withDesc(String desc) { + this.description = new Description(desc); + return this; + } + + /** + * Sets the {@code duration} of the {@code Item} that we are building. + */ + public ItemBuilder withDuration(String duration) { + this.duration = new Duration(duration); + return this; + } + + /** + * Sets the {@code cost} of the {@code Item} that we are building. + */ + public ItemBuilder withCost(String cost) { + this.cost = new Cost(cost); + return this; + } + + /** + * Sets the {@code priority} of the {@code Item} that we are building. + */ + public ItemBuilder withPriority(int priority) { + this.priority = new Priority(priority); + return this; + } + + public Item build() { + return new Item(description, priority, cost, duration); + } +} diff --git a/src/test/java/seedu/waddle/testutil/ItineraryBuilder.java b/src/test/java/seedu/waddle/testutil/ItineraryBuilder.java new file mode 100644 index 00000000000..e3d3f641ac1 --- /dev/null +++ b/src/test/java/seedu/waddle/testutil/ItineraryBuilder.java @@ -0,0 +1,105 @@ +package seedu.waddle.testutil; + +import seedu.waddle.model.itinerary.Budget; +import seedu.waddle.model.itinerary.Country; +import seedu.waddle.model.itinerary.Date; +import seedu.waddle.model.itinerary.Description; +import seedu.waddle.model.itinerary.Itinerary; +import seedu.waddle.model.itinerary.ItineraryDuration; +import seedu.waddle.model.itinerary.People; + +/** + * A utility class to help with building Itinerary objects. + */ +public class ItineraryBuilder { + public static final String DEFAULT_NAME = "Summer"; + public static final String DEFAULT_COUNTRY = "FRANCE"; + public static final String DEFAULT_START_DATE = "2023-10-14"; + public static final String DEFAULT_DURATION = "1"; + public static final String DEFAULT_PEOPLE = "1"; + public static final String DEFAULT_BUDGET = "2000.00"; + + private Description description; + private Country country; + private Date startDate; + private ItineraryDuration duration; + private People people; + private Budget budget; + + /** + * Creates a {@code ItineraryBuilder} with the default details. + */ + public ItineraryBuilder() { + description = new Description(DEFAULT_NAME); + country = new Country(DEFAULT_COUNTRY); + startDate = new Date(DEFAULT_START_DATE); + duration = new ItineraryDuration(DEFAULT_DURATION); + people = new People(DEFAULT_PEOPLE); + budget = new Budget(DEFAULT_BUDGET); + } + + /** + * Initializes the ItineraryBuilder with the data of {@code itineraryToCopy}. + */ + public ItineraryBuilder(Itinerary itineraryToCopy) { + description = itineraryToCopy.getDescription(); + country = itineraryToCopy.getCountry(); + startDate = itineraryToCopy.getStartDate(); + duration = itineraryToCopy.getDuration(); + people = itineraryToCopy.getPeople(); + budget = itineraryToCopy.getBudget(); + } + + /** + * Sets the {@code Name} of the {@code Itinerary} that we are building. + */ + public ItineraryBuilder withDescription(String name) { + this.description = new Description(name); + return this; + } + + /** + * Sets the {@code Country} of the {@code Itinerary} that we are building. + */ + public ItineraryBuilder withCountry(String country) { + this.country = new Country(country); + return this; + } + + /** + * Sets the {@code Start Date} of the {@code Itinerary} that we are building. + */ + public ItineraryBuilder withStartDate(String startDate) { + this.startDate = new Date(startDate); + return this; + } + + /** + * Sets the {@code ItineraryDuration} of the {@code Itinerary} that we are building. + */ + public ItineraryBuilder withDuration(String duration) { + this.duration = new ItineraryDuration(duration); + return this; + } + + /** + * Sets the {@code People} of the {@code Itinerary} that we are building. + */ + public ItineraryBuilder withPeople(String people) { + this.people = new People(people); + return this; + } + + /** + * Sets the {@code Budget} of the {@code Itinerary} that we are building. + */ + public ItineraryBuilder withBudget(String budget) { + this.budget = new Budget(budget); + return this; + } + + public Itinerary build() { + return new Itinerary(description, country, startDate, duration, people, budget); + } + +} diff --git a/src/test/java/seedu/waddle/testutil/ItineraryUtil.java b/src/test/java/seedu/waddle/testutil/ItineraryUtil.java new file mode 100644 index 00000000000..f303ee47f8c --- /dev/null +++ b/src/test/java/seedu/waddle/testutil/ItineraryUtil.java @@ -0,0 +1,53 @@ +package seedu.waddle.testutil; + +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_BUDGET; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_COUNTRY; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_ITINERARY_DURATION; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_PEOPLE; +import static seedu.waddle.logic.parser.CliSyntax.PREFIX_START_DATE; + +import seedu.waddle.logic.commands.AddCommand; +import seedu.waddle.logic.commands.EditCommand.EditItineraryDescriptor; +import seedu.waddle.model.itinerary.Itinerary; +/** + * A utility class for Person. + */ +public class ItineraryUtil { + + /** + * Returns an add command string for adding the {@code itinerary}. + */ + public static String getAddCommand(Itinerary itinerary) { + return AddCommand.COMMAND_WORD + " " + getItineraryDetails(itinerary); + } + + /** + * Returns the part of command string for the given {@code itinerary}'s details. + */ + public static String getItineraryDetails(Itinerary itinerary) { + StringBuilder sb = new StringBuilder(); + sb.append(PREFIX_DESCRIPTION + itinerary.getDescription().description + " "); + sb.append(PREFIX_COUNTRY + itinerary.getCountry().country + " "); + sb.append(PREFIX_START_DATE + itinerary.getStartDate().toString() + " "); + sb.append(PREFIX_ITINERARY_DURATION + itinerary.getDuration().toString() + " "); + sb.append(PREFIX_PEOPLE + itinerary.getPeople().numOfPeople + " "); + sb.append(PREFIX_BUDGET + itinerary.getBudget().toString()); + return sb.toString(); + } + + /** + * Returns the part of command string for the given {@code EditItineraryDescriptor}'s details. + */ + public static String getEditItineraryDescriptorDetails(EditItineraryDescriptor descriptor) { + StringBuilder sb = new StringBuilder(); + descriptor.getDescription().ifPresent( + name -> sb.append(PREFIX_DESCRIPTION).append(name.description).append(" ")); + descriptor.getCountry().ifPresent(phone -> sb.append(PREFIX_COUNTRY).append(phone).append(" ")); + descriptor.getStartDate().ifPresent(email -> sb.append(PREFIX_START_DATE).append(email).append(" ")); + descriptor.getDuration().ifPresent(address -> sb.append(PREFIX_ITINERARY_DURATION).append(address).append(" ")); + descriptor.getPeople().ifPresent(people -> sb.append(PREFIX_PEOPLE).append(people).append(" ")); + descriptor.getBudget().ifPresent(people -> sb.append(PREFIX_BUDGET).append(people).append(" ")); + return sb.toString(); + } +} diff --git a/src/test/java/seedu/address/testutil/SerializableTestClass.java b/src/test/java/seedu/waddle/testutil/SerializableTestClass.java similarity index 98% rename from src/test/java/seedu/address/testutil/SerializableTestClass.java rename to src/test/java/seedu/waddle/testutil/SerializableTestClass.java index f5a66340489..397cdc2094a 100644 --- a/src/test/java/seedu/address/testutil/SerializableTestClass.java +++ b/src/test/java/seedu/waddle/testutil/SerializableTestClass.java @@ -1,4 +1,4 @@ -package seedu.address.testutil; +package seedu.waddle.testutil; import java.time.LocalDateTime; import java.util.ArrayList; diff --git a/src/test/java/seedu/address/testutil/TestUtil.java b/src/test/java/seedu/waddle/testutil/TestUtil.java similarity index 72% rename from src/test/java/seedu/address/testutil/TestUtil.java rename to src/test/java/seedu/waddle/testutil/TestUtil.java index 896d103eb0b..946ae776d56 100644 --- a/src/test/java/seedu/address/testutil/TestUtil.java +++ b/src/test/java/seedu/waddle/testutil/TestUtil.java @@ -1,13 +1,13 @@ -package seedu.address.testutil; +package seedu.waddle.testutil; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import seedu.address.commons.core.index.Index; -import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.model.Model; +import seedu.waddle.model.itinerary.Itinerary; /** * A utility class for test cases. @@ -36,20 +36,20 @@ public static Path getFilePathInSandboxFolder(String fileName) { * Returns the middle index of the person in the {@code model}'s person list. */ public static Index getMidIndex(Model model) { - return Index.fromOneBased(model.getFilteredPersonList().size() / 2); + return Index.fromOneBased(model.getFilteredItineraryList().size() / 2); } /** * Returns the last index of the person in the {@code model}'s person list. */ public static Index getLastIndex(Model model) { - return Index.fromOneBased(model.getFilteredPersonList().size()); + return Index.fromOneBased(model.getFilteredItineraryList().size()); } /** * Returns the person in the {@code model}'s person list at {@code index}. */ - public static Person getPerson(Model model, Index index) { - return model.getFilteredPersonList().get(index.getZeroBased()); + public static Itinerary getItinerary(Model model, Index index) { + return model.getFilteredItineraryList().get(index.getZeroBased()); } } diff --git a/src/test/java/seedu/waddle/testutil/TypicalIndexes.java b/src/test/java/seedu/waddle/testutil/TypicalIndexes.java new file mode 100644 index 00000000000..8a6994967e4 --- /dev/null +++ b/src/test/java/seedu/waddle/testutil/TypicalIndexes.java @@ -0,0 +1,12 @@ +package seedu.waddle.testutil; + +import seedu.waddle.commons.core.index.Index; + +/** + * A utility class containing a list of {@code Index} objects to be used in tests. + */ +public class TypicalIndexes { + public static final Index INDEX_FIRST_ITINERARY = Index.fromOneBased(1); + public static final Index INDEX_SECOND_ITINERARY = Index.fromOneBased(2); + public static final Index INDEX_THIRD_ITINERARY = Index.fromOneBased(3); +} diff --git a/src/test/java/seedu/waddle/testutil/TypicalItems.java b/src/test/java/seedu/waddle/testutil/TypicalItems.java new file mode 100644 index 00000000000..8a5a48fc9dc --- /dev/null +++ b/src/test/java/seedu/waddle/testutil/TypicalItems.java @@ -0,0 +1,91 @@ +package seedu.waddle.testutil; + +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COST_ART; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COST_BEACH; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COST_BREAKFAST; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COST_LUNCH; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COST_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COST_SKINNY; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COST_TOUR; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_ART; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_BEACH; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_BREAKFAST; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_LUNCH; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_SKINNY; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_TOUR; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITEM_DESC_ART; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITEM_DESC_BEACH; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITEM_DESC_BREAKFAST; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITEM_DESC_LUNCH; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITEM_DESC_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITEM_DESC_SKINNY; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITEM_DESC_TOUR; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PRIORITY_ART; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PRIORITY_BEACH; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PRIORITY_BREAKFAST; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PRIORITY_LUNCH; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PRIORITY_SHOPPING; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PRIORITY_SKINNY; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PRIORITY_TOUR; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.waddle.model.item.Item; + +/** + * A utility class containing a list of {@code Item} objects to be used in tests. + */ +public class TypicalItems { + private TypicalItems() { + } // prevents instantiation + + public static Item getShopping() { + return new ItemBuilder().withDesc(VALID_ITEM_DESC_SHOPPING) + .withDuration(VALID_DURATION_SHOPPING).withCost(VALID_COST_SHOPPING) + .withPriority(VALID_PRIORITY_SHOPPING).build(); + } + + public static Item getSkinny() { + return new ItemBuilder().withDesc(VALID_ITEM_DESC_SKINNY) + .withDuration(VALID_DURATION_SKINNY).withCost(VALID_COST_SKINNY) + .withPriority(VALID_PRIORITY_SKINNY).build(); + } + + public static Item getBeach() { + return new ItemBuilder().withDesc(VALID_ITEM_DESC_BEACH) + .withDuration(VALID_DURATION_BEACH).withCost(VALID_COST_BEACH) + .withPriority(VALID_PRIORITY_BEACH).build(); + } + + public static Item getArt() { + return new ItemBuilder().withDesc(VALID_ITEM_DESC_ART) + .withDuration(VALID_DURATION_ART).withCost(VALID_COST_ART) + .withPriority(VALID_PRIORITY_ART).build(); + } + + public static Item getTour() { + return new ItemBuilder().withDesc(VALID_ITEM_DESC_TOUR) + .withDuration(VALID_DURATION_TOUR).withCost(VALID_COST_TOUR) + .withPriority(VALID_PRIORITY_TOUR).build(); + } + + public static Item getBreakfast() { + return new ItemBuilder().withDesc(VALID_ITEM_DESC_BREAKFAST) + .withDuration(VALID_DURATION_BREAKFAST).withCost(VALID_COST_BREAKFAST) + .withPriority(VALID_PRIORITY_BREAKFAST).build(); + } + + public static Item getLunch() { + return new ItemBuilder().withDesc(VALID_ITEM_DESC_LUNCH) + .withDuration(VALID_DURATION_LUNCH).withCost(VALID_COST_LUNCH) + .withPriority(VALID_PRIORITY_LUNCH).build(); + } + + public static List getTypicalItems() { + return new ArrayList<>(Arrays.asList(getShopping(), getSkinny(), + getBeach(), getArt(), getTour(), getBreakfast(), getLunch())); + } +} diff --git a/src/test/java/seedu/waddle/testutil/TypicalItineraries.java b/src/test/java/seedu/waddle/testutil/TypicalItineraries.java new file mode 100644 index 00000000000..9b7bf86a230 --- /dev/null +++ b/src/test/java/seedu/waddle/testutil/TypicalItineraries.java @@ -0,0 +1,118 @@ +package seedu.waddle.testutil; + +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_BUDGET_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_BUDGET_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COUNTRY_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_COUNTRY_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_DURATION_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITINERARY_DESC_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_ITINERARY_DESC_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PEOPLE_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_PEOPLE_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_START_DATE_SUMMER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_START_DATE_WINTER; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_START_TIME_0000; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_START_TIME_1200; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_START_TIME_1715; +import static seedu.waddle.logic.commands.CommandTestUtil.VALID_START_TIME_2330; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.logic.commands.exceptions.CommandException; +import seedu.waddle.model.Waddle; +import seedu.waddle.model.item.Item; +import seedu.waddle.model.itinerary.DayNumber; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * A utility class containing a list of {@code Itinerary} objects to be used in tests. + */ +public class TypicalItineraries { + + public static final Itinerary SPRING = new ItineraryBuilder().withDescription("Spring Trip") + .withCountry("Australia").withStartDate("2023-01-01") + .withDuration("14").withPeople("1").withBudget("300").build(); + public static final Itinerary AUTUMN = new ItineraryBuilder().withDescription("Autumn Hiking") + .withCountry("Canada").withStartDate("2023-02-02") + .withDuration("22").withPeople("2").withBudget("700").build(); + public static final Itinerary GRADUATION = new ItineraryBuilder().withDescription("Graduation Trip") + .withCountry("France").withStartDate("2023-03-03") + .withDuration("4").withPeople("4").withBudget("2200").build(); + // Manually added - Itinerary's details found in {@code CommandTestUtil} + public static final Itinerary SUMMER = new ItineraryBuilder().withDescription(VALID_ITINERARY_DESC_SUMMER) + .withCountry(VALID_COUNTRY_SUMMER).withStartDate(VALID_START_DATE_SUMMER) + .withDuration(VALID_DURATION_SUMMER).withPeople(VALID_PEOPLE_SUMMER) + .withBudget(VALID_BUDGET_SUMMER).build(); + public static final Itinerary WINTER = new ItineraryBuilder().withDescription(VALID_ITINERARY_DESC_WINTER) + .withCountry(VALID_COUNTRY_WINTER).withStartDate(VALID_START_DATE_WINTER) + .withDuration(VALID_DURATION_WINTER).withPeople(VALID_PEOPLE_WINTER) + .withBudget(VALID_BUDGET_WINTER).build(); + public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER + + private TypicalItineraries() { + + } // prevents instantiation + + public static Itinerary getSpring() { + return new ItineraryBuilder().withDescription("Spring Trip") + .withCountry("Australia").withStartDate("2023-01-01") + .withDuration("14").withPeople("1").withBudget("300").build(); + } + + public static Itinerary getAutumn() { + return new ItineraryBuilder().withDescription("Autumn Hiking") + .withCountry("Canada").withStartDate("2023-02-02") + .withDuration("22").withPeople("2").withBudget("700").build(); + } + + public static Itinerary getGraduation() { + return new ItineraryBuilder().withDescription("Graduation Trip") + .withCountry("France").withStartDate("2023-03-03") + .withDuration("4").withPeople("4").withBudget("2200").build(); + } + + /** + * Returns a {@code Waddle} with all the typical itineraries. + */ + public static Waddle getTypicalWaddle() { + Waddle waddle = new Waddle(); + for (Itinerary itinerary : getTypicalItineraries()) { + waddle.addItinerary(itinerary); + } + return waddle; + } + + public static List getTypicalItineraries() { + List typicalItems = TypicalItems.getTypicalItems(); + Itinerary autumn = getAutumn(); + Itinerary graduation = getGraduation(); + + // configure AUTUMN + autumn.addItem(typicalItems.get(0)); + autumn.addItem(typicalItems.get(4)); + autumn.addItem(typicalItems.get(5)); + try { + autumn.planItem(Index.fromZeroBased(0), new DayNumber("1"), VALID_START_TIME_2330); + } catch (CommandException e) { + assert false : e.getMessage(); + } + + // configure GRADUATION + graduation.addItem(typicalItems.get(1)); + graduation.addItem(typicalItems.get(2)); + graduation.addItem(typicalItems.get(3)); + try { + graduation.planItem(Index.fromZeroBased(0), new DayNumber("1"), VALID_START_TIME_0000); + graduation.planItem(Index.fromZeroBased(0), new DayNumber("1"), VALID_START_TIME_1715); + graduation.planItem(Index.fromZeroBased(0), new DayNumber("2"), VALID_START_TIME_1200); + } catch (CommandException e) { + assert false : e.getMessage(); + } + + return new ArrayList<>(Arrays.asList(SPRING, autumn, graduation)); + } +} diff --git a/src/test/java/seedu/waddle/testutil/TypicalMultiIndexes.java b/src/test/java/seedu/waddle/testutil/TypicalMultiIndexes.java new file mode 100644 index 00000000000..303341a4b19 --- /dev/null +++ b/src/test/java/seedu/waddle/testutil/TypicalMultiIndexes.java @@ -0,0 +1,22 @@ +package seedu.waddle.testutil; + +import seedu.waddle.commons.core.index.Index; +import seedu.waddle.commons.core.index.MultiIndex; + +/** + * Represents a series of MultiIndex objects. + */ +public class TypicalMultiIndexes { + public static final MultiIndex MULTI_INDEX_FIRST_UNSCHEDULED_ITEM = + new MultiIndex().addIndex(Index.fromOneBased(1)); + public static final MultiIndex MULTI_INDEX_SECOND_UNSCHEDULED_ITEM = + new MultiIndex().addIndex(Index.fromOneBased(2)); + public static final MultiIndex MULTI_INDEX_THIRD_UNSCHEDULED_ITEM = + new MultiIndex().addIndex(Index.fromOneBased(3)); + public static final MultiIndex MULTI_INDEX_FIRST_DAY_FIRST_ITEM = + new MultiIndex().addIndex(Index.fromOneBased(1)).addIndex(Index.fromOneBased(1)); + public static final MultiIndex MULTI_INDEX_FIRST_DAY_SECOND_ITEM = + new MultiIndex().addIndex(Index.fromOneBased(1)).addIndex(Index.fromOneBased(2)); + public static final MultiIndex MULTI_INDEX_SECOND_DAY_FIRST_ITEM = + new MultiIndex().addIndex(Index.fromOneBased(2)).addIndex(Index.fromOneBased(1)); +} diff --git a/src/test/java/seedu/waddle/testutil/WaddleBuilder.java b/src/test/java/seedu/waddle/testutil/WaddleBuilder.java new file mode 100644 index 00000000000..b66c7263f27 --- /dev/null +++ b/src/test/java/seedu/waddle/testutil/WaddleBuilder.java @@ -0,0 +1,34 @@ +package seedu.waddle.testutil; + +import seedu.waddle.model.Waddle; +import seedu.waddle.model.itinerary.Itinerary; + +/** + * A utility class to help with building Waddle objects. + * Example usage:
+ * {@code Waddle w = new WaddleBuilder().withItineraries("John", "Doe").build();} + */ +public class WaddleBuilder { + + private Waddle waddle; + + public WaddleBuilder() { + waddle = new Waddle(); + } + + public WaddleBuilder(Waddle waddle) { + this.waddle = waddle; + } + + /** + * Adds a new {@code Itinerary} to the {@code Waddle} that we are building. + */ + public WaddleBuilder withItinerary(Itinerary itinerary) { + waddle.addItinerary(itinerary); + return this; + } + + public Waddle build() { + return waddle; + } +} diff --git a/src/test/java/seedu/address/ui/TestFxmlObject.java b/src/test/java/seedu/waddle/ui/TestFxmlObject.java similarity index 96% rename from src/test/java/seedu/address/ui/TestFxmlObject.java rename to src/test/java/seedu/waddle/ui/TestFxmlObject.java index 5ecd82656f2..d2ff8c2d90d 100644 --- a/src/test/java/seedu/address/ui/TestFxmlObject.java +++ b/src/test/java/seedu/waddle/ui/TestFxmlObject.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.waddle.ui; import javafx.beans.DefaultProperty; diff --git a/src/test/java/seedu/address/ui/UiPartTest.java b/src/test/java/seedu/waddle/ui/UiPartTest.java similarity index 97% rename from src/test/java/seedu/address/ui/UiPartTest.java rename to src/test/java/seedu/waddle/ui/UiPartTest.java index 33d82d911b8..d9163d5b9cd 100644 --- a/src/test/java/seedu/address/ui/UiPartTest.java +++ b/src/test/java/seedu/waddle/ui/UiPartTest.java @@ -1,8 +1,8 @@ -package seedu.address.ui; +package seedu.waddle.ui; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.waddle.testutil.Assert.assertThrows; import java.net.URL; import java.nio.file.Path; @@ -11,7 +11,7 @@ import org.junit.jupiter.api.io.TempDir; import javafx.fxml.FXML; -import seedu.address.MainApp; +import seedu.waddle.MainApp; public class UiPartTest { diff --git a/src/test/resources/view/UiPartTest/validFile.fxml b/src/test/resources/view/UiPartTest/validFile.fxml index bab836af0db..ce6b1c7f18b 100644 --- a/src/test/resources/view/UiPartTest/validFile.fxml +++ b/src/test/resources/view/UiPartTest/validFile.fxml @@ -1,4 +1,4 @@ - + Hello World! diff --git a/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml b/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml index 151e09ce926..6125de5cb85 100644 --- a/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml +++ b/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml @@ -1,6 +1,6 @@ - + Hello World!