diff --git a/build.gradle b/build.gradle
index b0c5528fb5..3e9c783171 100644
--- a/build.gradle
+++ b/build.gradle
@@ -41,6 +41,7 @@ checkstyle {
toolVersion = '8.23'
}
-run{
+run {
standardInput = System.in
+ enableAssertions = true;
}
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 0f072953ea..f6b6d2b5b1 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -1,9 +1,11 @@
# About us
-Display | Name | Github Profile | Portfolio
+Display | Name | Github Profile | Portfolio
--------|:----:|:--------------:|:---------:
- | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
+ | Woo Bo Tuan | [Bobowoo2468](https://github.com/Bobowoo2468) | [Portfolio](team/bobowoo2468.md)
+ | Wong Tze Shan Samantha | [swongts](https://github.com/swongts) | [Portfolio](team/swongts.md)
+| Yip Wayne | [YipWayne](https://github.com/YipWayne) | [Portfolio](team/YipWayne.md)
+ | Zhou Chengxu | [Demonshaha](https://github.com/Demonshaha) | [Portfolio](team/Demonshaha.md)
+
+
+
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 64e1f0ed2b..2643bb5d15 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,38 +1,729 @@
# Developer Guide
-## Acknowledgements
+## Table of Contents
-{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+- [Getting Started](#getting-started)
+ - [Recommended software (for optimal compatability)](#recommended-software-for-optimal-compatibility)
+ - [Setting up this project in your computer](#setting-up-this-project-in-your-computer)
-## Design & implementation
-{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
+- [Design & Implementation](#design--implementation)
+ - [Architecture](#architecture)
+ - [Parser](#parser-component)
+ - [Ui component](#ui-component)
+ - [Command component](#command-component)
+ - [Add feature](#add-feature)
+ - [Cut feature](#cut-feature)
+ - [List feature](#list-feature)
+ - [Find feature](#find-feature)
+ - [Sort feature](#sort-feature)
+ - [Storage component](#storage-component)
-## Product scope
+- [Appendix A: Product Scope](#appendix-a-product-scope)
+ - [Target user profile](#target-user-profile)
+ - [Value proposition](#value-proposition)
+- [Appendix B: User Stories](#appendix-b-user-stories)
+- [Appendix C: Non-Functional Requirements](#appendix-c-non-functional-requirements)
+- [Appendix D: Glossary](#appendix-d-glossary)
+- [Appendix E: Instructions for manual testing](#appendix-e-instructions-for-manual-testing)
+
+
+
+## Getting Started
+
+
+
+### Recommended software (for optimal compatibility)
+
+* GitHub
+* Sourcetree (for version control)
+* IntelliJ IDEA (IDE)
+* Amazon Coretto
+
+### Setting up this project in your computer
+
+1. On Github, fork this repo by clicking on the fork button
+
+
+2. In Sourcetree, clone the fork into your computer
+ 1. Open a new tab
+ 2. Select the clone button at the top of the menu
+ 3. Key in the details of this repository
+
+
+3. Configure the JDK: Follow the guide [Project Configuration/ SDKs](https://www.jetbrains.com/help/idea/sdk.html)
+ to ensure that Intellij is configured to use JDK 11.
+
+
+4. Import the project as a Gradle project: Follow the
+ guide [Intellij IDEA: Importing a Gradle project](https://se-education.org/guides/tutorials/intellijImportGradleProject.html)
+
+
+5. Verify the setup:
+ 1. Run the seedu.duke.TourPlanner and try a few commands.
+ 2. Run the tests to ensure they all pass.
+
+
+
+## Design & Implementation
+
+
+
+## Architecture
+
+
+
+The diagram below shows the high-level design of TourPlanner:
+
+
+
+Below is an overview of the main components, and how they interact with each other.
+
+**Main components**
+
+```TourPlanner``` acts as the main class. It is responsible for:
+
+* At app launch: Initialises the components in the correct sequence, and connects them up with each other.
+* While app is running: Executes commands from the user.
+* At shut down: Shuts down the components and invokes clean-up methods where necessary.
+
+The rest of the app consists of the following components:
+
+* ```Ui```: The UI of the application.
+* ```Parser```: Deciphers user input and returns the appropriate command to `TourPlanner`.
+* ```Command```: The different types of commands that can possibly be executed.
+* ```ObjectList```: Holds data in different arrays, based on their type. Namely there are four types of ObjectLists:
+ * `ClientList`
+ * `TourList`
+ * `FlightList`
+ * `ClientPackageList`
+* ```Storage```: Reads data from, and writes data to, the hard disk.
+
+
+
+**Interaction of components**
+
+The diagram below shows how the components interact with each other if the user inputs the command ```list -c```:
+
+
+
+
+
+## Parser component
+
+
+
+**API: `Parser.java`**
+
+The Sequence Diagram below illustrates the flow of parsing a command:
+
+Here is a class diagram of Parser component:
+
+
+
+The flow of **parse** in `Parser` is as follows:
+
+**Step 1.** `Parser` splits the user input String to identify user's **Command** (add, list, cut etc.).
+
+**Step 2.** Depending on the Command, `Parser` calls `parseXYZ()` method respective to the `XYZCommand`
+(`XYZ` is a placeholder for the specific command e.g. `AddClientCommand`) given.
+
+**Step 3.** `Parser` creates an `XYZCommand` object which Parser returns as a `Command` object.
+
+**Step 4.** `XYZCommand` is returned to `TourPlanner` to be executed.
+
+
+
+### Example of parse mechanism - `ParseAdd`
+
+Given below is an example scenario, outlining how parseAdd behaves at each step.
+
+**Step 1.** `Parser` first identifies the identifier for add, namely `-c` (add client), `-t` (add tour), `-f` (add
+flight), `-p` (add package), by splitting the identifier and the rest of the string by spaces.
+
+```
+-t JPN /n Japan Tour /p 1500 --> [-t, JPN, /n, Japan Tour, /p 1500]
+```
+
+**Step 2.** Next, `Parser` checks if all prefixes for data fields e.g. name, price, id are present.
+
+**Step 3.** To sort the values for addition, the prefixes and their corresponding indexes are stored as key-value pairs
+into a TreeMap. A TreeMap helps to sort the pairs by the natural ordering of the keys.
+:information_source: See [TreeMap](#appendix-d-glossary).
+
+```
+^JPN ^/n Japan Tour ^/p 1500 --> [(0, null), (4, /n), (18, /p)] (sorted)
+```
+
+**Step 4.** The prefix and its index will facilitate splitting the string into substrings, containing both the prefix
+and the value corresponding to the prefix.
+
+```
+-t JPN /n Japan Tour /p 1500 --> [JPN, /n Japan Tour, /p 1500]
+```
+
+**Step 5.** Given the prefix, the array index that the value will be inserted into is predetermined.
+
+```
+[JPN, /n Japan Tour, /p 1500] --> [JPN, Japan Tour, 1500]
+```
+
+**Step 6.** The value is extracted from the substring by removing the prefix, and inserted into an array. The array is
+used as an argument for the *Object* constructor.
+
+
+
+## UI Component
+
+
+
+**API: `Ui.java`**
+
+The Ui component is the means by which Command(s) can receive inputs from the user, as well as display information to
+them, all through the console terminal.
+
+The diagram below shows the class diagram of the Ui component, in relation with other major components:
+
+
+
+
+
+After the user typed in an input into the console terminal and presses 'Enter':
+
+* the ```Ui``` reads the input typed in by the user on the console terminal.
+* the ```Parser``` class parses the read input and calls the appropriate ```Command```. (see the ```Parser```
+ and ```Command``` sections for more information)
+* the called ```Command``` calls a function in the ```Ui``` to print the appropriate information onto the console
+ terminal.
+
+
+
+## Command component
+
+
+
+**API: `Command.java`**
+
+Here's a (partial) class diagram of Command component:
+
+
+
+:information_source: `XYZ` in this diagram is a placeholder for the specific data type (`Client`, `Flight`, `Tour`
+, `ClientPackage`). A similar workflow applies for these classes depending on the availability of the command for the
+specific data type.
+
+How the `Command` works:
+
+**Step 1.** Based on the user input, `Parser` returns one of the subclasses of `Command` to `TourPlanner`.
+
+**Step 2.** `TourPlanner` calls `Command.execute()`.
+
+**Step 3.** Corresponding command uses associate classes, `XYZList` (`XYZ` is a placeholder for the specific data type
+e.g. `Client`) and `Ui` to carry out its function.
+
+
+
+### Add feature
+
+The add feature is facilitated mainly by `Parser`, and returns an `AddCommand` object. When `AddCommand` is executed,
+the values corresponding to their fields are added.
+
+After receiving the command from the user by `readCommand` of `Ui`, the command is parsed by `Parser`. The `Parser`
+first determines the command type by separating the command from the subsequent arguments. After determining the
+command, `Parser` executes the specific methods to sense-make the arguments, in response to the specific command.
+
+Here are sample add command inputs that can be parsed.
+
+Example add commands:
+
+* `add -c c001 /n Bo Tuan /cn 91234567 /m bobotea@gmail.com`
+* `add -f SQ-JPN1 /d Japan /r Singapore /dd 29/10/21 13:00 /rd 5/11/21 02:00`
+* `add -t t001 /n AustralianRomance /p 1500`
+* `add -p p001 /c c001 /f SQ-JPN1 /t t001`
+
+In general, there is a sequence of steps when any of the 4 `add` commands are called.
+
+Here is an example usage of `add -c` to add client "c001":
+
+**Step 1.** User inputs `add -c c001 [ARGS...]`. This command is passed to `parse()` method in the `Parser` class.
+
+**Step 2.** Based on the user input, `parse()` identifies that it is of type `add` command and calls `ParseAdd()`.
+
+**Step 3** `ParseAdd()` executes three instructions:
+
+* Extract values from user's input and creates a `values` Array
+* Handle exceptions to the `values` Array, determined by the data type identifier (e.g. `-c`, `-t`)
+* Instantiates a `Client`/`Tour`/`Flight`/`ClientPackage` *Object*, determined by the same data type identifier.
+* Returns an `AddXYZCommand` based on the same data type identifier, passing in the *Object* as an argument for adding.
+
+Depending on the type of add command being called, these command types will be returned:
+
+* `add -c`: `AddClientCommand`
+* `add -f`: `AddFlightCommand`
+* `add -t`: `AddTourCommand`
+* `add -p`: `AddClientPackageCommand`
+
+The following 2 sections focuses on add for the specific classes.
+
+
+
+#### Adding a client package
+
+Given below is an example usage of `add -p p001 ARGS...` to add client package "p001".
+
+Here is a (partial) sequence diagram of above user input:
+
+
+
+**Step 1.** Parser creates a `values` array, upon extracting values from user's input.
+Creates `AddClientPackageCommand(values)`, determined by the datatype identifier `-p`. Returns the created `Command`
+subclass to `TourPlanner`.
+
+**Step 2.** Then, `execute()` method in `AddClientCommand` is called. `getClientPackageById("p001")` is called, which
+finds the `ClientPackage` based on the `CLIENT_PACKAGE_ID` "p001".
+
+**Step 3.** If the `CLIENT_PACKAGE_ID` "p001" already exists, an error message is returned.
+
+**Step 4.** Else, the `CLIENT_PACKAGE_ID` "p001" does not exist, `execute()` calls `createClientPackage` which finds the
+specific `Client`, `Tour` and `Flight` objects to be added to the `ClientPackage`
+
+**Step 5.** Then, `add` in `ClientPackageList` is called, to add the specific `ClientPackage` into `ClientPackageList`.
+
+**Step 5.** `execute()` calls `showAdd` in `Ui`, which prints out the Object, `ClientPackage` that was added.
+
+
+
+#### Adding a particular client / tour / flight
+
+Given below is an example usage of `add -t t001 ARGS...` to add tour with the respective arguments.
+
+Here is a (partial) sequence diagram for above user input:
+
+
+
+**Step 1.** `Parser` parses the input and instantiates `AddTourCommand`. It then returns it to `TourPlanner`.
+
+**Step 2.** Then, `execute()` method in `AddTourCommand` is called. `getTourById("t001")` is called, which finds
+the `Tour` based on the `TOUR_ID` "t001".
+
+**Step 3.** If the `TOUR_ID` "t001" already exists, an error message is returned.
+
+**Step 4.** Else, the `TOUR_ID` "t001" does not exist, `execute()` calls `add` in `TourList`, to add the specific client
+package and its arguments into the `TourList`.
+
+**Step 5.** `execute()` calls `showAdd` in `Ui`, which prints out the Object, `Tour` that was added.
+
+
+
+
+### Cut feature
+
+The `cut` feature is used to remove an entry from the `ObjectList` (for `Client`, `Tour`, `Flight` and `ClientPackage`),
+where `Parser` determines the `ObjectList` to remove from.
+
+It implements these following types of `cut` commands:
+
+* `cut -c CLIENT_ID`
+* `cut -t TOUR_ID`
+* `cut -f FLIGHT_ID`
+* `cut -p CLIENTPACKAGE_ID`
+
+In general, there is a sequence of steps when any of the 4 `cut` commands are called.
+
+Here is an example usage of `cut -c` to delete client with `CLIENT_ID` of "c001":
+
+**Step 1.** After adding a few client packages to the database, user inputs `cut -c c001`. This command is passed
+to `parse()` method in the `Parser` class.
+
+**Step 2.** Based on the user input, `parse()` identifies that it is of type `cut` command and calls `ParseCut()`.
+`ParseCut()` will then create `CutClientCommand("c001")` based on the prefix `-c`. Returns the created `Command`
+subclass to `TourPlanner`.
+
+Depending on the type of cut command being called, these command types will be returned:
+
+* `cut -c`: `CutClientCommand`
+* `cut -f`: `CutFlightCommand`
+* `cut -t`: `CutTourCommand`
+* `cut -p`: `CutClientPackageCommand`
+
+The following 2 sections focuses on cut for the specific classes.
+
+
+
+#### Cutting a particular client package
+
+Given below is an example usage of `cut -p p001` to deletes client package with `CLIENTPACKAGE_ID` of "p001".
+
+Here is a (partial) sequence diagram of above user input:
+
+
+
+**Step 1.** `Parser` creates `CutClientPackageCommand("p001")` and returns it to `TourPlanner`.
+
+**Step 2.** Then, `execute()` method in `CutClientPackageCommand` calls `getClientPackageById("p001")`
+which finds the `ClientPackage` based on the `CLIENTPACKAGE_ID` "p001".
+
+**Step 3.** The `ClientPackage` is removed from the `ClientPackageList`.
+
+
+
+#### Cutting a particular client / tour / flight
+
+Given below is an example usage of `cut -c c001` to delete client with `CLIENT_ID` of "c001"
+and all corresponding client packages that contain deleted client.
+
+Here is a (partial) sequence diagram for above user input:
+
+
+
+**Step 1.** `Parser` creates `CutClientCommand("c001")` and returns it to `TourPlanner`.
+
+**Step 2.** Then, `execute()` method in `CutClientCommand` calls `getClientById("c001")`
+which finds the `Client` based on the `CLIENT_ID` "c001".
+
+**Step 3.** The `Client` is removed from the `ClientList`.
+
+**Step 4.** Next, `getClientPackageByClient("c001")` returns an `ArrayList` of `ClientPackages` that contains
+the `CLient` "c001". :information_source: See [ArrayList](#appendix-d-glossary).
+
+**Step 5.** Loops through the `ClientPackages` and deletes them from `ClientPackageList`.
+
+*:information_source: `Tour` and `Flight` works in the same way respectively with `TourList` and `FlightList`
+
+
+
+### List feature
+
+The `list` feature is used to display all entries in `ObjectList` (`Client`, `Tour`, `Flight` and `ClientPackage`),
+where `Parser` determines the `ObjectList` to list from.
+
+It implements these following types of list commands:
+
+* `list -c`: Lists all existing clients and their contact numbers
+* `list -t`: Lists all existing tours
+* `list -f`: Lists all existing flights
+* `list -p`: Lists all clients and their corresponding tours and flights
+
+Given below is an example usage of command `list -c` that lists all `Client` objects in `ClientList`:
+
+Here is a (partial) sequence diagram for the above user input:
+
+
+**Step 1**: After adding a few clients to the database, user inputs `list -c`. This command is passed to `parse()`
+method in the `Parser` class.
+
+**Step 2**: Based on the user input, `parse()` identifies that it is of type `list` command and calls `ParseList()`.
+`ParseList()` will then return `ListClientCommand` based on the prefix `-c`.
+
+**Step 3**: Then, `execute()` method in `ListClientCommand` is called, where it loops through the current `clientList`
+and prints out all client names and their details.
+
+Depending on the type of list command being called, these command types will be returned:
+
+* `list -c`: `ListClientCommand`
+* `list -f`: `ListFlightCommand`
+* `list -t`: `ListTourCommand`
+* `list -p`: `ListClientPackageCommand`
+
+
+
+### Find feature
+
+The ```find``` feature is to be used to query for a particular client, tour or flight, providing extensive information
+about it. It is facilitated by the ```parse``` function in the ```Parser``` class, and returns a FindCommand object.
+When FindCommand is executed, it queries the corresponding `ObjectList` and returns the matching entries.
+
+It implements these following types of find commands:
+
+* `find -c CLIENT_NAME`: returns clients that contain `CLIENT_NAME` in their name and client packages they are
+ subscribed to.
+* `find -t TOUR_ID`: returns tours that match the `TOUR_ID` and tours subscribed to the tour.
+* `find -f FLIGHT_ID`: returns flights that match the `FLIGHT_ID` and clients subscribed to the flight.
+
+
+
+In general, there is a sequence of steps when any of the 3 `find` commands are called.
+
+Firstly, assume that in previous sessions, commands were executed to add clients, tours, flights and packages to
+the ```ClientList```, ```TourList```, ```FlightList``` and ```PackageList``` respectively. In particular, these specific
+commands were executed.
+
+* ```add -c c001 /n Bo Tuan /cn 93338333 /m borangutuan@mail.com```
+* ```add -t JPN /n Japan Tour /p 1500```
+* ```add -f SQ-JPN /d JPN /r SG /dd 20/10/2021 18:00 /rd 21/10/2021 03:00```
+* ```add -p p001 /c c001 /t JPN /f SQ-JPN```
+
+Here is an example usage of `find -c bo` to find a client with name "Bo Tuan":
+**Step 1**: The user executes ```find -c bo``` to query if a client named "Bo Tuan" exists in the ClientList.
+The ```parse``` function in the ```Parser``` class takes the command, and calls `parseFind()`
+based on (```find```) in the input. ```parseFind()``` determines which type of Object is to be queried for and
+```FindClientCommand()``` is created with the parameter ```bo``` based on prefix `-c`.
+
+Depending on the type of find command being called, these command types will be returned:
+
+* ```find -c```: ```FindClientCommand```
+* ```find -t```: ```FindTourCommand```
+* ```find -f```: ```FindFlightCommand```
+
+The following 3 sections focuses on find for the specific classes.
+
+
+
+#### Finding a particular client
+
+Given below is an example usage of `find -c bo`.
+
+Here is a (partial) sequence diagram for the above user input:
+
+
+
+**Step 1** `Parser` creates `FindClientCommand("bo")` and returns it to `TourPlanner`.
+
+**Step 2** The ```FindClientCommand``` iterates through each ```Client``` object in the ```ClientList```. For
+every ```Client```, the ```getName()``` function is called to retrieve the name attribute of the Client. The name
+attribute is then converted to lower case for comparison with the substring. If the name attribute is contains the
+substring```bo```, the ```Client``` object is printed onto the console terminal.
+
+**Step 3** In addition, the ```FindClientCommand``` iterates through each ```ClientPackage``` object in
+the ```ClientPackageList```. For every ```ClientPackage```, the ```getClient()``` function is called to retrieve the
+client attribute of the ClientPackage. If the client attribute is equals to the same ```Client``` object that was found
+in Step 2, the respective client package will be printed onto the console terminal.
+
+
+
+#### Finding a particular tour
+
+Given below is an example usage of `find -t JPN`.
+
+Here is a (partial) sequence diagram for the above user input:
+
+
+
+**Step 1**: `Parser` creates `FindTourCommand("JPN")` and returns it to `TourPlanner`.
+
+**Step 2**: The ```FindTourCommand``` is executed and iterates through each ```Tour``` object in the ```TourList```. For
+every ```Tour```, the ```getCode()``` function is called to retrieve the code attribute of the Tour. If the tour
+attribute is equals to ```JPN```, the ```Tour``` object is printed onto the console terminal.
+
+**Step 3**: In addition, the ```FindTourCommand``` iterates through each ```ClientPackage``` object in
+the ```ClientPackageList```. For every ```ClientPackage```, the ```getTour()``` function is called to retrieve the tour
+attribute of the ClientPackage. If the tour attribute is equals to the same ```Tour``` object that was found in Step 2,
+the client attribute of that same ClientPackage will be retrieved using the ```getClient()``` function.
+
+**Step 4**: The client's name attribute is then retrieved using the ```getName()``` function in the Client class. The
+name is printed onto the console terminal under "Subscribed Clients". Once all ```ClientPackage``` objects are iterated
+through in the ```ClientPackageList```, the total number of subscribed clients will be printed on the console terminal.
+
+
+
+#### Finding a particular flight
+
+Given below is an example usage of `find -f SQ-JPN`.
+
+Here is a (partial) sequence diagram for the above user input:
+
+
+
+**Step 1**: `Parser` creates `FindFlightCommand("SQ-JPN")` and returns it to `TourPlanner`.
+
+**Step 2**: The ```FindFlightCommand``` is executed and iterates through each ```Flight``` object in
+the ```FlightList```. For every ```Flight```, the ```getCode()``` function is called to retrieve the code attribute of
+the Tour. If the tour attribute is equals to ```SQ-JPN```, the ```Flight``` object is printed onto the console terminal.
+
+**Step 3**: In addition, the ```FindFlightCommand``` iterates through each ```ClientPackage``` object in
+the ```ClientPackageList```. For every ```ClientPackage```, the ```getFlight()``` function is called to retrieve the
+tour attribute of the ClientPackage. If the tour attribute is equals to the same ```Flight``` object that was found in
+Step 2, the client attribute of that same ClientPackage will be retrieved using the ```getClient()``` function.
+
+**Step 4**: The client's name attribute is then retrieved using the ```getName()``` function in the Client class. The
+name is printed onto the console terminal under "Passengers". Once all ```ClientPackage``` objects are iterated through
+in the ```ClientPackageList```, the total number of passengers will be printed on the console terminal.
+
+
+
+#### Design Considerations
+
+* Alternative: only iterate through the ```ClientPackageList```.
+ * Pros: fast querying time.
+ * Cons: If the client/tour/flight is not in any package, none of their information can be accessed, including their
+ contact number.
+
+
+
+### Sort feature
+
+The `sort` feature is used to sort the `ObjectList` (for `Client`, `Tour`, `Flight` and `ClientPackage`) and list it,
+where `Parser` determines the `ObjectList` and criteria to sort for.
+
+It implements these following types of `sort` commands:
+
+* Client
+ * `sort -c /n`: Sorts alphabetically by client name
+ * `sort -c /id`: Sorts alphabetically by client id
+* Flight
+ * `sort -f /d`: Sorts in ascending order of date and time for departure flight
+ * `sort -f /r`: Sorts in ascending order of date and time for return flight
+ * `sort -f /id`: Sorts alphabetically by flight id
+* Tour
+ * `sort -t /id`: Sorts alphabetically by tour id
+ * `sort -t /p`: Sorts in ascending order of tour price
+
+Given below is an example usage of `sort -c /id`:
+
+Here is a (partial) sequence diagram of the above user input:
+
+
+
+**Step 1**: After adding a few clients to the database, user inputs `sort -c /id`. This command is passed to `parse()`
+method in the `Parser` class.
+
+**Step 2**: Based on the user input, `parse()` identifies that it is of type `sort` command and calls `ParseSort()`.
+`ParseSort()` will then return `SortClientCommand` based on the prefix `-c`.
+
+**Step 3**: Then, `execute()` method in `SortClientCommand` is called, where it gets the sorted `ArrayList` of
+`clientIds`. :information_source: See [ArrayList](#appendix-d-glossary).
+
+**Step 4** In `UI`, `showSortedClientById()` is called, with `clientIds` passed in. The method iterates through all the
+client IDs. For each iteration, finds the corresponding `Client` with `getClientById()` and prints out the `Client`
+and their details.
+
+Depending on the type of sort command being called, these command types will be returned:
+
+* `sort -c`: `SortClientCommand`
+* `sort -f`: `SortFlightCommand`
+* `sort -t`: `SortTourCommand`
+
+
+
+## Storage Component
+
+
+
+**API: `ClientPackageStorage.java` `ClientStorage.java` `FlightStorage.java` `TourStorage.java`**
+
+Shown below is the class diagram for TourPlanner's Storage Component:
+
+
+
+
+
+The Storage component consists of:
+
+1. `ClientPackageStorage.java`: Reading and saving files which record all clientpackages.
+2. `ClientStorage.java`: Reading and saving files which record all clients.
+3. `FlightStorage.java`: Reading and saving files which record all flights.
+4. `TourStorage.java`: Reading and saving files which record all tours.
+
+To add on Storage component is designed to access only the following folder:
+
+1. `data/`
+
+
+
+## Appendix A: Product scope
+
+
+
### Target user profile
-{Describe the target user profile}
+* Tour company employees that need to manage a significant amount of tour data
+* Prefer desktop apps over other types
+* Can type fast
+* Is reasonably comfortable using CLI apps
### Value proposition
-{Describe the value proposition: what problem does it solve?}
+* Manage tour information faster than typical mouse /GUI driven apps
+
+
+
+## Appendix B: User Stories
-## User Stories
+
-|Version| As a ... | I want to ... | So that I can ...|
+Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
+
+|Priority| As a ... | I want to ... | So that I can ...|
|--------|----------|---------------|------------------|
-|v1.0|new user|see usage instructions|refer to them when I forget how to use the application|
-|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list|
+|`* * *`|new user|see usage instructions|refer to them when I forget how to use the application|
+|`* * *`|user|add a new entry of specific data type| add data into the current database|
+|`* * *`|user|delete an existing entry of specific data type |remove outdated data from the current database|
+|`* *`|user with large amounts of data|find an existing entry of specific data type |locate a specific entry easily|
+|`* *`|user with large amounts of data|sort existing entries of specific data type |make smarter recommendations to clients based on their preferences|
+|`* *`|user|check number of clients subscribed to a tour / flight|check the popularity, vacancy of certain tours / flights|
+
+:information_source: 'specific data type' refers to either clients, tours, flights or client packages.
+
+
+
+## Appendix C: Non-Functional Requirements
+
+
+
+* Should work on any mainstream OS as long as it has Java 11 or above installed.
+* Should be able to hold up to 1000 entries without a noticeable sluggishness in performance for typical usage.
+* 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.
+
+
+
+## Appendix D: Glossary
+
+
+
+**ClientPackage:** serves as a link between the client, tour and flight, helps to keep track of the tours (and flights)
+they are subscribed to. `ClientPackage` class stores the actual `Client`, `Flight` and `Tour` objects.
+
+**ArrayList:** acts as a dynamic array, where items can be easily added and removed.
+
+**TreeMap:** sorts and stores key-value pairs. The key-value pairs are sorted according to the natural ordering of its
+keys, or by a Comparator provided at map creation time.
+
+**Subclass:** To say class `A` is a subclass of class `B` would mean that class `A` inherits from class `B`. A subclass
+inherits attributes and methods from the parent class.
+
+**Exceptions:** is any event that interrupts the normal flow of program execution, due to an unwanted event which cannot
+be controlled by developers
+
+
+
+## Appendix E: Instructions for manual testing
+
+
+
+Given below are instructions to test the app manually.
+
+### Launch and shutdown
+
+1. Initial launch
+ 1. Download the jar file and copy into an empty folder.
+ 2. Run the command ```java -jar TourPlanner.jar``` in a command window to start the program.
+
+ * Expected: Applications shows welcome messages and allows you to type in commands.
+
+### Adding and viewing data
+
+1. Add `Client` to database
+ * Test case: `add -c c001 /n Bo Tuan /cn 93338333 /m borangutuan@mail.com`
-## Non-Functional Requirements
+ 1. Input the test case to the command window.
+ 2. Input `list -c`.
-{Give non-functional requirements}
+ * Expected: `Client` with id "c001", name "Bo Tuan", contact "93338333", mail "borangutuan@mail.com"
+ is displayed in the list of clients.
+ * :information_source: tests can be repeated with `Tour`, `Flight` and `ClientPackage` with the corresponding data
+ fields.
-## Glossary
+### Cutting data
-* *glossary item* - Definition
+1. Cut `Client` from database
+ * Test case: `cut -c c001` after calling `add -c c001 /n Bo Tuan /cn 93338333 /m borangutuan@mail.com`
-## Instructions for manual testing
+ 1. Input the test case to the command window.
+ 2. Input `list -c`.
-{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing}
+ * Expected: `Client` with id "c001", name "Bo Tuan", contact "93338333", mail "borangutuan@mail.com"
+ is no longer displayed in the list of clients.
+ * :information_source: tests can be repeated with `Tour`, `Flight` and `ClientPackage` with the corresponding data
+ fields.
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..e2556e39ae 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,6 +1,8 @@
-# Duke
+# TourPlanner
-{Give product intro here}
+If you are a tour agency who require customised data management solutions, **TourPlanner** is the program for you!
+TourPlanner has an intuitive and easy-to-use CLI interface, one can easily add to, and update the database with client, flight, and tour details!
+What are you waiting for?
Useful links:
* [User Guide](UserGuide.md)
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index abd9fbe891..5b5f5392a7 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,42 +1,960 @@
-# User Guide
+# **TourPlanner User Guide**
-## Introduction
+
+TourPlanner is a desktop application meant for employees of travel agencies. Its main purpose is to manage clients,
+flights, accommodations and client packages data, optimized for use via a Command Line Interface (CLI). If you can type
+fast, this application can allow one to access relevant travel information faster than traditional GUI applications.
-{Give a product intro}
+
-## Quick Start
+## Table of Contents
-{Give steps to get started quickly}
+### Taking Off - Introducing TourPlanner
-1. Ensure that you have Java 11 or above installed.
-1. Down the latest version of `Duke` from [here](http://link.to/duke).
+- [User Guide - How to Use](#user-guide---how-to-use)
+- [Quick Start](#quick-start)
+- [Introduction to Data Types](#introduction-to-data-types)
+- [Introduction to Data Fields](#introduction-to-data-fields)
-## Features
+### On Top Of The World - Features of TourPlanner
-{Give detailed description of each feature}
+#### Add/Cut Data Entries
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
+- [Adding Data Types](#heavy_plus_sign-adding-data-types--add)
+ - [Adding Clients](#adding-clients-into-database-add--c)
+ - [Adding Tours](#adding-tours-into-database-add--t)
+ - [Adding Flights](#adding-flights-into-database-add--f)
+ - [Adding Client Packages](#adding-clientpackage-into-database-add--p)
+- [Cutting Data Types](#scissors-cutting-data-types--cut)
+ - [Cutting Client Packages](#cut-client-package)
+ - [Cutting Clients/Tours/Flights](#cut-client--tour--flight)
-Format: `todo n/TODO_NAME d/DEADLINE`
+#### Querying the Database
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+- [Listing Data Types](#mag_right-listing-data-types-list)
+- [Finding Data Types](#finding-data-types-find)
+ - [Finding Clients](#find-client)
+ - [Finding Tour/Flights](#find-tour--flight)
+- [Sorting Data Types](#chart_with_upwards_trend-sorting-data-types-sort)
+ - [Sorting Clients](#sort-client)
+ - [Sorting Tours](#sort-tour)
+ - [Sorting Flights](#sort-flights)
-Example of usage:
+#### Miscellaneous
-`todo n/Write the rest of the User Guide d/next week`
+- [Viewing help](#viewing-helphelp)
+- [Exiting TourPlanner](#exit-application-bye)
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
+### Landing Gracefully - Summary
-## FAQ
+- [General Command Information](#general-command-information)
+- [Supporting Command Information](#supporting-command-information)
-**Q**: How do I transfer my data to another computer?
+
-**A**: {your answer here}
+## **User Guide - How to Use**
-## Command Summary
+
-{Give a 'cheat sheet' of commands here}
+Like our application, TourPlanner's User Guide is **intuitive** to use, but **comprehensive** as well. Any doubts you
+might have when using the application, we got you covered.
-* Add todo `todo n/TODO_NAME d/DEADLINE`
+**Navigate** across the User Guide with the [**Table of Contents**](#table-of-contents). Just click on the hyperlinks
+associated with your query, and you will be redirected to the specific section that addresses your query.
+
+The contents in this user guide follows you through the runtime of the program, and is sequenced chronologically.
+
+If you require help with your set-up, look under the [**Taking off**](#taking-off---introducing-tourplanner) section.
+
+Details on how to use the features are given under the
+[**On Top of The World**](#on-top-of-the-world---features-of-tourplanner) section.
+
+Lastly, for a summary of all the commands and prefixes, look under the
+[**Landing Gracefully**](#landing-gracefully---summary) section.
+
+Happy to have you onboard TourPlanner!
+
+
+
+## **Quick Start** ##
+
+
+
+1. Ensure you have Java 11 installed in your Computer.
+
+2. Download the latest TourPlanner.jar from [here](https://github.com/AY2122S1-CS2113T-F11-3/tp/releases/tag/A-JarV2.1).
+
+3. Copy the file to the folder you want to use as the home folder for your database.
+
+4. Open a command window in that folder
+
+5. Run the command ```java -jar TourPlanner.jar``` to start the program
+
+6. Type the command in the command box and press Enter to execute
+
+
+
+[**Return to Table of Contents**](#table-of-contents)
+
+## Introduction to Data Types ##
+
+
+
+Throughout the user guide, you may observe that many commands have a
+```[DATA_TYPE]``` parameter. This parameter is to be specified right after declaring the command to use.
+
+There are 4 data types that are stored in TourPlanner:
+
+* ```-c``` Clients
+* ```-t``` Tours
+* ```-f``` Flights
+* ```-p``` Client Package
+
+:information_source: Client Package contains the client along with the tour and flight they have opted for.
+
+Examples of data types in commands:
+
+* ```add -t JPN /n Japan Basic Tour /p 1500 ``` calls for a tour to be added.
+* ```list -p ``` calls for all available client packages to be listed out.
+
+
+
+[**Return to Table of Contents**](#table-of-contents)
+
+## Introduction to Data Fields
+
+
+
+Throughout the user guide, you may observe that many commands have a ```[DATA_FIELDS]``` parameter.
+The ```[DATA_FIELDS]``` parameter represents the compulsory information fields that serves to describe the command in
+more detail.
+
+Data fields can be viewed as supporting documents to the main command to be executed.
+
+One data field is represented in the following format: ```/PREFIX INFO```
+
+These fields are *mandatory*. Let's say a client is added without his **name**, or **contact details**. Having these
+empty fields reduces the utility of the program, since certain meaningful operations cannot be performed. Case in point,
+one will not be able to query the client by name.
+
+These fields paint a complete picture of the different data types. Intuitively, clients have *key information fields*
+such as **name**, **contact number** and **email**.
+
+Examples of data fields in commands:
+
+* Add client:
+ * ```/n NAME```
+ * ```/cn CONTACT_NUMBER```
+ * ```/m EMAIL```
+
+
+* Sort tours:
+ * ```/p``` - sorts tours by *price*
+ * ```/n``` - sorts tours by *name*
+
+*Note: TourPlanner does not support the use of special characters or numbers larger than 2 billion.*
+
+
+
+[**Return to Table of Contents**](#table-of-contents)
+
+## Viewing help:``help``
+
+
+
+Shows a message with the link to this user guide.
+
+
+
+Format: ```help```
+
+The following output will be shown:
+
+```
+add: Add information of all data types into the database.
+Prefixes can be input in any order.
+Add client: add -c CLIENT_ID /n NAME /cn CONTACT_NUM /m EMAIL
+Add flight: add -f FLIGHT_ID /d DEPART_DESTINATION /r RETURN_DESTINATION
+/dd DEPARTURE_DATETIME /rd RETURN_DATETIME
+Add tour: add -t TOUR_ID /n DEPART_DESTINATION /p TOUR_PRICE
+Add client package: add -p PACKAGE_ID /c CLIENT_ID /t TOUR_ID /f FLIGHT_ID
+
+list: Shows a list of all available entries of a specific data type, along with their respective fields.
+List client: list -c
+List flight: list -f
+List tour: list -t
+List client package: list -p
+
+cut: Deletes entry of a certain data type and all client packages corresponding to the entry.
+Cut client: cut -c CLIENT_ID
+Cut flight: cut -f FLIGHT_ID
+Cut tour: cut -t TOUR_ID
+Cut client package: cut -p PACKAGE_ID
+
+find: Finds specific entry of data type, returns the entry and other relevant information.
+Find client: find -c CLIENT_NAME
+Find flight: find -f FLIGHT_ID
+Find tour: find -t TOUR_ID
+
+sort: Sorts entries in the data type based on the criteria.
+Sort client:
+Sort by id: sort -c /id
+Sort by name: sort -c /n
+Sort flight:
+Sort by id: sort -f /id
+Sort by departure date: sort -f /d
+Sort by return date: sort -f /r
+Sort tour:
+Sort by id: sort -t /id
+Sort by name: sort -t /n
+Sort by price: sort -t /p
+
+bye: Exits the program.
+```
+
+
+
+[**Return to Table of Contents**](#table-of-contents)
+
+## Adding / Cutting Data Types
+
+
+
+:information_source:Please refer to Introduction to Data Types and Introduction to Data Fields on the
+purpose and syntax of ```DATA_TYPES``` and ```DATA_FIELDS```.
+
+
+
+## :heavy_plus_sign: Adding data types: ```add```
+
+You are able to add information of all data types into the database, specified by mandatory fields to enter for each
+entry.
+
+Format: ```add [DATA_TYPE] [DATA_FIELDS]```
+
+
+
+### Adding clients into database: ```add -c```
+
+Format: ```add [DATA_TYPE] [CLIENT_ID] [DATA_FIELDS]```
+
+These are your existing or potential customers. In this industry of tour planning, your livelihoods depend on them.
+
+Mandatory data fields:
+
+* Client ID - ```CLIENT_ID```
+* Client name - ```/n NAME```
+* Client contact number - ```/cn CONTACT_NUMBER```
+* Client email address - ```/m EMAIL```
+
+:exclamation: Note that the given **contact number** should ideally contain only eight numbers from _0 to 9_. If there
+are extra inputs, TourPlanner will return a **warning** message.
+
+Example:
+
+* `add -c c001 /n Bo Tuan /cn 91234567 /m bobotea@gmail.com`
+
+Adds *Bo Tuan*, contact number *91234567* and email
+*bobotea@gmail.com*, as client ID *c001*.
+
+* `add -c c002 /n Wayne /m winnie@gmail.com /cn 92468024`
+
+Adds *Wayne*, contact number *92468024* and email
+*winnie@gmail.com*, as client ID *c002*.
+
+
+
+### Adding flights into database: ```add -f```
+
+Format: ```add [DATA_TYPE] [FLIGHT_ID] [DATA_FIELDS]```
+
+Mandatory data fields:
+
+* Flight ID - ```FLIGHT_ID```
+* Flight departure destination - ```/d DEPARTURE_DESTINATION```
+* Flight return destination - ```/r RETURN_DESTINATION```
+* Flight departure date and time - ```/dd DEPARTURE_TIME```
+* Flight return date and time - ```/rd RETURN_TIME```
+
+:exclamation: Note that the given date and times should be of the format: ```d/M/yy HH:mm```. TourPlanner will
+**reject** any entry that violates this format rule.
+
+Example:
+
+* `add -f SQ-JPN1 /d Japan /r Singapore /dd 29/10/21 13:00 /rd 5/11/21 02:00`
+
+Add flights from _Singapore_ to _Japan_
+and back, departing from Singapore at _1pm, 29 Oct 2021_ and returning to Singapore at _5 Nov 2021, 2am_. Stored in the
+database as ID: _SQ-JPN1_.
+
+* `add -f SQ-KOR1 /d Korea /dd 2/5/22 11:00 /r Singapore /rd 15/5/22 23:00`
+
+Add flights from _Singapore_ to _Korea_
+and back, departing from Korea at _11am, 2 May 2022_ and returning to Singapore at _15 May 2022, 11pm_. Stored in the
+database as ID: _SQ-KOR1_.
+
+:exclamation: Note that the given **date-times** should be logically coherent
+(i.e. arrival at destination should be before return). TourPlanner will reject erroneous inputs with an **error**
+message.
+
+
+
+### Adding tours into database: ```add -t```
+
+Format: ```add [DATA_TYPE] [TOUR_ID] [DATA_FIELDS]```
+
+Mandatory data fields:
+
+* Tour ID - ```TOUR_ID```
+* Tour name - ```/n TOUR_NAME```
+* Tour price - ```/p TOUR_PRICE```
+
+:exclamation: Note that the given **price** should be a *positive numerical value* (up to 2 decimal places).
+
+Example:
+
+* `add -t t001 /n AustralianRomance /p 1500`
+
+Adds _AustralianRomance_, which costs _$1500_, as tour _t001_.
+
+* `add -t t002 /p 2300 /n KoreanWonderland`
+
+Adds _KoreanWonderland_, which costs _$2300_, as tour _t002_.
+
+
+
+### Adding ClientPackage into database: ```add -p```
+
+ClientPackage acts as a master list of details for each client, to have an overview of the client upon querying. Each
+package represents one client, with his or her respective tour and flight details.
+
+Format: ```add [DATA_TYPE] [PACKAGE_ID] [DATA_FIELDS]```
+
+Mandatory data fields:
+
+* Package ID - ```PACKAGE_ID```
+* Client ID - ```/c CLIENT_ID```
+* Flight ID - ```/f FLIGHT_ID```
+* Tour ID - ```/t TOUR_ID```
+
+:exclamation: Note that the respective client, flight and tour must **exist** in order for package to be added.
+
+Example:
+
+* `add -p p001 /c c001 /f SQ-JPN1 /t t001`
+
+Adds Client _c001_, Flight _SQ-JPN1_, and Tour _t001_ into an overall package _p001_.
+
+* `add -p p002 /c c002 /t t002 /f SQ-KOR1 `
+
+Adds Client _c002_, Flight _SQ-KOR1_, and Tour _t002_ into an overall package _p002_.
+
+
+
+[**Return to Table of Contents**](#table-of-contents)
+
+
+
+## :scissors: Cutting data types: ```cut```
+
+Deletes entry of a certain data type.
+
+
+
+### Cut Client Package
+
+Deletes the client package from the list of packages.
+
+Format: `cut -p DATA_ID`
+
+* Deletes the client package with specified DATA_ID.
+
+
+Examples:
+
+* `cut -p p001` deletes client package with id 'p001', where 'p001' contains client 'c001', tour 'JPN1' and flight '
+ SQ-JPN'
+
+An output of this format will be shown:
+
+```
+Client has been deleted:
+Package ID: p001
+
+Client:
+Client ID: c001
+Name: Adam
+Contact Number: 93338333
+Email: adam@mail.com
+
+Tour:
+Name: Japan Basic Tour
+Code: JPN1
+Price per pax: $1500.00
+
+Flight:
+Flight ID: SQ-JPN
+Departure Flight: JPN, 20/10/21 18:00
+Return Flight: SG, 21/10/21 03:00
+```
+
+
+
+### Cut Client / Tour / Flight
+
+Format: `cut [DATA_TYPE] DATA_ID`
+
+* Deletes the entry of DATA_TYPE with specified DATA_ID.
+* Deletes all client packages that contains the specific entry
+
+
+Examples:
+
+* `cut -c c001` deletes client with id 'c001', and all client packages that contains client 'c001'.
+
+An output of this format will be shown:
+
+Deleting client:
+
+```
+Client has been deleted:
+Client ID: c001
+Name: Adam
+Contact Number: 93338333
+Email: adam@mail.com
+```
+
+Deleting all related client packages:
+
+```
+Client Package has been deleted:
+Package ID: p001
+
+Client:
+Client ID: c001
+Name: Adam
+Contact Number: 93338333
+Email: adam@mail.com
+
+Tour:
+Name: Japan Basic Tour
+Code: JPN1
+Price per pax: $1500.00
+
+Flight:
+Flight ID: SQ-JPN
+Departure Flight: JPN, 20/10/21 18:00
+Return Flight: SG, 21/10/21 03:00
+```
+
+
+
+* `cut -t KOR` deletes tour with id 'KOR', and all client packages that contains tour 'KOR'.
+
+An output of this format will be shown:
+
+Deleting tour:
+
+```
+Tour has been deleted:
+Name: Korea Cultural Tour
+Code: KOR
+Price per pax: $3000.00
+```
+
+Deleting all related client packages:
+
+```
+Client Package has been deleted:
+Package ID: p001
+
+Client:
+Client ID: c001
+Name: Adam
+Contact Number: 93338333
+Email: adam@mail.com
+
+Tour:
+Name: Korea Cultural Tour
+Code: KOR
+Price per pax: $3000.00
+
+Flight:
+Flight ID: SQ-JPN
+Departure Flight: JPN, 20/10/21 18:00
+Return Flight: SG, 21/10/21 03:00
+```
+
+
+
+[**Return to Table of Contents**](#table-of-contents)
+
+## **Querying Data Types**
+
+
+
+The user is also able to view all entries of a specific data type, as well as find specific entrie(s) based their codes.
+
+
+
+:information_source: Please refer to Introduction to Data Types on the syntax of ```[DATA_TYPE]```
+
+
+
+## :mag_right: Listing data types: ```list```
+
+Shows a list of all available entries of a specific data type, along with their respective fields.
+
+Format: ```list [DATA_TYPE]```
+
+
+Examples:
+
+* ```list -c``` lists out all available client entries.
+
+An output of this format will be shown:
+
+```
+Here is a list of all clients:
+1. Client ID: c001
+Name: Adam
+Contact Number: 93338333
+Email: adam@mail.com
+
+2. Client ID: c002
+Name: Betty
+Contact Number: 12223444
+Email: betty@mail.com
+
+Total Clients: 2
+```
+
+
+
+* ```list -f``` lists out all available flight entries.
+
+An output of this format will be shown:
+
+```
+Here is a list of all flights:
+1. Flight ID: SQ-JPN
+Departure Flight: JPN, 20/10/2021 18:00
+Return Flight: SG, 21/10/2021 03:00
+
+2. Flight ID: SQ-KOR
+Departure Flight: KOR, 23/10/2021 18:00
+Return Flight: SG, 30/10/2021 03:00
+
+Total Flights: 2
+```
+
+
+
+[**Return to Table of Contents**](#table-of-contents)
+
+## Finding data types: ```find```
+
+### Find client
+
+Finds specific client(s) based on a particular substring. It will return client(s) if their name contains the substring.
+Note that the substring is case-insensitive. In addition, it will return the found clients' subscribed packages.
+
+
+
+Format: ```find -c [SUBSTRING]```
+
+Examples:
+
+* ```find -c Adam``` finds clients that contain 'Adam' in their name.
+
+An output of this format will be shown:
+
+```
+This is the client(s) that matches your search
+1. Client ID: c001
+Name: Adam
+Contact Number: 93338333
+Email: adam@mail.com
+
+Package ID: p123
+
+Client:
+Client ID: c001
+Name: Adam
+Contact Number: 93338333
+Email: adam@mail.com
+
+Tour:
+Name: australiaromance
+Code: aus1369
+Price per pax: $1300.00
+
+Flight:
+Flight ID: MSIA-KOR1
+Departure Flight: MSIA, 29/10/21 12:00
+Return Flight: KOR, 24/6/21 02:00
+```
+
+In addition, ```find -c ad``` will yield the same results, since "ad" is contained in "Adam".
+
+
+### Find tour / flight
+
+Finds a specific entry based on a particular code. In addition, for tours and flights, it will show the names of the
+subscriber(s) / passenger(s) who are assigned to them respectively.
+
+
+
+Format: ```find [DATA_TYPE] [ID]```
+
+Examples:
+
+* ```find -t JPN``` finds a particular tour with code 'JPN'. It also shows the clients who are subscribed to said tour.
+
+An output of this format will be shown:
+
+```
+This is the tour that matches your search
+Name: Japan Basic Tour
+Code: JPN
+Price per pax: $1500.00
+
+
+Subscribed Clients:
+Adam
+
+Total Subscribed Clients: 1
+```
+
+
+
+* ```find -f SQ-JPN``` finds a particular flight with code 'SQ-JPN'. It also shows the clients who are passengers to
+ said flight.
+
+An output of this format will be shown:
+
+```
+This is the flight that matches your search
+Flight ID: SQ-JPN
+Departure Flight: JPN, 20/10/2021 18:00
+Return Flight: SG, 21/10/2021 03:00
+
+
+Passengers:
+Betty
+
+Total Passengers: 1
+```
+
+
+
+[**Return to Table of Contents**](#table-of-contents)
+
+## :chart_with_upwards_trend: Sorting data types: ```sort```
+
+Sort a specific data type based on a particular ```[FILTER]```. It will return all client(s) in ascending alphabetical
+order. the possible values of ```[FILTER]``` varies between data types.
+
+Format: ```sort [DATA_TYPE] [FILTER]```
+
+
+
+### Sort client
+
+The possible values of ```[FILTER]``` are:
+
+* ```/n``` to sort by client name
+* ```/id``` to sort by client id
+
+
+
+Examples:
+
+* ```sort -c /n``` sorts the clients by client name alphabetically.
+
+An output of this format will be shown:
+
+```
+Sorted by client name alphabetically
+1. Client ID: c001
+Name: Bo Tuan
+Contact Number: 93338333
+Email: borangutuan@mail.com
+
+2. Client ID: c004
+Name: ChengXu
+Contact Number: 10101010
+Email: demonshaha@mail.com
+
+3. Client ID: c002
+Name: Sam
+Contact Number: 12223444
+Email: sam@mail.com
+
+4. Client ID: c003
+Name: Wayne
+Contact Number: 56667888
+Email: wendy@mail.com
+```
+
+
+
+* ```sort -c /id``` sorts the clients by id alphabetically.
+
+An output of this format will be shown:
+
+```
+orted by client id alphabetically
+1. Client ID: c001
+Name: Bo Tuan
+Contact Number: 93338333
+Email: borangutuan@mail.com
+
+2. Client ID: c002
+Name: Sam
+Contact Number: 12223444
+Email: sam@mail.com
+
+3. Client ID: c003
+Name: Wayne
+Contact Number: 56667888
+Email: wendy@mail.com
+
+4. Client ID: c004
+Name: ChengXu
+Contact Number: 10101010
+```
+
+
+
+### Sort tour
+
+Sort tour(s) based on a particular ```[FILTER]```. It will return all tour(s) in ascending alphabetical order.
+
+The possible values of ```[FILTER]``` are:
+
+* ```/id``` to sort by tour id
+* ```/p``` to sort by price
+
+
+
+Examples:
+
+* ```sort -t /id``` sorts the tours by tour id alphabetically.
+
+An output of this format will be shown:
+
+```
+Sorted by tour id alphabetically
+1. Name: Japan Basic Tour
+Code: JPN1
+Price per pax: $1500.00
+
+2. Name: Japan Food Tour
+Code: JPN2
+Price per pax: $4000.00
+
+3. Name: Korea Cultural Tour
+Code: KOR
+Price per pax: $3000.00
+
+4. Name: Zimbabwe Tour
+Code: ZWM
+Price per pax: $1700.00
+```
+
+
+
+* ```sort -t /n``` sorts the tours by tour name alphabetically.
+
+```
+Sorted by tour name alphabetically
+1. Name: Japan Basic Tour
+Code: JPN1
+Price per pax: $1500.00
+
+2. Name: Japan Food Tour
+Code: JPN2
+Price per pax: $4000.00
+
+3. Name: Korea Cultural Tour
+Code: KOR
+Price per pax: $3000.00
+
+4. Name: Zimbabwe Tour
+Code: ZWM
+Price per pax: $1700.00
+```
+
+
+
+* ```sort -t /p``` sorts the tours by price in ascending order.
+
+An output of this format will be shown:
+
+```
+Sorted by tour id alphabetically
+1. Name: Japan Basic Tour
+Code: JPN1
+Price per pax: $1500.00
+
+2. Name: Zimbabwe Tour
+Code: ZWM
+Price per pax: $1700.00
+
+3. Name: Korea Cultural Tour
+Code: KOR
+Price per pax: $3000.00
+
+4. Name: Japan Food Tour
+Code: JPN2
+Price per pax: $4000.00
+```
+
+
+
+### Sort flights
+
+Sort flight(s) based on a particular ```[FILTER]```. It will return all flight(s) in ascending alphabetical order.
+
+The possible values of ```[FILTER]``` are:
+
+* ```/id``` to sort by flight id
+* ```/d``` to sort by departing flight times
+* ```/r``` to sort by returning flight times
+
+
+
+Examples:
+
+* ```sort -f /id``` sorts the flights by flight id alphabetically.
+
+An output of this format will be shown:
+
+```
+Sorted by departing flight times
+Sorted by flight id alphabetically
+1. Flight ID: SQ-JPN
+Departure Flight: JPN, 20/10/21 18:00
+Return Flight: SG, 21/10/21 03:00
+
+2. Flight ID: SQ-KOR
+Departure Flight: KOR, 23/10/21 08:00
+Return Flight: SG, 30/11/21 03:00
+
+3. Flight ID: SQ-ZWM
+Departure Flight: ZWM, 5/11/21 09:00
+Return Flight: SG, 7/11/21 15:00
+```
+
+
+
+* ```sort -f /d``` sorts the flights by departing returning flight times.
+
+An output of this format will be shown:
+
+```
+Sorted by departing flight times
+1. Flight ID: SQ-JPN
+Departure Flight: JPN, 20/10/21 18:00
+Return Flight: SG, 21/10/21 03:00
+
+2. Flight ID: SQ-KOR
+Departure Flight: KOR, 23/10/21 08:00
+Return Flight: SG, 30/11/21 03:00
+
+3. Flight ID: SQ-ZWM
+Departure Flight: ZWM, 5/11/21 09:00
+Return Flight: SG, 7/11/21 15:00
+```
+
+
+
+* ```sort -f /r``` sorts the flights by ascending returning flight times.
+
+An output of this format will be shown:
+
+```
+Sorted by returning flight times
+1. Flight ID: SQ-JPN
+Departure Flight: JPN, 20/10/21 18:00
+Return Flight: SG, 21/10/21 03:00
+
+2. Flight ID: SQ-ZWM
+Departure Flight: ZWM, 5/11/21 09:00
+Return Flight: SG, 7/11/21 15:00
+
+3. Flight ID: SQ-KOR
+Departure Flight: KOR, 23/10/21 08:00
+Return Flight: SG, 30/11/21 03:00
+```
+
+
+
+[**Return to Table of Contents**](#table-of-contents)
+
+## Exit application: ```bye```
+
+
+
+Exits the application.
+
+
+
+Format: ```bye```
+
+The following output will be shown:
+
+```Thanks for using TourPlanner. Goodbye!```
+
+
+
+[**Return to Table of Contents**](#table-of-contents)
+
+## Saving Data
+
+
+
+In TourPlanner, all data (clients, tours, flights, client packages) are saved in the hard disk
+ONLY after the user exits the application with the ```bye``` command.
+
+
+
+## **Command Summary**
+
+
+
+### **General Command Information**
+
+Command | Format
+------------ | -------------
+```help``` | ```help```
+```add``` | ```add [DATA_TYPE] [ID] [DATA_FIELDS] ```
+```cut``` | ```cut [DATA_TYPE] [ID]```
+```list``` | ```list [DATA_TYPE]```
+```find``` | Clients: ```find [DATA_TYPE] [SUBSTRING]```
Tour / Flights: ```find [DATA_TYPE] [ID]```
+```sort``` | ```sort [DATA_TYPE] [FILTER]```
+```bye``` | ```bye```
+
+
+
+### **Supporting Command Information**
+
+Command | Data Type | Data Fields
+------------ | ------------- |-------------
+```help``` | - | -
+```add``` Clients | `-c` | `/id`, `/n`, `/cn`, `/m`
+```add``` Flights | `-f` |`/id`, `/d`, `/r`, `/dd`, `/rd`
+```add``` Tours | `-t` |`/id`, `/n`, `/p`
+```add``` Packages | `-p` |`/id`, `/c`, `/f`, `/t`
+```cut``` | `-c`
`-f`
`-t`
`-p`| -
+```list``` | `-c`
`-f`
`-t`
`-p` | -
+```find``` | `-c`
`-f`
`-t` | -
+```sort``` Clients | `-c` | `/id`, `/n`
+```sort``` Flights | `-f` |`/id`,`/d`, `/r`
+```sort``` Tours | `-t` |`/id`,`/n`, `/p`
+```bye``` | - | -
+
+
+
+[**Return to Table of Contents**](#table-of-contents)
diff --git a/docs/_config.yml b/docs/_config.yml
new file mode 100644
index 0000000000..ce1b35c5da
--- /dev/null
+++ b/docs/_config.yml
@@ -0,0 +1,2 @@
+theme: jekyll-theme-time-machine
+gems: ["jemoji"]
diff --git a/docs/team/Demonshaha.md b/docs/team/Demonshaha.md
new file mode 100644
index 0000000000..d9b76fef29
--- /dev/null
+++ b/docs/team/Demonshaha.md
@@ -0,0 +1,55 @@
+# Zhou Chengxu - Project Portfolio Page
+
+## Overview - TourPlanner
+
+TourPlanner is a data management solution, for tour agencies. The user interacts with it using a CLI, to update a
+database with flights, tours, clients, and combine them into packages.
+
+It is written in Java, and has about 7kLoC.
+
+Given below are the contributions to the project.
+
+* Feature: **Storage** - Added 4 class files to let the program be able to load and save the files to data folder(Because my vpn cannot work, bobowoo2468 helped
+ me to push)
+
+ * What it does: Allows the program to load the clients, tours, flights, and clientpackages that user added before, and save the data they add every time.
+
+ * Justification: This function is essential, because Touragencies need to save all the data they have added in, without the storage function, this program
+ cannot save the data users have added every time the user close the program, all of the data will disappear.
+
+ * Highlight: In order to make the program convinient for the user, the clients, tours, flights, clientpakcages will be stored to 4 different files in data folder.
+
+* Feature: **Add's exception** --Add exceptions to add commantd which help to check human error
+
+ * What it does: Help to check if the information input by the user is correct.
+ Including:
+ 1. Email checking
+ 2. Contact number checking
+ 3. Price checking
+ 4. Departure date and return date checking
+
+ * Justification: This can help to check human errors which can improve the effciency of users and this is one of the advantage of our program. For example, if the employee
+ of the touragencies input the departure time and return time reversely, the program can warning him or her.
+
+ * Highlights: For date checking, it implements java.data class to check the if the departure date and return dates are illegal and if they are reverse.
+
+* Task: **Storage** methods and code structure
+* Task: **Modify clientlist, tourlist, fightlist, clientpackagelist classes** in order to implement storage
+* Task: **Modify** toString methods
+
+
+
+Code contributed: : [Reposense link](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=demonshaha&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-25&tabOpen=true&tabType=authorship&tabAuthor=Demonshaha&tabRepo=AY2122S1-CS2113T-F11-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false)
+
+
+
+* Documentation
+ * User Guide:
+ * Added sort commands guideline
+
+ * Developers Guide:
+ * Added implementation details for Storage
+
+* Coummunity:
+ * Reviewed and approved PRs frequently
+ * Give suggestions on codes.
diff --git a/docs/team/YipWayne.md b/docs/team/YipWayne.md
new file mode 100644
index 0000000000..6087e367fd
--- /dev/null
+++ b/docs/team/YipWayne.md
@@ -0,0 +1,64 @@
+# Yip Wayne - Project Portfolio Page
+
+## Overview - TourPlanner
+
+TourPlanner is a data management solution, for tour agencies. The user interacts with it using a CLI, to update a
+database with flights, tours, clients, and combine them into packages.
+
+It is written in Java, and has about 7 kLoC.
+
+Given below are my contributions to the project.
+
+* Feature: **LIST** - Added the ability to **list** clients, flights, tours and client in the database.
+ * What it does: Allows the user to list a certain data type by typing into the CLI input, in the format of:
+ `list [DATA_TYPE]`
+
+ * Justification: This is the functionality that is core to the program, as it allows the travel agency employee to view all the clients,
+ tours, flights and client packages at a quick glance.
+
+ * Highlights: The list feature required the elements of a certain data type to be printed incrementally. Since each data
+ type has its own attributes, a custom method to print each type of data had to be used.
+
+* Feature: **FIND** - Added the ability to **find** clients, flights, tours and client in the database.
+ * What it does: Allows the user to find a certain data type by typing into the CLI input, in the format of:
+ `list [DATA_TYPE] [SUBSTRING]` for clients, and `list [DATA_TYPE] [ID]` for the other tours and flights.
+
+ * Justification: This is the functionality that is core to the program. In the event that there is a lot of data within the database, this
+ function allows the travel agency employee to quickly find the specific entry that they are looking for.
+
+ * Highlights: The find feature was different between the data types. Hence, separate implementations were required.
+
+ * Highlights: For finding tours and flights, it also printed out the relevant clients that subscribed to them. This implementation
+ was challenging as required interaction with other object classes, such as the ClientPackage class.
+
+* Task: General implementation of Ui class
+* Task: General implementation of Tour and TourList classes
+* Task: Implementation of list and find-related Parser exception functions
+
+
+* Code contributed: : [Reposense link](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-25&tabOpen=true&tabType=authorship&tabAuthor=YipWayne&tabRepo=AY2122S1-CS2113T-F11-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&reverseAuthorshipOrder=true)
+
+
+* Project Management:
+ * Created general idea and structure for this project.
+ * Created issues and milestones for Milestone v1.0 and some of 2.0
+ * Approved and reviewed PRs frequently.
+
+
+* Documentation:
+ * User Guide:
+ * Added documentation for the features: `list` amd `find`
+ * Added `Quick Start` and `Introduction to Data Types`
+ * Added `Command summary` table for UG
+ * Did clean-up, make sure structure was consistent among members
+
+ * Developers Guide:
+ * Added Table of Contents (TOC) and navigation for DG
+ * Added `Architecture` section along with relevant UML diagrams
+ * Added documentation for the feature `find`, along with relevant UML diagrams
+ * Addeed `Getting Started` and `User Stories` section
+
+
+* Community:
+ * Reviewed and approved PRs frequently
+ * Made suggestions for the other teams in the class during tutorial
diff --git a/docs/team/bobowoo2468.md b/docs/team/bobowoo2468.md
new file mode 100644
index 0000000000..a02000a9b9
--- /dev/null
+++ b/docs/team/bobowoo2468.md
@@ -0,0 +1,66 @@
+# Woo Bo Tuan - Project Portfolio Page
+
+## Overview - TourPlanner
+
+TourPlanner is a data management solution, for tour agencies. The user interacts with it using a CLI, to update a
+database with flights, tours, clients, and combine them into packages.
+
+It is written in Java, and has about 7 kLoC.
+
+Given below are the contributions to the project.
+
+* Feature: **ADD** - Added the ability to **add** clients, flights, tours into the database.
+ * What it does: Allows the user to add by typing into the CLI input, in the format of:
+ `add -[IDENTIFIER] /PREFIX1 DATA1 /PREFIX2 DATA2 ...`
+
+ * Justification: This is the functionality that is core to the program, as it allows the input of data into
+ database. As such, many exceptions have to be handled, along with catchers for erroneous inputs.
+
+ * Highlights: The add feature implemented (including parsing of user input) allows for greater flexibility during
+ input. The prefixes can be arranged in any order during adding, and additional white spaces in the command will be
+ trimmed.
+
+ * Highlights: The exception handling for add is rather extensive. First, erroneous inputs such as missing
+ identifier, missing prefixes (hence data fields), duplicated prefixes, missing data fields will all be flagged as
+ an error. Erroneous or illogical entries will either be flagged out as a **WARNING** or an **ERROR**, depending on
+ its severity.
+
+* Feature: **SORT** - Added the ability to **sort** clients, flights, tours in the database.
+ * What it does: Allows the user to sort, in the format of:
+ `sort -[IDENTIFIER] /FILTER`
+
+ * Justification: This is an additional functionality for tour agency planners to be able to perform meaningful
+ operations with data. For example, sorting by ascending price can allow tour agency planners to have a
+ side-by-side comparison of the budget of tours, from the least expensive to the most expensive tours.
+
+ * Highlights: Sorting is mostly implemented with using the natural sorting of String or Float. Sorting for local date-time requires
+ the need for a defined `Comparator`. The program has allowed for sorting regardless of duplicates in the data.
+
+* Task: General **Parser** methods and code structure
+* Task: **Refactor code** to follow OOP guidelines
+* Task: **Linking Storage Class** to Main, and ObjectLists
+
+* Code
+ contributed: [Reposense link](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-25&tabOpen=true&tabType=authorship&tabAuthor=bobowoo2468&tabRepo=AY2122S1-CS2113T-F11-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&reverseAuthorshipOrder=true)
+
+* Project Management:
+ * Managed releases for v2.0 on GitHub
+ * Created the *developers* team and *team repo*, including settings for GitHub workflow
+ * Set-up GHPages for `docs` (containing UG and DG)
+ * Managed *issues*, *assigned* work and *tracked* completion for Milestone v2.0
+ * Approved and reviewed PRs frequently.
+
+* Documentation:
+ * User Guide:
+ * Added Table of Contents (TOC) and navigation for UG
+ * Added documentation for the features: `add`
+ * Added `How-to-use` for UG
+ * Added `Supporting Command Information` table for UG
+
+ * Developers Guide:
+ * Added implementation details for the `add` feature
+ * Added implementation details for the `Parser` feature
+
+* Community:
+ * Reviewed and approved PRs frequently
+ * Made suggestions for the other teams in the class during tutorial
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index ab75b391b8..0000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# John Doe - Project Portfolio Page
-
-## Overview
-
-
-### Summary of Contributions
diff --git a/docs/team/swongts.md b/docs/team/swongts.md
new file mode 100644
index 0000000000..0cb4d4b1d0
--- /dev/null
+++ b/docs/team/swongts.md
@@ -0,0 +1,53 @@
+# Samantha Wong - Project Portfolio Page
+
+## Overview: TourPlanner
+**TourPlanner** is a desktop application meant for employees of travel agencies.
+Its main purpose is to manage clients, flights, accommodations and client packages data, optimized for use via a Command Line Interface (CLI).
+
+It is written in Java, and has about 7 kLoC.
+
+### Summary of Contributions
+**Code contributed**: [RepoSense Link](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=F11&sort=groupTitle&sortWithin=title&since=2021-09-25&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=swongts&tabRepo=AY2122S1-CS2113T-F11-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false)
+
+**Enhancements implemented**:
+* `ClientPackage` and `ClientPackageList` classes
+ * What it does: Stores the related `Client`, `Flight` and `Tour` into one `ClientPackage`.
+ * Justification: This feature is crucial to TourPlanner and serves as a link between these classes.
+ * Highlights: To make it easier to use and more efficient for commands like `find` and `cut` to use,
+`ClientPackage` stores the actual `Client`, `Flight` and `Tour` objects instead of simply using a reference String id.
+ * Highlights: Created methods to get an ArrayList of `ClientPackage` based on the `Client` / `Flight` / `Tour` which
+were used across a few features.
+
+* `AddClientPackageCommand`
+ * What it does: Allows user to create the `ClientPackage` using the client, flight and tour id.
+ * Highlights: The command searches for the ids with the respective `ObjectList` to find the
+`Object` (`Client`, `Tour`, `Flight`) to store into `ClientPackage`.
+
+* `CutCommand` for `Client`, `Tour`, `Flight` and `ClientPackage`
+ * What it does: Deletes the specific `Object` from its respective `ObjectList`.
+ * Justification: Crucial feature for the application to be functional.
+ * Highlights: When the `Client` / `Tour` / `Flight` is deleted, the corresponding `ClientPackage` which contains the specific
+`Object` must also be deleted from the `ClientPackageList`. This is so that it wouldn't be incoherent that the `ClientPackage`
+contains an `Object` which no longer exists.
+
+* `HelpCommand`
+ * What it does: Lists all available commands.
+ * Justification: Allows user to have an overview of how to use the commands conveniently.
+
+**Contributions to documentation**:
+* User guide:
+ * Added `cut` feature
+* Developer guide:
+ * Added diagrams and explanations for `cut`, `sort`, `list` feature and `Command` class
+ * Added Appendix D and E
+
+**Contributions to team-based tasks**:
+* Manage issue tracker: In charge of issue tracker for `v2.1`, helped with labels for `v1.0`.
+* Release management: In charge of release for `v1.0`.
+* General code enhancement: Helped look out for code quality, and refactor other's codes to ensure SLAP
+(e.g. `TourPlanner`).
+
+**Review/mentoring contributions**:
+* Major bug fixes to ClientPackageStorage as the client packages weren't loading properly from storage.
+* Frequently reviewed and approved pull requests.
+* Helped to proof-read Developer's Guide and link everyone's parts to ensure it sounds coherent.
\ No newline at end of file
diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..3c45a3b725
--- /dev/null
+++ b/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: seedu.duke.TourPlanner
+
diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java
deleted file mode 100644
index 5c74e68d59..0000000000
--- a/src/main/java/seedu/duke/Duke.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package seedu.duke;
-
-import java.util.Scanner;
-
-public class Duke {
- /**
- * Main entry-point for the java.duke.Duke application.
- */
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- System.out.println("What is your name?");
-
- Scanner in = new Scanner(System.in);
- System.out.println("Hello " + in.nextLine());
- }
-}
diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java
new file mode 100644
index 0000000000..afb4b34d0a
--- /dev/null
+++ b/src/main/java/seedu/duke/Parser.java
@@ -0,0 +1,756 @@
+package seedu.duke;
+
+import seedu.duke.commands.ByeCommand;
+import seedu.duke.commands.Command;
+import seedu.duke.commands.HelpCommand;
+import seedu.duke.commands.clientpackages.AddClientPackageCommand;
+import seedu.duke.commands.clientpackages.CutClientPackageCommand;
+import seedu.duke.commands.clientpackages.ListClientPackageCommand;
+import seedu.duke.commands.clients.AddClientCommand;
+import seedu.duke.commands.clients.CutClientCommand;
+import seedu.duke.commands.clients.FindClientCommand;
+import seedu.duke.commands.clients.ListClientCommand;
+import seedu.duke.commands.clients.SortClientCommand;
+import seedu.duke.commands.flights.AddFlightCommand;
+import seedu.duke.commands.flights.CutFlightCommand;
+import seedu.duke.commands.flights.FindFlightCommand;
+import seedu.duke.commands.flights.ListFlightCommand;
+import seedu.duke.commands.flights.SortFlightCommand;
+import seedu.duke.commands.tours.AddTourCommand;
+import seedu.duke.commands.tours.CutTourCommand;
+import seedu.duke.commands.tours.FindTourCommand;
+import seedu.duke.commands.tours.ListTourCommand;
+import seedu.duke.commands.tours.SortTourCommand;
+import seedu.duke.data.Client;
+import seedu.duke.data.Flight;
+import seedu.duke.data.Tour;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Sense-makes the inputs given and distributes the information to other parts of the program.
+ */
+public class Parser {
+
+ private static final String CLIENT_IDENTIFIER = "-c";
+ private static final String TOUR_IDENTIFIER = "-t";
+ private static final String FLIGHT_IDENTIFIER = "-f";
+ private static final String PACKAGE_IDENTIFIER = "-p";
+
+ private static final String PACKAGE_CLIENT_PREFIX = "/c";
+ private static final String PACKAGE_TOUR_PREFIX = "/t";
+ private static final String PACKAGE_FLIGHT_PREFIX = "/f";
+
+ private static final String FLIGHT_DEPARTURE_PREFIX = "/d";
+ private static final String FLIGHT_RETURN_PREFIX = "/r";
+ private static final String FLIGHT_DEPARTURE_DATE_PREFIX = "/dd";
+ private static final String FLIGHT_RETURN_DATE_PREFIX = "/rd";
+ public static final String TOUR_NAME_PREFIX = "/n";
+ public static final String TOUR_PRICE_PREFIX = "/p";
+
+ public static final String CLIENT_NAME_PREFIX = "/n";
+ public static final String CLIENT_CONTACT_NUMBER_PREFIX = "/cn";
+ public static final String CLIENT_EMAIL_PREFIX = "/m";
+
+ public static final String ERROR_INVALID_INPUT = "ERROR: TourPlanner cannot understand the command! "
+ + "Please enter a valid command.\n"
+ + "Type 'help' to view a list of commands available for use.";
+ public static final String ERROR_MISSING_IDENTIFIER =
+ "ERROR: Missing/wrong command filter used! Please enter a command with this format: COMMAND -FILTER DATA \n"
+ + "Example: find -t TOUR_ID";
+ public static final String ERROR_DUPLICATE_PREFIXES = "ERROR: TourPlanner has detected duplicate prefixes!";
+ public static final String ERROR_MISSING_PREFIXES
+ = "ERROR: TourPlanner has detected missing/wrong prefixes! Did you miss out some fields?";
+ public static final String ERROR_MISSING_ID = "Missing id that you wish to cut from! Please try again.";
+ public static final String ERROR_MISSING_FIELDS = "ERROR: TourPlanner has detected empty fields! "
+ + "Please enter all fields!";
+ public static final String ERROR_FLIGHT_TIME_FORMAT = "ERROR: TourPlanner detected wrong date-time entry "
+ + "formatting! \n Please input your date-times with the following format: d/M/yy HH:mm";
+ public static final String ERROR_FLIGHT_TIME_INVERT = "ERROR: TourPlanner detected erroneous flight time entry! \n";
+ public static final String ERROR_PRICE_FORMAT = "ERROR: TourPlanner has detected erroneous price entry! "
+ + "Only include numbers (includes decimal)/one decimal point!";
+
+ public static final String WARNING_EXTRA_INPUT = "WARNING: Extra input! Refrain from doing so.";
+ public static final String WARNING_EMAIL_FORMAT_WRONG = "WARNING: TourPlanner has detected possible "
+ + "erroneous email! \n";
+ public static final String WARNING_CONTACT_NUMBER_WRONG =
+ "WARNING: TourPlanner has detected possible erroneous contact number! (characters other than numbers) \n";
+ public static final String WARNING_SHORT_CONTACT_NUMBER =
+ "WARNING: TourPlanner detected that the given contact number is too short (< 8)! \n";
+ private static final String WARNING_LONG_CONTACT_NUMBER =
+ "WARNING: TourPlanner detected that the given contact number is too long (> 8)! \n";
+ private static final String WARNING_PRICE_TOO_MANY_DECIMAL = "WARNING: TourPlanner has detected "
+ + "erroneous price entry! Price has too many decimal places!";
+
+ public static final int IDENTIFIER_INDEX = 0;
+ public static final int ARGS_INDEX = 1;
+ public static final int COMMAND_INDEX = 0;
+ public static final int PARAMS_INDEX = 1;
+ public static final int MAX_VALUE_ARRAY_SIZE = 5;
+
+ //Client has three prefixes: /n, /cn, /m
+ private static final int CLIENT_PREFIX_NUM = 4;
+
+ //Flight has four prefixes: /d, /r, /dd, /rd
+ private static final int FLIGHT_PREFIX_NUM = 5;
+
+ //Tour has three prefixes: /n, /p
+ private static final int TOUR_PREFIX_NUM = 3;
+
+ // Package has four prefixes: /c, /f, /t
+ private static final int PACKAGE_PREFIX_NUM = 4;
+
+ public static final String EMPTY_STRING = "";
+
+ /**
+ * Parses user's input into command to execute.
+ *
+ * @param input full user's input string
+ * @return the command parsed from user's input
+ * @throws TourPlannerException if there are missing fields, duplicated prefixes, missing prefixes and other general
+ * parsing errors
+ */
+ public static Command parse(String input) throws TourPlannerException {
+ String[] commandAndParams = splitCommandString(input);
+ String command = commandAndParams[COMMAND_INDEX];
+ String params = commandAndParams[PARAMS_INDEX];
+
+ switch (command) {
+ case "add":
+ return parseAdd(params);
+ case "bye":
+ return parseBye(params);
+ case "cut":
+ return parseCut(params);
+ case "find":
+ return parseFind(params);
+ case "help":
+ return parseHelp(params);
+ case "list":
+ return parseList(params);
+ case "sort":
+ return parseSort(params);
+ default:
+ throw new TourPlannerException(ERROR_INVALID_INPUT);
+ }
+ }
+
+ /**
+ * Separates user input into 2 based on the separator.
+ * If the value of the String after the separator is null,
+ * the second value of the String array will be returned as "".
+ *
+ * @param input full user's input string
+ * @return the array containing command and argument/params strings
+ */
+ private static String[] splitCommandString(String input) {
+ String[] split = input.trim().split(" ", 2);
+ return split.length == 2 ? split : new String[]{split[0], ""};
+ }
+
+ /**
+ * Extracts the indexes for prefixes and put into a map that sorts the list by the natural ordering of the keys.
+ *
+ * @param argString full user's argument string
+ * @return the treemap with prefix index as the key and the corresponding prefix as the value
+ * @throws TourPlannerException if there are missing fields or missing prefixes
+ */
+ private static TreeMap extractPrefixIndexes(String argString, String identifier)
+ throws TourPlannerException {
+
+ List prefixes = generatePrefixesFromIdentifier(identifier);
+ if (!containAllPrefixes(argString, prefixes)) {
+ throw new TourPlannerException(ERROR_MISSING_PREFIXES);
+ }
+
+ TreeMap prefixIndexes = new TreeMap<>();
+ prefixIndexes.put(0, "");
+ prefixes.forEach((prefix) -> {
+ int prefixIndex = argString.indexOf(prefix);
+ prefixIndexes.put(prefixIndex, prefix);
+ });
+
+ int expectedNumberOfPrefixIndexes = generateExpectedNumberOfPrefixIndexes(identifier);
+ boolean hasUniquePrefixIndexes = prefixIndexes.size() == expectedNumberOfPrefixIndexes;
+
+ if (!hasUniquePrefixIndexes) {
+ throw new TourPlannerException(ERROR_MISSING_FIELDS);
+ }
+ return prefixIndexes;
+ }
+
+ /**
+ * Returns the number of prefixes for the specific data type, determined by the identifier.
+ *
+ * @param identifier specific identifier to determine specific data type (client, flight, tour, package)
+ * @return the number of prefixes determined by the identifier
+ */
+ private static int generateExpectedNumberOfPrefixIndexes(String identifier) {
+ int numberOfPrefixIndexes = 0;
+ switch (identifier) {
+ case CLIENT_IDENTIFIER:
+ numberOfPrefixIndexes = CLIENT_PREFIX_NUM;
+ break;
+ case PACKAGE_IDENTIFIER:
+ numberOfPrefixIndexes = PACKAGE_PREFIX_NUM;
+ break;
+ case TOUR_IDENTIFIER:
+ numberOfPrefixIndexes = TOUR_PREFIX_NUM;
+ break;
+ case FLIGHT_IDENTIFIER:
+ numberOfPrefixIndexes = FLIGHT_PREFIX_NUM;
+ break;
+ default:
+ break;
+ }
+ return numberOfPrefixIndexes;
+ }
+
+ /**
+ * Extract values from user's input command in a sorted fashion.
+ *
+ * @param prefixIndexes the treemap with prefix index as the key and the corresponding prefix as the value
+ * @param argString full user's argument string
+ * @param identifier specific identifier to determine specific data type (client, flight, tour, package)
+ * @return the array containing all extracted values in a sorted fashion
+ * @throws TourPlannerException if there are duplicate prefixes found
+ */
+ private static ArrayList extractValuesIntoArray(TreeMap prefixIndexes,
+ String argString, String identifier)
+ throws TourPlannerException {
+ ArrayList extractedValues = new ArrayList<>();
+ initialiseArrayList(extractedValues);
+ ArrayList indexes = new ArrayList<>();
+ ArrayList prefixes = new ArrayList<>();
+ for (Map.Entry prefixIndex : prefixIndexes.entrySet()) {
+ indexes.add(prefixIndex.getKey());
+ prefixes.add(prefixIndex.getValue());
+ }
+
+ for (int i = 0; i < indexes.size() - 1; i++) {
+ int previousIndex = indexes.get(i);
+ int nextIndex = indexes.get(i + 1);
+ String prefix = prefixes.get(i);
+ String value = extractValue(argString, prefix, previousIndex, nextIndex, identifier);
+ int inputIndex = obtainArrayIndex(prefix, identifier);
+ extractedValues.set(inputIndex, value);
+ }
+
+ String finalPrefix = prefixes.get(indexes.size() - 1);
+ int finalIndex = indexes.get(indexes.size() - 1);
+
+ int inputIndex = obtainArrayIndex(finalPrefix, identifier);
+ String value = extractValue(argString, finalPrefix, finalIndex, argString.length(), identifier);
+ extractedValues.set(inputIndex, value);
+ return extractedValues;
+ }
+
+ /**
+ * Initialise an empty array list of size 5 - max array size for values.
+ *
+ * @param extractedValues empty extracted values array to be initialised with empty string values
+ */
+ private static void initialiseArrayList(ArrayList extractedValues) {
+ for (int i = 0; i < MAX_VALUE_ARRAY_SIZE; i++) {
+ extractedValues.add("");
+ }
+ }
+
+ /**
+ * Extract value from a substring of the user's argument string, according to prefix.
+ *
+ * @param argString full user's argument string
+ * @param prefix prefix of value to be extracted
+ * @param startIndex start index of substring
+ * @param endIndex end index of substring
+ * @param identifier specific identifier to determine specific data type (client, flight, tour, package)
+ * @return value corresponding to prefix given
+ * @throws TourPlannerException if there are duplicate prefixes found
+ */
+ private static String extractValue(String argString, String prefix, int startIndex, int endIndex, String identifier)
+ throws TourPlannerException {
+ List prefixes = generatePrefixesFromIdentifier(identifier);
+ String unformattedSubstring = argString.substring(startIndex, endIndex).trim();
+ String value = unformattedSubstring.replaceFirst(prefix, "").trim();
+
+ if (value.equals(EMPTY_STRING)) {
+ throw new TourPlannerException(ERROR_MISSING_FIELDS);
+ }
+ for (String pf : prefixes) {
+ boolean hasDuplicatePrefix = value.contains(pf);
+ if (hasDuplicatePrefix) {
+ throw new TourPlannerException(ERROR_DUPLICATE_PREFIXES);
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Return the list of prefixes for each data type.
+ *
+ * @param identifier specific identifier to determine specific data type (client, flight, tour, package)
+ * @return the list of prefixes, as specified by the identifier
+ * @throws TourPlannerException if the identifier is invalid
+ */
+ private static List generatePrefixesFromIdentifier(String identifier) throws TourPlannerException {
+ List prefixes;
+ switch (identifier) {
+ case CLIENT_IDENTIFIER:
+ prefixes = Arrays.asList(CLIENT_NAME_PREFIX, CLIENT_CONTACT_NUMBER_PREFIX, CLIENT_EMAIL_PREFIX);
+ break;
+ case TOUR_IDENTIFIER:
+ prefixes = Arrays.asList(TOUR_NAME_PREFIX, TOUR_PRICE_PREFIX);
+ break;
+ case FLIGHT_IDENTIFIER:
+ prefixes = Arrays.asList(FLIGHT_DEPARTURE_PREFIX, FLIGHT_RETURN_PREFIX,
+ FLIGHT_DEPARTURE_DATE_PREFIX, FLIGHT_RETURN_DATE_PREFIX);
+ break;
+ case PACKAGE_IDENTIFIER:
+ prefixes = Arrays.asList(PACKAGE_CLIENT_PREFIX, PACKAGE_TOUR_PREFIX, PACKAGE_FLIGHT_PREFIX);
+ break;
+ default:
+ throw new TourPlannerException(ERROR_MISSING_IDENTIFIER);
+ }
+ return prefixes;
+ }
+
+ /**
+ * Obtains array index that corresponds to the prefix given.
+ *
+ * @param prefix prefix of value extracted
+ * @return array index of values according to prefix
+ */
+ private static int obtainArrayIndex(String prefix, String identifier) {
+ int index;
+
+ switch (identifier) {
+ case CLIENT_IDENTIFIER:
+ index = obtainClientArrayIndex(prefix);
+ break;
+ case TOUR_IDENTIFIER:
+ index = obtainTourArrayIndex(prefix);
+ break;
+ case FLIGHT_IDENTIFIER:
+ index = obtainFlightArrayIndex(prefix);
+ break;
+ case PACKAGE_IDENTIFIER:
+ index = obtainPackageArrayIndex(prefix);
+ break;
+ default:
+ index = 0;
+ break;
+ }
+ return index;
+ }
+
+ /**
+ * Returns a value's insertion index into the ClientPackage input array, based on the given prefix.
+ * ClientPackage array: (Package ID, Client id, Tour id, Flight id)
+ *
+ * @param prefix prefix of value extracted
+ * @return respective insertion index
+ */
+ private static int obtainPackageArrayIndex(String prefix) {
+ int index;
+
+ switch (prefix) {
+ case PACKAGE_CLIENT_PREFIX:
+ index = 1;
+ break;
+ case PACKAGE_TOUR_PREFIX:
+ index = 2;
+ break;
+ case PACKAGE_FLIGHT_PREFIX:
+ index = 3;
+ break;
+ default:
+ index = 0;
+ break;
+ }
+ return index;
+ }
+
+ /**
+ * Returns a value's insertion index into the Flight input array, based on the given prefix.
+ * Flight array: (ID, Departure destination, Return destination, Departure Date,Return Date)
+ *
+ * @param prefix prefix of value extracted
+ * @return respective insertion index
+ */
+ private static int obtainFlightArrayIndex(String prefix) {
+ int index;
+
+ switch (prefix) {
+ case FLIGHT_DEPARTURE_PREFIX:
+ index = 1;
+ break;
+ case FLIGHT_RETURN_PREFIX:
+ index = 2;
+ break;
+ case FLIGHT_DEPARTURE_DATE_PREFIX:
+ index = 3;
+ break;
+ case FLIGHT_RETURN_DATE_PREFIX:
+ index = 4;
+ break;
+ default:
+ index = 0;
+ break;
+ }
+ return index;
+ }
+
+ /**
+ * Returns a value's insertion index into the Tour input array, based on the given prefix.
+ * Tour array: (ID, Name, Price)
+ *
+ * @param prefix prefix of value extracted
+ * @return respective insertion index
+ */
+ private static int obtainTourArrayIndex(String prefix) {
+ int index;
+
+ switch (prefix) {
+ case TOUR_NAME_PREFIX:
+ index = 1;
+ break;
+ case TOUR_PRICE_PREFIX:
+ index = 2;
+ break;
+ default:
+ index = 0;
+ break;
+ }
+ return index;
+ }
+
+ /**
+ * Returns a value's insertion index into the Client input array, based on the given prefix.
+ * Client array: (ID, Name, Contact Number, eMail)
+ *
+ * @param prefix prefix of value extracted
+ * @return respective insertion index
+ */
+ private static int obtainClientArrayIndex(String prefix) {
+ int index;
+
+ switch (prefix) {
+ case CLIENT_NAME_PREFIX:
+ index = 1;
+ break;
+ case CLIENT_CONTACT_NUMBER_PREFIX:
+ index = 2;
+ break;
+ case CLIENT_EMAIL_PREFIX:
+ index = 3;
+ break;
+ default:
+ index = 0;
+ break;
+ }
+ return index;
+ }
+
+ /**
+ * Returns true if all prefixes are present in add command's argument string.
+ *
+ * @param argString full user's argument string
+ * @return true if all prefixes are present in add command's argument string
+ */
+ private static boolean containAllPrefixes(String argString, List prefixList) {
+
+ String[] splitBySpaces = argString.trim().split("\\s+");
+ String[] prefixes = prefixList.toArray(new String[0]);
+ for (String prefix : prefixes) {
+ boolean containPrefix = false;
+ for (String substring : splitBySpaces) {
+ if (prefix.equals(substring)) {
+ containPrefix = true;
+ break;
+ }
+ }
+ if (!containPrefix) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Handles all exceptions regarding inputs for add tour command.
+ * Throws TourPlannerException when there are duplicate decimal points (.), or additional characters in price
+ * that prevent parsing.
+ * Prints a warning if price has too many divisions, i.e. decimal places
+ *
+ * @param values the array of values extracted from user's input after parsing
+ * @throws TourPlannerException if there are duplicate decimal points, additional characters in price
+ */
+ private static void handleTourException(String[] values) throws TourPlannerException {
+ String price = values[2];
+ int decimalCount = (int) price.chars().filter(ch -> ch == '.').count();
+ String priceAfterParseString = price.replaceAll("[^0-9.]", "");
+ boolean containsAdditionalDecimalPoints = decimalCount > 1;
+ if (containsAdditionalDecimalPoints) {
+ throw new TourPlannerException(ERROR_PRICE_FORMAT);
+ }
+
+ boolean containsAdditionalCharacters = !price.equals(priceAfterParseString);
+
+ if (containsAdditionalCharacters) {
+ throw new TourPlannerException(ERROR_PRICE_FORMAT);
+ }
+
+ if (decimalCount == 1) {
+ int decimalIndex = price.indexOf(".");
+ int numberOfDecimalPlaces = price.length() - decimalIndex - 1;
+
+ if (numberOfDecimalPlaces > 2) {
+ System.out.println(WARNING_PRICE_TOO_MANY_DECIMAL);
+ }
+ }
+ }
+
+ /**
+ * Handles all exceptions regarding inputs for add flight command.
+ *
+ * @param values the array of values extracted from user's input after parsing
+ * @throws TourPlannerException if there are logical errors with date-time input (i.e. date-time of flight to
+ * destination location is after date-time of return flight)
+ * or date-time parsing/format errors
+ */
+ private static void handleFlightException(String[] values) throws TourPlannerException {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yy HH:mm");
+ LocalDateTime start;
+ LocalDateTime end;
+ String startDateString = values[3];
+ String endDateString = values[4];
+
+ try {
+ start = LocalDateTime.parse(startDateString, formatter);
+ end = LocalDateTime.parse(endDateString, formatter);
+ } catch (DateTimeParseException e) {
+ throw new TourPlannerException(ERROR_FLIGHT_TIME_FORMAT);
+ }
+ if (end.isBefore(start) || end.equals(start)) {
+ throw new TourPlannerException(ERROR_FLIGHT_TIME_INVERT);
+ }
+ }
+
+ /**
+ * Handles all exceptions regarding inputs for add client command.
+ * Prints warnings for erroneous contact number (inclusive of characters other than numbers, length != 8)
+ * Also prints warnings for erroneous email (lacking/more than one '@' symbol or period '.', that are commonly
+ * found in emails).
+ *
+ * @param values the array of values extracted from user's input after parsing
+ */
+ private static void handleClientException(String[] values) {
+ String contactNum = values[2];
+ String email = values[3];
+ int adSymbolCount = (int) email.chars().filter(ch -> ch == '@').count();
+ int periodCount = (int) email.chars().filter(ch -> ch == '.').count();
+
+ if (adSymbolCount != 1 || periodCount != 1) {
+ System.out.println(WARNING_EMAIL_FORMAT_WRONG);
+ }
+
+ boolean isWrongContactNumber = false;
+ int contactNumberLength = contactNum.length();
+ for (int i = 0; i < contactNumberLength; i++) {
+ char ch = contactNum.charAt(i);
+ if (!(ch <= '9' && ch >= '0')) {
+ isWrongContactNumber = true;
+ break;
+ }
+ }
+ if (isWrongContactNumber) {
+ System.out.println(WARNING_CONTACT_NUMBER_WRONG);
+ } else if (contactNumberLength < 8) {
+ System.out.println(WARNING_SHORT_CONTACT_NUMBER);
+ } else if (contactNumberLength > 8) {
+ System.out.println(WARNING_LONG_CONTACT_NUMBER);
+ }
+ }
+
+ /**
+ * Parses arguments with respect to the add command.
+ * Extract values from user's input and passes it as an argument to construct Client/Flight/Tour object
+ * depending on the identifier.
+ * Passes the created object to the specific AddCommand, determined by the identifier.
+ *
+ * @param params full user's argument string
+ * @return the specific AddCommand object determined by the command's identifier
+ * @throws TourPlannerException if there are missing fields,duplicated or missing prefixes
+ */
+ private static Command parseAdd(String params) throws TourPlannerException {
+ String[] identifierAndArgs = splitCommandString(params);
+ String identifier = identifierAndArgs[IDENTIFIER_INDEX];
+ String args = identifierAndArgs[ARGS_INDEX];
+
+ TreeMap prefixIndexes = extractPrefixIndexes(args, identifier);
+ ArrayList valuesList = extractValuesIntoArray(prefixIndexes, args, identifier);
+ String[] values = valuesList.toArray(new String[0]);
+
+ switch (identifier) {
+ case CLIENT_IDENTIFIER:
+ handleClientException(values);
+ Client client = new Client(values);
+ return new AddClientCommand(client);
+ case FLIGHT_IDENTIFIER:
+ handleFlightException(values);
+ Flight flight = new Flight(values);
+ return new AddFlightCommand(flight);
+ case TOUR_IDENTIFIER:
+ handleTourException(values);
+ Tour tour = new Tour(values);
+ return new AddTourCommand(tour);
+ case PACKAGE_IDENTIFIER:
+ return new AddClientPackageCommand(values);
+ default:
+ throw new TourPlannerException(ERROR_MISSING_IDENTIFIER);
+ }
+ }
+
+ /**
+ * Parses arguments with respect to the bye command.
+ *
+ * @param params full user's argument string
+ * @return ByeCommand object
+ */
+ private static Command parseBye(String params) {
+ if (!params.equals(EMPTY_STRING)) {
+ System.out.println(WARNING_EXTRA_INPUT);
+ }
+ return new ByeCommand();
+ }
+
+ /**
+ * Parses user input, identifies data type to be cutting from based on the command filter,
+ * and returns the corresponding subclass of Command.
+ *
+ * @param params user input excluding "cut"
+ * @return command corresponding to the data type of the command filter
+ * @throws TourPlannerException if command filter is wrong/missing or id to cut is wrong/missing
+ */
+ private static Command parseCut(String params) throws TourPlannerException {
+ String[] identifierAndArgs = splitCommandString(params);
+ String identifier = identifierAndArgs[IDENTIFIER_INDEX];
+ String args = identifierAndArgs[ARGS_INDEX];
+
+ switch (identifier) {
+ case CLIENT_IDENTIFIER:
+ handleCutException(args);
+ return new CutClientCommand(args);
+ case TOUR_IDENTIFIER:
+ handleCutException(args);
+ return new CutTourCommand(args);
+ case FLIGHT_IDENTIFIER:
+ handleCutException(args);
+ return new CutFlightCommand(args);
+ case PACKAGE_IDENTIFIER:
+ handleCutException(args);
+ return new CutClientPackageCommand(args);
+ default:
+ throw new TourPlannerException(ERROR_MISSING_IDENTIFIER);
+ }
+ }
+
+ private static void handleCutException(String args) throws TourPlannerException {
+ if (args.equals(EMPTY_STRING)) {
+ throw new TourPlannerException(ERROR_MISSING_ID);
+ }
+ }
+
+ /**
+ * Parses the parameters given to determine which FindXYZCommand to be called for.
+ *
+ * @param params full user's argument string
+ * @return a FindXYZCommand of a specific data type (client, tour, flight, client package)
+ * @throws TourPlannerException if there are missing fields, duplicated or missing prefixes
+ */
+ private static Command parseFind(String params) throws TourPlannerException {
+ String[] prefixSuffix = params.split(" ", 2);
+ if (prefixSuffix.length < 2) {
+ throw new TourPlannerException(ERROR_MISSING_IDENTIFIER);
+ }
+ String prefix = prefixSuffix[IDENTIFIER_INDEX];
+ String suffix = prefixSuffix[ARGS_INDEX];
+ switch (prefix) {
+ case CLIENT_IDENTIFIER:
+ return new FindClientCommand(suffix);
+ case TOUR_IDENTIFIER:
+ return new FindTourCommand(suffix);
+ case FLIGHT_IDENTIFIER:
+ return new FindFlightCommand(suffix);
+ default:
+ throw new TourPlannerException(ERROR_MISSING_IDENTIFIER);
+ }
+ }
+
+ /**
+ * Parses arguments with respect to the help command.
+ *
+ * @param params full user's argument string
+ * @return HelpCommand object
+ */
+ private static Command parseHelp(String params) {
+ if (!params.equals(EMPTY_STRING)) {
+ System.out.println(WARNING_EXTRA_INPUT);
+ }
+ return new HelpCommand();
+ }
+
+ /**
+ * Parses the parameters given to determine which ListXYZCommand to be called for.
+ *
+ * @param params full user's argument string
+ * @return a ListXYZCommand of a specific data type (client, tour, flight, client package)
+ * @throws TourPlannerException if there are missing fields, duplicated or missing prefixes
+ */
+ private static Command parseList(String params) throws TourPlannerException {
+ switch (params) {
+ case CLIENT_IDENTIFIER:
+ return new ListClientCommand();
+ case TOUR_IDENTIFIER:
+ return new ListTourCommand();
+ case FLIGHT_IDENTIFIER:
+ return new ListFlightCommand();
+ case PACKAGE_IDENTIFIER:
+ return new ListClientPackageCommand();
+ default:
+ throw new TourPlannerException(ERROR_MISSING_IDENTIFIER);
+ }
+ }
+
+ /**
+ * Parses arguments with respect to the sort command.
+ *
+ * @param params full user's argument string
+ * @return the specific SortCommand object determined by the command's identifier
+ * @throws TourPlannerException if there are missing fields,duplicated or missing prefixes
+ */
+ private static Command parseSort(String params) throws TourPlannerException {
+ String[] identifierAndFilter = splitCommandString(params);
+ String identifier = identifierAndFilter[IDENTIFIER_INDEX];
+ String filter = identifierAndFilter[ARGS_INDEX];
+ switch (identifier) {
+ case TOUR_IDENTIFIER:
+ return new SortTourCommand(filter);
+ case CLIENT_IDENTIFIER:
+ return new SortClientCommand(filter);
+ case FLIGHT_IDENTIFIER:
+ return new SortFlightCommand(filter);
+ default:
+ throw new TourPlannerException(ERROR_MISSING_IDENTIFIER);
+ }
+ }
+}
+
+
diff --git a/src/main/java/seedu/duke/TourPlanner.java b/src/main/java/seedu/duke/TourPlanner.java
new file mode 100644
index 0000000000..ae138ed6fd
--- /dev/null
+++ b/src/main/java/seedu/duke/TourPlanner.java
@@ -0,0 +1,157 @@
+package seedu.duke;
+
+import seedu.duke.commands.Command;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.TourList;
+import seedu.duke.storage.ClientPackageStorage;
+import seedu.duke.storage.ClientStorage;
+import seedu.duke.storage.FlightStorage;
+import seedu.duke.storage.TourStorage;
+
+import java.io.IOException;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.FileHandler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+import java.util.logging.SimpleFormatter;
+
+/**
+ * Main entry-point of the TourPlanner application.
+ * Initialises the application and starts interaction with application user.
+ */
+public class TourPlanner {
+ private static Ui ui;
+ private static ClientList clients;
+ private static TourList tours;
+ private static FlightList flights;
+ private static ClientPackageList clientPackages;
+
+ private static ClientPackageStorage clientPackageStorage;
+ private static ClientStorage clientStorage;
+ private static TourStorage tourStorage;
+ private static FlightStorage flightStorage;
+
+ private static final Logger logr = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+
+ public TourPlanner() {
+ }
+
+ /**
+ * Main method of TourPlanner.
+ * Initialises Ui and ClientList objects.
+ * Reads, parses and executes command from user's input until exit condition is met.
+ *
+ * @param args not used
+ */
+ public static void main(String[] args) {
+ loadLogger();
+ loadStorage();
+ ui.showWelcome();
+ runCommandLoopUntilExitCommand();
+ }
+
+ /**
+ * Sets up logger for TourPlanner.
+ */
+ private static void loadLogger() {
+ LogManager.getLogManager().reset();
+ logr.setLevel(Level.ALL);
+ ConsoleHandler consoleHandler = new ConsoleHandler();
+ consoleHandler.setLevel(Level.OFF);
+ logr.addHandler(consoleHandler);
+
+ try {
+ FileHandler fileHandler = new FileHandler("TourPlannerLogger.log");
+ fileHandler.setLevel(Level.ALL);
+ fileHandler.setFormatter(new SimpleFormatter());
+ logr.addHandler(fileHandler);
+ } catch (IOException e) {
+ logr.log(Level.SEVERE, "File logger not working", e);
+ }
+ }
+
+ /**
+ * Populates the ClientList, TourList, FlightList and ClientPackageList from previously stored files.
+ */
+ private static void loadStorage() {
+ try {
+ initialize();
+ loadClientTourFlight();
+ loadClientPackage();
+ } catch (TourPlannerException e) {
+ ui.showFileError();
+ System.exit(0);
+ }
+ }
+
+ private static void initialize() throws TourPlannerException {
+ ui = new Ui();
+ clientPackageStorage = new ClientPackageStorage();
+ clientStorage = new ClientStorage();
+ tourStorage = new TourStorage();
+ flightStorage = new FlightStorage();
+ }
+
+ /**
+ * Populates the ClientList, TourList, FlightList from previously stored files.
+ *
+ * @throws TourPlannerException if there are IOException, FileNotFoundException for the storage file
+ */
+ private static void loadClientTourFlight() throws TourPlannerException {
+ clientStorage.loadFile();
+ clients = clientStorage.getClients();
+ tourStorage.loadFile();
+ tours = tourStorage.getTours();
+ flightStorage.loadFile();
+ flights = flightStorage.getFlights();
+ }
+
+ /**
+ * Populates ClientPackageList from previously stored files.
+ *
+ * @throws TourPlannerException if there are IOException, FileNotFoundException for the storage file
+ */
+ private static void loadClientPackage() throws TourPlannerException {
+ clientPackageStorage.loadFile(clients, tours, flights, ui);
+ clientPackages = clientPackageStorage.getClientPackages();
+ }
+
+ /**
+ * Loop that reads command from Ui, parses command and executes command.
+ * Loop ends when ByeCommand is called and isExit is set to true.
+ */
+ private static void runCommandLoopUntilExitCommand() {
+ boolean isExit = false;
+ while (!isExit) {
+ String command = ui.readCommand();
+ try {
+ Command specificCommand = Parser.parse(command);
+ specificCommand.setData(clients, flights, tours, clientPackages, ui);
+ specificCommand.execute();
+ isExit = specificCommand.isExit();
+ } catch (NullPointerException e) {
+ logr.log(Level.SEVERE, e.getMessage());
+ } catch (TourPlannerException e) {
+ ui.show(e.getMessage());
+ logr.log(Level.WARNING, e.getMessage());
+ } finally {
+ ui.showLine();
+ saveToStorage();
+ }
+ }
+ }
+
+ /**
+ * Saves the ClientList, FlightList, TourList and ClientPackageList to text files in the data folder.
+ */
+ private static void saveToStorage() {
+ clientStorage.saveFile();
+ flightStorage.saveFile();
+ tourStorage.saveFile();
+ clientPackageStorage.saveFile();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/duke/TourPlannerException.java b/src/main/java/seedu/duke/TourPlannerException.java
new file mode 100644
index 0000000000..9c41210752
--- /dev/null
+++ b/src/main/java/seedu/duke/TourPlannerException.java
@@ -0,0 +1,16 @@
+package seedu.duke;
+
+/**
+ * Represents a generalised error thrown when there are erroneous/out-of-bounds/missing inputs.
+ */
+public class TourPlannerException extends Exception {
+ /**
+ * Class constructor for TourPlannerException.
+ * Returns an error message to the user to inform user of error.
+ *
+ * @param errorMessage the customised error message corresponding to the error made
+ */
+ public TourPlannerException(String errorMessage) {
+ super(errorMessage);
+ }
+}
diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java
new file mode 100644
index 0000000000..f63dbe2a56
--- /dev/null
+++ b/src/main/java/seedu/duke/Ui.java
@@ -0,0 +1,580 @@
+package seedu.duke;
+
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.Tour;
+import seedu.duke.data.TourList;
+import seedu.duke.data.Flight;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.ClientPackage;
+import seedu.duke.data.ClientPackageList;
+
+
+import java.util.ArrayList;
+import java.util.Scanner;
+
+/**
+ * Text UI of the application.
+ */
+public class Ui {
+ public static final String ADD_CLIENT_MESSAGE = "Client has been added:";
+ public static final String ADD_FLIGHT_MESSAGE = "Flight has been added:";
+ public static final String ADD_TOUR_MESSAGE = "Tour has been added:";
+ public static final String ADD_CLIENT_PACKAGE_MESSAGE = "Client package has been added:";
+
+ public static final String CUT_CLIENT_MESSAGE = "Client has been deleted:";
+ public static final String CUT_FLIGHT_MESSAGE = "Flight has been deleted:";
+ public static final String CUT_TOUR_MESSAGE = "Tour has been deleted:";
+ public static final String CUT_CLIENT_PACKAGE_MESSAGE = "Client package has been deleted:";
+
+ public static final String LIST_NO_MESSAGE = "I'm sorry, there seems to be no ";
+ public static final String LIST_MESSAGE = "Here is a list of all ";
+ public static final String FIND_FAIL_MESSAGE_LEFT = "I'm sorry, there seems to be no ";
+ public static final String FIND_SUCCESS_MESSAGE_LEFT = "This is the ";
+ public static final String FIND_MESSAGE_RIGHT = "that matches your search";
+ public static final String BYE_MESSAGE = "Thanks for using TourPlanner. Goodbye!";
+
+ public static final String SORT_TOUR_ID_MESSAGE = "Sorted by tour id alphabetically";
+ public static final String SORT_TOUR_NAME_MESSAGE = "Sorted by tour name alphabetically";
+ public static final String SORT_TOUR_PRICE_MESSAGE = "Sorted by price in ascending order";
+ public static final String SORT_CLIENT_ID_MESSAGE = "Sorted by client id alphabetically";
+ public static final String SORT_CLIENT_NAME_MESSAGE = "Sorted by client name alphabetically";
+ public static final String SORT_FLIGHT_BY_DEPARTURE_MESSAGE = "Sorted by departing flight times";
+ public static final String SORT_FLIGHT_BY_ARRIVAL_MESSAGE = "Sorted by returning flight times";
+ public static final String SORT_FLIGHT_ID_MESSAGE = "Sorted by flight id alphabetically";
+
+ private static final String FILE_CORRUPT = "TourPlanner can't read your corrupted file.\n"
+ + "Please delete your 'data' folder and try again.";
+
+ private static final Scanner in = new Scanner(System.in);
+
+ /**
+ * Empty Ui class constructor.
+ */
+ public Ui() {
+ }
+
+ /**
+ * Shows a divider to the user.
+ */
+ public void showLine() {
+ show("____________________________________________________________");
+ }
+
+ /**
+ * Shows a welcome message to the user.
+ */
+ public void showWelcome() {
+ showLine();
+ String logo = " _____ _____ _\n"
+ + " |_ _| ___ _ _ ___ | _ || | ___ _____ _____ ___ ___\n"
+ + " | | | || || || _|| ___|| || \\ | _ || _ || -_|| _|\n"
+ + " | | | | || || || | | | | || | || | | || | | || |_ | |\n"
+ + " |_| |___||____||_| |_| |_||___|||_| |_||_| |_||___||_|\n";
+ String greet = "Hello, Welcome to TourPlanner!\n"
+ + "What can I do for you?";
+ show(logo);
+ show(greet);
+ showLine();
+ }
+
+ /**
+ * Shows a help message to the user, which includes all available commands in TourPlanner.
+ */
+ public void showHelp() {
+ String add = "add: Add information of all data types into the database.\n"
+ + "Prefixes can be input in any order.\n"
+ + " Add client: add -c CLIENT_ID /n NAME /cn CONTACT_NUM /m EMAIL\n"
+ + " Add flight: add -f FLIGHT_ID /d DEPART_DESTINATION /r RETURN_DESTINATION\n"
+ + " /dd DEPARTURE_DATETIME /rd RETURN_DATETIME\n"
+ + " Add tour: add -t TOUR_ID /n DEPART_DESTINATION /p TOUR_PRICE\n"
+ + " Add client package: add -p PACKAGE_ID /c CLIENT_ID /t TOUR_ID /f FLIGHT_ID\n\n";
+ String list = "list: Shows a list of all available entries of a specific data type, along with their "
+ + "respective fields.\n"
+ + " List client: list -c\n"
+ + " List flight: list -f\n"
+ + " List tour: list -t\n"
+ + " List client package: list -p\n\n";
+ String cut = "cut: Deletes entry of a certain data type and all client packages corresponding to the entry.\n"
+ + " Cut client: cut -c CLIENT_ID\n"
+ + " Cut flight: cut -f FLIGHT_ID\n"
+ + " Cut tour: cut -t TOUR_ID\n"
+ + " Cut client package: cut -p PACKAGE_ID\n\n";
+ String find = "find: Finds specific entry of data type, returns the entry and other relevant information.\n"
+ + " Find client: find -c CLIENT_NAME\n"
+ + " Find flight: find -f FLIGHT_ID\n"
+ + " Find tour: find -t TOUR_ID\n\n";
+ String sort = "sort: Sorts entries in the data type based on the criteria.\n "
+ + " Sort client:\n"
+ + " Sort by id: sort -c /id\n"
+ + " Sort by name: sort -c /n\n"
+ + " Sort flight:\n"
+ + " Sort by id: sort -f /id\n"
+ + " Sort by departure date: sort -f /d\n"
+ + " Sort by return date: sort -f /r\n"
+ + " Sort tour:\n"
+ + " Sort by id: sort -t /id\n"
+ + " Sort by name: sort -t /n\n"
+ + " Sort by price: sort -t /p\n\n";
+ String bye = "bye: Exits the program.";
+ show(add + list + cut + find + sort + bye);
+ }
+
+ /**
+ * Prompts for user's input and read's the text entered by the user.
+ *
+ * @return full input entered by the user
+ */
+ public String readCommand() {
+ return in.nextLine();
+ }
+
+ /**
+ * Outputs to the CLI the text to show the user.
+ * Creates a newline as well.
+ *
+ * @param textToShow the output text intended for the user
+ */
+ public void show(String textToShow) {
+ System.out.print(textToShow + System.lineSeparator());
+ }
+
+ /**
+ * Ui response to cut command.
+ * Checks the type of object before showing a customised message for deleting the specific object.
+ *
+ * @param object the object that was cut
+ */
+ public void showCut(Object object) {
+ if (object instanceof Client) {
+ show(CUT_CLIENT_MESSAGE + "\n" + object);
+ } else if (object instanceof Flight) {
+ show(CUT_FLIGHT_MESSAGE + "\n" + object);
+ } else if (object instanceof Tour) {
+ show(CUT_TOUR_MESSAGE + "\n" + object);
+ } else if (object instanceof ClientPackage) {
+ show(CUT_CLIENT_PACKAGE_MESSAGE + "\n" + object);
+ }
+ }
+
+ /**
+ * Ui response to add command.
+ * Checks the type of object before showing a customised message for deleting the specific object.
+ *
+ * @param object the object that was cut
+ */
+ public void showAdd(Object object) {
+ if (object instanceof Client) {
+ show(ADD_CLIENT_MESSAGE + "\n" + object);
+ } else if (object instanceof Flight) {
+ show(ADD_FLIGHT_MESSAGE + "\n" + object);
+ } else if (object instanceof Tour) {
+ show(ADD_TOUR_MESSAGE + "\n" + object);
+ } else if (object instanceof ClientPackage) {
+ show(ADD_CLIENT_PACKAGE_MESSAGE + "\n" + object);
+ }
+ }
+
+ /**
+ * Ui response to ListClientCommand.
+ * Shows all clients in the clientList on the terminal.
+ *
+ * @param clients the ClientList from which the clients are obtained from.
+ */
+ public void showListClient(ClientList clients) {
+ int count = clients.getClientCount();
+ if (count == 0) {
+ show(LIST_NO_MESSAGE + "clients");
+ return;
+ }
+ show(LIST_MESSAGE + "clients:");
+ for (int i = 1; i <= count; i++) {
+ Client currClient = clients.getClientByIndex(i - 1);
+ show(i + ". " + currClient + "\n");
+ }
+ show("Total Clients: " + count);
+ }
+
+ /**
+ * Ui response to FindClientCommand.
+ * Shows specific client(s) based on a susbtring, as well as the client packages they are a part of.
+ *
+ * @param clients the ClientList from which the clients are obtained from.
+ * @param clientPackages the ClientPackageList from which the client packages of the client are obtained from.
+ * @param substring the substring used to obtain the specific client(s).
+ */
+ public void showFindClient(ClientList clients, ClientPackageList clientPackages, String substring) {
+ String lowercaseName = substring.toLowerCase();
+ int foundClients = 0;
+ int count = clients.getClientCount();
+ for (int i = 0; i < count; i++) {
+ Client currClient = clients.getClientByIndex(i);
+ if (currClient.getName().toLowerCase().contains(lowercaseName)) {
+ if (foundClients == 0) {
+ show(FIND_SUCCESS_MESSAGE_LEFT + "client(s) " + FIND_MESSAGE_RIGHT);
+ }
+ show((foundClients + 1) + ". " + currClient + "\n");
+ showFlightTourOfClient(currClient, clientPackages);
+ foundClients++;
+ }
+ }
+ if (foundClients == 0) {
+ show(FIND_FAIL_MESSAGE_LEFT + "client(s) " + FIND_MESSAGE_RIGHT);
+ }
+ }
+
+ /**
+ * Shows clientPackages that a certain client is a part of.
+ *
+ * @param currClient the client which the shown clientPackages must contain.
+ * @param clientPackages the ClientPackageList from which the client packages of the client are obtained from.
+ */
+ private void showFlightTourOfClient(Client currClient, ClientPackageList clientPackages) {
+ ArrayList clientPackagesWithClient;
+ clientPackagesWithClient = clientPackages.getClientPackageByClient(currClient);
+ for (ClientPackage clientPackage : clientPackagesWithClient) {
+ System.out.println(clientPackage + "\n");
+ }
+ }
+
+ /**
+ * Ui response to ListTourCommand.
+ * Shows all tours in the tourList on the terminal.
+ *
+ * @param tours the TourList from which the tours are obtained from.
+ */
+ public void showListTour(TourList tours) {
+ int count = tours.getTourCount();
+ if (count == 0) {
+ show(LIST_NO_MESSAGE + "tours");
+ return;
+ }
+ show(LIST_MESSAGE + "tours:");
+ for (int i = 1; i <= count; i++) {
+ Tour currTour = tours.getTourByIndex(i - 1);
+ show(i + ". " + currTour + "\n");
+ }
+ show("Total Tours: " + count);
+ }
+
+ /**
+ * Ui response to FindTourCommand.
+ * Shows specific tour based on an ID, as well as the subscribed clients for said tour.
+ *
+ * @param tours the TourList from which the tours are obtained from.
+ * @param clientPackages the ClientPackageList from which the subscribers of the tour are obtained from.
+ * @param id the ID used to find the specific tour.
+ */
+ public void showFindTour(TourList tours, ClientPackageList clientPackages, String id)
+ throws TourPlannerException {
+ Tour foundTour = tours.getTourById(id);
+ if (foundTour != null) {
+ show(FIND_SUCCESS_MESSAGE_LEFT + "tour " + FIND_MESSAGE_RIGHT);
+ show(foundTour + "\n" + "\n");
+ int subbedClients = 0;
+ int count = clientPackages.getClientPackageCount();
+ show("Subscribed Clients:");
+ for (int i = 0; i < count; i++) {
+ Tour currTour = clientPackages.getClientPackageByIndex(i).getTour();
+ if (currTour.equals(foundTour)) {
+ String currClientName = clientPackages.getClientPackageByIndex(i).getClient().getName();
+ String currClientId = clientPackages.getClientPackageByIndex(i).getClient().getId();
+ show((subbedClients + 1) + ". " + currClientName + " (ID: " + currClientId + ")");
+ subbedClients++;
+ }
+ }
+ show("\n" + "Total Subscribed Clients: " + subbedClients);
+ } else {
+ show(FIND_FAIL_MESSAGE_LEFT + "tours " + FIND_MESSAGE_RIGHT);
+ }
+ }
+
+ private boolean checkEmptyTours(TourList tours) {
+ int count = tours.getTourCount();
+ if (count == 0) {
+ show(LIST_NO_MESSAGE + "tours");
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Ui response to sort tour by id.
+ *
+ * @param tours the current list of tours in the database
+ * @param sortedTourIds the list of sorted tour codes/ids (by alphabetical order)
+ * @throws TourPlannerException if there is no tours that can be found given the tour code
+ */
+ public void showSortedTourById(TourList tours, ArrayList sortedTourIds)
+ throws TourPlannerException {
+ if (checkEmptyTours(tours)) {
+ return;
+ }
+ show(SORT_TOUR_ID_MESSAGE);
+ int listIndex = 1;
+ for (String tourId : sortedTourIds) {
+ Tour currTour = tours.getTourById(tourId);
+ show(listIndex + ". " + currTour + System.lineSeparator());
+ listIndex++;
+ }
+ }
+
+ /**
+ * Ui response to sort tour by price.
+ *
+ * @param tours the current list of tours in the database
+ * @param sortedTourPrices the list of sorted tour prices (by ascending order)
+ * @throws TourPlannerException if there is no tours that can be found given the tour price
+ */
+ public void showSortedTourByPrice(TourList tours, ArrayList sortedTourPrices) throws TourPlannerException {
+ if (checkEmptyTours(tours)) {
+ return;
+ }
+ tours.initTempArray();
+ show(SORT_TOUR_PRICE_MESSAGE);
+ int listIndex = 1;
+ for (Float tourPrice : sortedTourPrices) {
+ Tour currTour = tours.getTourByPrice(tourPrice);
+ show(listIndex + ". " + currTour + System.lineSeparator());
+ listIndex++;
+ }
+ }
+
+ /**
+ * Ui response to sort tour by name.
+ *
+ * @param tours the current list of tours in the database
+ * @param sortedTourNames the list of sorted tour names (by alphabetical order)
+ * @throws TourPlannerException if there is no tours that can be found given the tour name
+ */
+ public void showSortedTourByName(TourList tours, ArrayList sortedTourNames) throws TourPlannerException {
+ if (checkEmptyTours(tours)) {
+ return;
+ }
+ tours.initTempArray();
+ show(SORT_TOUR_NAME_MESSAGE);
+ int listIndex = 1;
+ for (String tourName : sortedTourNames) {
+ Tour currTour = tours.getTourByName(tourName);
+ show(listIndex + ". " + currTour + System.lineSeparator());
+ listIndex++;
+ }
+ }
+
+ private boolean checkEmptyClients(ClientList clients) {
+ int count = clients.getClientCount();
+ if (count == 0) {
+ show(LIST_NO_MESSAGE + "flights");
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Ui response to sort client by id.
+ *
+ * @param clients the current list of clients in the database
+ * @param sortedClientIds the list of sorted client IDs (by alphabetical order)
+ * @throws TourPlannerException if there is no clients that can be found given the client ID
+ */
+ public void showSortedClientById(ClientList clients, ArrayList sortedClientIds)
+ throws TourPlannerException {
+ if (checkEmptyClients(clients)) {
+ return;
+ }
+ show(SORT_CLIENT_ID_MESSAGE);
+ int listIndex = 1;
+ for (String clientId : sortedClientIds) {
+ Client currClient = clients.getClientById(clientId);
+ show(listIndex + ". " + currClient + System.lineSeparator());
+ listIndex++;
+ }
+ }
+
+ /**
+ * Ui response to sort client by name.
+ *
+ * @param clients the current list of clients in the database
+ * @param sortedClientNames the list of sorted client names (by alphabetical order)
+ * @throws TourPlannerException if there is no clients that can be found given the client name
+ */
+ public void showSortedClientByName(ClientList clients, ArrayList sortedClientNames)
+ throws TourPlannerException {
+ if (checkEmptyClients(clients)) {
+ return;
+ }
+ clients.initTempArray();
+ show(SORT_CLIENT_NAME_MESSAGE);
+ int listIndex = 1;
+ for (String clientName : sortedClientNames) {
+ Client currClient = clients.getClientByName(clientName);
+ show(listIndex + ". " + currClient + System.lineSeparator());
+ listIndex++;
+ }
+ }
+
+ private boolean checkEmptyFlights(FlightList flights) {
+ int count = flights.getFlightCount();
+ if (count == 0) {
+ show(LIST_NO_MESSAGE + "flights");
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Ui response to sort flight by ID.
+ *
+ * @param flights the current list of flights in the database
+ * @param sortedIds the list of sorted flight IDs (by alphabetical order)
+ * @throws TourPlannerException if there is no flights that can be found given the flight ID
+ */
+ public void showSortedFlightById(FlightList flights, ArrayList sortedIds)
+ throws TourPlannerException {
+ if (checkEmptyFlights(flights)) {
+ return;
+ }
+ show(SORT_FLIGHT_ID_MESSAGE);
+ int listIndex = 1;
+ for (String flightId : sortedIds) {
+ Flight currFlight = flights.getFlightById(flightId);
+ show(listIndex + ". " + currFlight + System.lineSeparator());
+ listIndex++;
+ }
+ }
+
+ /**
+ * Ui response to sort flight by return date.
+ *
+ * @param flights the current list of flights in the database
+ * @param sortedFlightByArriveDates the list of sorted return dates (by natural order of time)
+ * @throws TourPlannerException if there is no flights that can be found given the return date
+ */
+ public void showSortedFlightByReturn(FlightList flights, ArrayList sortedFlightByArriveDates)
+ throws TourPlannerException {
+ if (checkEmptyFlights(flights)) {
+ return;
+ }
+ show(SORT_FLIGHT_BY_ARRIVAL_MESSAGE);
+ flights.initTempArray();
+ int listIndex = 1;
+ for (String flightArriveDate : sortedFlightByArriveDates) {
+ Flight currFlight = flights.getFlightByReturnDate(flightArriveDate);
+ show(listIndex + ". " + currFlight + System.lineSeparator());
+ listIndex++;
+ }
+ }
+
+ /**
+ * Ui response to sort flight by depart date.
+ *
+ * @param flights the current list of flights in the database
+ * @param sortedFlightByDepartureDates the list of sorted departure dates (by natural order of time)
+ * @throws TourPlannerException if there is no flights that can be found given the departure date
+ */
+ public void showSortedFlightByDeparture(FlightList flights, ArrayList sortedFlightByDepartureDates)
+ throws TourPlannerException {
+ if (checkEmptyFlights(flights)) {
+ return;
+ }
+ show(SORT_FLIGHT_BY_DEPARTURE_MESSAGE);
+ flights.initTempArray();
+ int listIndex = 1;
+ for (String flightDepartDate : sortedFlightByDepartureDates) {
+ Flight currFlight = flights.getFlightByDepartDate(flightDepartDate);
+ show(listIndex + ". " + currFlight + System.lineSeparator());
+ listIndex++;
+ }
+ }
+
+ /**
+ * Ui response to ListFlightCommand.
+ * Shows all flights in the flightList on the terminal.
+ *
+ * @param flights the FlightList from which the flights are obtained from.
+ */
+ public void showListFlight(FlightList flights) {
+ int count = flights.getFlightCount();
+ if (count == 0) {
+ show(LIST_NO_MESSAGE + "flights");
+ return;
+ }
+ show(LIST_MESSAGE + "flights:");
+ for (int i = 1; i <= count; i++) {
+ Flight currFlight = flights.getFlightByIndex(i - 1);
+ show(i + ". " + currFlight + "\n");
+ }
+ show("Total Flights: " + count);
+ }
+
+
+ /**
+ * Ui response to FindFlightCommand.
+ * Shows specific flight based on an ID, as well as the passengers for said flight.
+ *
+ * @param flights the FlightList from which the flights are obtained from.
+ * @param clientPackages the ClientPackageList from which the passengers for the flight are obtained from.
+ * @param id the ID used to find the specific flight.
+ */
+ public void showFindFlight(FlightList flights, ClientPackageList clientPackages, String id)
+ throws TourPlannerException {
+ Flight foundFlight = flights.getFlightById(id);
+ if (foundFlight != null) {
+ show(FIND_SUCCESS_MESSAGE_LEFT + "flight " + FIND_MESSAGE_RIGHT);
+ show(foundFlight + "\n" + "\n");
+ int passengers = 0;
+ int count = clientPackages.getClientPackageCount();
+ show("Passengers:");
+ for (int i = 0; i < count; i++) {
+ Flight currFlight = clientPackages.getClientPackageByIndex(i).getFlight();
+ if (currFlight.equals(foundFlight)) {
+ String currClientName = clientPackages.getClientPackageByIndex(i).getClient().getName();
+ String currClientId = clientPackages.getClientPackageByIndex(i).getClient().getId();
+ show((passengers + 1) + ". " + currClientName + " (ID: " + currClientId + ")");
+ passengers++;
+ }
+ }
+ show("\n" + "Total Passengers: " + passengers);
+ } else {
+ show(FIND_FAIL_MESSAGE_LEFT + "flights " + FIND_MESSAGE_RIGHT);
+ }
+ }
+
+ /**
+ * Ui response to ListClientPackageCommand.
+ * Shows all client packages in the clientPackageList on the terminal.
+ *
+ * @param clientPackages the ClientPackageList from which the client packages are obtained from.
+ */
+ public void showListClientPackage(ClientPackageList clientPackages) {
+ int count = clientPackages.getClientPackageCount();
+ if (count == 0) {
+ show(LIST_NO_MESSAGE + "packages");
+ return;
+ }
+ show(LIST_MESSAGE + "packages:");
+ for (int i = 0; i < count; i++) {
+ ClientPackage currPackage = clientPackages.getClientPackageByIndex(i);
+ show((i + 1) + ". " + currPackage + "\n" + "\n");
+ }
+ show("Total Packages: " + count);
+ }
+
+ /**
+ * Shows an exit message to the user to acknowledge exiting the application.
+ */
+ public void showBye() {
+ show(BYE_MESSAGE);
+ showLine();
+ }
+
+ /**
+ * Shows error message for corrupted storage file.
+ */
+ public void showFileError() {
+ showLine();
+ show(FILE_CORRUPT);
+ showLine();
+ }
+}
+
diff --git a/src/main/java/seedu/duke/commands/ByeCommand.java b/src/main/java/seedu/duke/commands/ByeCommand.java
new file mode 100644
index 0000000000..6dc3113f35
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/ByeCommand.java
@@ -0,0 +1,23 @@
+package seedu.duke.commands;
+
+/**
+ * Acknowledge that the user is exiting the application.
+ */
+public class ByeCommand extends Command {
+
+
+ @Override
+ public void execute() {
+ ui.showBye();
+ }
+
+ /**
+ * Sets the exit condition to true since ByeCommand is initialised.
+ *
+ * @return true
+ */
+ @Override
+ public boolean isExit() {
+ return true;
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/Command.java b/src/main/java/seedu/duke/commands/Command.java
new file mode 100644
index 0000000000..6ee95417d3
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/Command.java
@@ -0,0 +1,51 @@
+package seedu.duke.commands;
+
+import seedu.duke.Ui;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.TourList;
+
+/**
+ * Abstract executable command.
+ */
+public abstract class Command {
+ protected ClientList clients;
+ protected FlightList flights;
+ protected TourList tours;
+ protected ClientPackageList clientPackages;
+ protected Ui ui;
+
+ /**
+ * Executes the specific command depending on the command constructed.
+ */
+ public abstract void execute();
+
+ /**
+ * Setter for ClientList, FlightList, TourList, ClientPackageList and Ui
+ * for subclasses of Command to access when executing their command.
+ *
+ * @param clients list of all clients
+ * @param flights list of all flights
+ * @param tours list of all tours
+ * @param clientPackages list of all client packages
+ * @param ui user interface in charge of reading user input and showing messages to user
+ */
+ public void setData(ClientList clients, FlightList flights, TourList tours,
+ ClientPackageList clientPackages, Ui ui) {
+ this.clients = clients;
+ this.flights = flights;
+ this.tours = tours;
+ this.clientPackages = clientPackages;
+ this.ui = ui;
+ }
+
+ /**
+ * Function that controls the exit condition of the loop in TourPlanner class.
+ *
+ * @return the exit condition from the loop
+ */
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/HelpCommand.java b/src/main/java/seedu/duke/commands/HelpCommand.java
new file mode 100644
index 0000000000..45303bae6d
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/HelpCommand.java
@@ -0,0 +1,7 @@
+package seedu.duke.commands;
+
+public class HelpCommand extends Command {
+ public void execute() {
+ ui.showHelp();
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/clientpackages/AddClientPackageCommand.java b/src/main/java/seedu/duke/commands/clientpackages/AddClientPackageCommand.java
new file mode 100644
index 0000000000..3952ae15a6
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/clientpackages/AddClientPackageCommand.java
@@ -0,0 +1,124 @@
+package seedu.duke.commands.clientpackages;
+
+import seedu.duke.TourPlannerException;
+import seedu.duke.commands.Command;
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientPackage;
+import seedu.duke.data.Flight;
+import seedu.duke.data.Tour;
+
+import java.util.ArrayList;
+
+public class AddClientPackageCommand extends Command {
+ public static final String ERROR_CLIENT_PACKAGE_ID_EXISTS =
+ "ERROR: Package ID already exists. Please try another Package ID.";
+ public static final String ERROR_CLIENT_PACKAGE_SAME_FIELDS =
+ "ERROR: Package with same fields already exists with different ID.";
+
+ private ClientPackage clientPackage;
+ private String[] rawClientPackage;
+
+ /**
+ * Class constructor of AddClientPackage, used to add a ClientPackage to ClientPackageList.
+ *
+ * @param rawClientPackage the array of values of the client package, ordered in this manner:
+ * package id, client id, tour id and flight id
+ */
+ public AddClientPackageCommand(String[] rawClientPackage) {
+ this.rawClientPackage = rawClientPackage;
+ }
+
+ /**
+ * Executes the addition of a ClientPackage to a ClientPackageList.
+ * If given client package ID already exists, the client package will not be added.
+ */
+ @Override
+ public void execute() {
+ try {
+ createClientPackage();
+ ArrayList checkClientPackage = clientPackages.getClientPackages();
+ for (ClientPackage currClientPackage : checkClientPackage) {
+ boolean sameId = currClientPackage.getId().equals(clientPackage.getId());
+ if (sameId) {
+ System.out.println(ERROR_CLIENT_PACKAGE_ID_EXISTS);
+ return;
+ }
+ boolean sameClient = currClientPackage.getClient().equals(clientPackage.getClient());
+ boolean sameTour = currClientPackage.getTour().equals(clientPackage.getTour());
+ boolean sameFlight = currClientPackage.getFlight().equals(clientPackage.getFlight());
+ if (sameClient && sameTour && sameFlight) {
+ System.out.println(ERROR_CLIENT_PACKAGE_SAME_FIELDS);
+ return;
+ }
+ }
+ if (!clientPackage.getClient().equals(null) && !clientPackage.getTour().equals(null)
+ && !clientPackage.getFlight().equals(null)) {
+ clientPackages.add(clientPackage);
+ ui.showAdd(clientPackage);
+ }
+ } catch (TourPlannerException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ /**
+ * Executes the addition of a ClientPackage to a ClientPackageList for Storage class.
+ * If given client package ID already exists, the client package will not be added.
+ */
+ public void executeStorage() {
+ try {
+ createClientPackage();
+ ArrayList checkClientPackage = clientPackages.getClientPackages();
+ for (ClientPackage currClientPackage : checkClientPackage) {
+ boolean sameId = currClientPackage.getId().equals(clientPackage.getId());
+ if (sameId) {
+ System.out.println(ERROR_CLIENT_PACKAGE_ID_EXISTS);
+ return;
+ }
+ boolean sameClient = currClientPackage.getClient().equals(clientPackage.getClient());
+ boolean sameTour = currClientPackage.getTour().equals(clientPackage.getTour());
+ boolean sameFlight = currClientPackage.getFlight().equals(clientPackage.getFlight());
+ if (sameClient && sameTour && sameFlight) {
+ System.out.println(ERROR_CLIENT_PACKAGE_SAME_FIELDS);
+ return;
+ }
+ }
+ clientPackages.add(clientPackage);
+ } catch (TourPlannerException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ /**
+ * Getter for the clientPackage to be added to clientPackages.
+ *
+ * @return Client Package that will be added to the ClientPackageList
+ */
+ public ClientPackage getClientPackage() {
+ return clientPackage;
+ }
+
+ private void createClientPackage() throws TourPlannerException {
+ String clientPackageId = rawClientPackage[0];
+ String clientId = rawClientPackage[1];
+ String tourId = rawClientPackage[2];
+ String flightId = rawClientPackage[3];
+ Client client = extractClient(clientId);
+ Tour tour = extractTour(tourId);
+ Flight flight = extractFlight(flightId);
+ clientPackage = new ClientPackage(clientPackageId, client, tour, flight);
+ }
+
+ private Client extractClient(String clientId) throws TourPlannerException {
+ return clients.getClientById(clientId);
+ }
+
+ private Tour extractTour(String tourId) throws TourPlannerException {
+ return tours.getTourById(tourId);
+ }
+
+ private Flight extractFlight(String flightId) throws TourPlannerException {
+ return flights.getFlightById(flightId);
+ }
+
+}
diff --git a/src/main/java/seedu/duke/commands/clientpackages/CutClientPackageCommand.java b/src/main/java/seedu/duke/commands/clientpackages/CutClientPackageCommand.java
new file mode 100644
index 0000000000..f10232907f
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/clientpackages/CutClientPackageCommand.java
@@ -0,0 +1,39 @@
+package seedu.duke.commands.clientpackages;
+
+import seedu.duke.TourPlannerException;
+import seedu.duke.commands.Command;
+import seedu.duke.data.ClientPackage;
+
+public class CutClientPackageCommand extends Command {
+ private final String clientPackageId;
+ private ClientPackage clientPackage;
+
+ /**
+ * Class constructor for CutClientPackageCommand.
+ *
+ * @param clientPackageId ID of to-be-deleted clientPackage in the clientPackage list
+ */
+ public CutClientPackageCommand(String clientPackageId) {
+ this.clientPackageId = clientPackageId;
+ }
+
+ /**
+ * Executes the deletion of a specific clientPackage from clientPackage list, according to the clientPackageId.
+ * If client package id cannot be found, nothing will be deleted from the list.
+ */
+ @Override
+ public void execute() {
+ try {
+ this.clientPackage = clientPackages.getClientPackageById(clientPackageId);
+ int newClientPackageCount = clientPackages.getClientPackageCount() - 1;
+ ui.showCut(clientPackage);
+ clientPackages.cut(clientPackage);
+ assert newClientPackageCount == clientPackages.getClientPackageCount();
+ assert newClientPackageCount >= 0;
+ } catch (IndexOutOfBoundsException e) {
+ System.out.println("INVALID: Index out of bounds");
+ } catch (TourPlannerException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/clientpackages/ListClientPackageCommand.java b/src/main/java/seedu/duke/commands/clientpackages/ListClientPackageCommand.java
new file mode 100644
index 0000000000..fb669da7e2
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/clientpackages/ListClientPackageCommand.java
@@ -0,0 +1,15 @@
+package seedu.duke.commands.clientpackages;
+
+import seedu.duke.commands.Command;
+
+/**
+ * Lists all client packages in the database.
+ */
+public class ListClientPackageCommand extends Command {
+ /**
+ * Executes the listing of client packages to the terminal.
+ */
+ public void execute() {
+ ui.showListClientPackage(clientPackages);
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/clients/AddClientCommand.java b/src/main/java/seedu/duke/commands/clients/AddClientCommand.java
new file mode 100644
index 0000000000..1a2586aab5
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/clients/AddClientCommand.java
@@ -0,0 +1,57 @@
+package seedu.duke.commands.clients;
+
+import seedu.duke.commands.Command;
+import seedu.duke.data.Client;
+
+/**
+ * Adds a client into the database.
+ */
+public class AddClientCommand extends Command {
+ private final Client client;
+
+
+ /**
+ * Class constructor for AddClientCommand.
+ *
+ * @param client client to be added
+ */
+ public AddClientCommand(Client client) {
+ this.client = client;
+ }
+
+ /**
+ * Returns the client object that was added.
+ *
+ * @return the added client object
+ */
+ public Client getClient() {
+ return client;
+ }
+
+ /**
+ * Executes the addition of client to client list.
+ * If given client ID already exists, the client will not be added.
+ */
+ @Override
+ public void execute() {
+
+ int count = clients.getClientCount();
+ for (int i = 0; i < count; i++) {
+ Client currClient = clients.getClientByIndex(i);
+ boolean sameId = currClient.getId().equals(client.getId());
+ if (sameId) {
+ System.out.println("ERROR: Client ID already exists. Please try another client code.");
+ return;
+ }
+ boolean sameName = currClient.getName().equals(client.getName());
+ boolean sameContactNum = currClient.getContactNum().equals(client.getContactNum());
+ boolean sameEmail = currClient.getEmail().equals(client.getEmail());
+ if (sameName && sameContactNum && sameEmail) {
+ System.out.println("ERROR: Client with same fields already exists with different ID.");
+ return;
+ }
+ }
+ clients.add(client);
+ ui.showAdd(client);
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/clients/CutClientCommand.java b/src/main/java/seedu/duke/commands/clients/CutClientCommand.java
new file mode 100644
index 0000000000..631d121fbf
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/clients/CutClientCommand.java
@@ -0,0 +1,64 @@
+package seedu.duke.commands.clients;
+
+import seedu.duke.TourPlannerException;
+import seedu.duke.commands.Command;
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientPackage;
+
+import java.util.ArrayList;
+
+public class CutClientCommand extends Command {
+ private final String clientId;
+ private Client client;
+
+ /**
+ * Class constructor for CutClientCommand.
+ *
+ * @param clientId ID of to-be-deleted client in the client list
+ */
+ public CutClientCommand(String clientId) {
+ this.clientId = clientId;
+ }
+
+ /**
+ * Executes deletion of the specific client and related client packages, according to the clientId.
+ */
+ @Override
+ public void execute() {
+ try {
+ cutClient();
+ cutClientPackage();
+ } catch (IndexOutOfBoundsException e1) {
+ System.out.println("INVALID: Index out of bounds");
+ } catch (TourPlannerException e2) {
+ System.out.println(e2.getMessage());
+ }
+ }
+
+ /**
+ * Executes deletion of the specific client from the client list.
+ *
+ * @throws TourPlannerException if client cannot be found based on the client id
+ */
+ private void cutClient() throws TourPlannerException {
+ this.client = clients.getClientById(clientId);
+ final int newClientCount = clients.getClientCount() - 1;
+ ui.showCut(client);
+ ui.showLine();
+ clients.cut(client);
+ assert newClientCount == clients.getClientCount();
+ assert newClientCount >= 0;
+ }
+
+ /**
+ * Executes deletion of the client packages containing the specific client from the client package list.
+ */
+ private void cutClientPackage() {
+ ArrayList clientPackagesWithClient = clientPackages.getClientPackageByClient(client);
+ for (ClientPackage clientPackage: clientPackagesWithClient) {
+ System.out.println();
+ ui.showCut(clientPackage);
+ clientPackages.cut(clientPackage);
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/clients/FindClientCommand.java b/src/main/java/seedu/duke/commands/clients/FindClientCommand.java
new file mode 100644
index 0000000000..2d43936c10
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/clients/FindClientCommand.java
@@ -0,0 +1,27 @@
+package seedu.duke.commands.clients;
+
+import seedu.duke.commands.Command;
+
+/**
+ * Finds client(s) based on a certain substring.
+ */
+public class FindClientCommand extends Command {
+ private final String substring;
+
+ /**
+ * Class constructor for FindClientCommand.
+ *
+ * @param substring substring used to determine which client(s) to find.
+ */
+ public FindClientCommand(String substring) {
+ this.substring = substring;
+ }
+
+ /**
+ * Executes the finding of client, as well as finding said client's packages.
+ * If there are no found clients, it will be shown to the user.
+ */
+ public void execute() {
+ ui.showFindClient(clients, clientPackages, substring);
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/clients/ListClientCommand.java b/src/main/java/seedu/duke/commands/clients/ListClientCommand.java
new file mode 100644
index 0000000000..29dc3ca241
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/clients/ListClientCommand.java
@@ -0,0 +1,15 @@
+package seedu.duke.commands.clients;
+
+import seedu.duke.commands.Command;
+
+/**
+ * Lists all clients in the database.
+ */
+public class ListClientCommand extends Command {
+ /**
+ * Executes the listing of clients to the terminal.
+ */
+ public void execute() {
+ ui.showListClient(clients);
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/clients/SortClientCommand.java b/src/main/java/seedu/duke/commands/clients/SortClientCommand.java
new file mode 100644
index 0000000000..c55d468e31
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/clients/SortClientCommand.java
@@ -0,0 +1,55 @@
+package seedu.duke.commands.clients;
+
+import seedu.duke.TourPlannerException;
+import seedu.duke.commands.Command;
+
+import java.util.ArrayList;
+
+/**
+ * Print a sorted list of clients, with the method of sorting specified by a filter.
+ * Sorts name of client, or ID of client alphabetically.
+ */
+public class SortClientCommand extends Command {
+ public static final String ERROR_MISSING_FILTER = "Missing filter! Sort clients with the format: \n"
+ + "sort -c /n (sort by name)\n"
+ + "sort -c /id (sort by id)";
+ public static final String SORT_NAME_FILTER = "/n";
+ public static final String SORT_ID_FILTER = "/id";
+
+ private final String filter;
+
+ /**
+ * Class constructor for SortClientCommand.
+ * Defines filter input by user.
+ *
+ * @param filter the user input filter that specifies how the clients should be sorted
+ */
+ public SortClientCommand(String filter) {
+ this.filter = filter;
+ }
+
+ /**
+ * Executes the printing of the sorted client list, specified by the filter.
+ * If filter given is /n, sorts the clients by name (in alphabetical order).
+ * If filter given is /id, sorts the clients by ID (in alphabetical order).
+ */
+ @Override
+ public void execute() {
+ try {
+ switch (filter) {
+ case SORT_NAME_FILTER:
+ ArrayList sortedNames = clients.getSortedClientNames();
+ ui.showSortedClientByName(clients, sortedNames);
+ break;
+ case SORT_ID_FILTER:
+ ArrayList sortedIds = clients.getSortedClientIds();
+ ui.showSortedClientById(clients, sortedIds);
+ break;
+ default:
+ throw new TourPlannerException(ERROR_MISSING_FILTER);
+ }
+ } catch (TourPlannerException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/flights/AddFlightCommand.java b/src/main/java/seedu/duke/commands/flights/AddFlightCommand.java
new file mode 100644
index 0000000000..fe41becbf7
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/flights/AddFlightCommand.java
@@ -0,0 +1,61 @@
+package seedu.duke.commands.flights;
+
+import seedu.duke.commands.Command;
+import seedu.duke.data.Flight;
+
+/**
+ * Adds a flight into the database.
+ */
+public class AddFlightCommand extends Command {
+ private final Flight flight;
+
+ /**
+ * Class constructor for AddFlightCommand.
+ *
+ * @param flight client to be added
+ */
+ public AddFlightCommand(Flight flight) {
+ this.flight = flight;
+ }
+
+ /**
+ * Returns the flight object that was added.
+ *
+ * @return the added flight object
+ */
+ public Flight getFlight() {
+ return flight;
+ }
+
+ /**
+ * Executes the addition of flight to flight list.
+ * If given flight ID already exists, the flight will not be added.
+ */
+ @Override
+ public void execute() {
+ int count = flights.getFlightCount();
+ for (int i = 0; i < count; i++) {
+ Flight currFlight = flights.getFlightByIndex(i);
+ boolean sameId = currFlight.getId().equals(flight.getId());
+ if (sameId) {
+ System.out.println("ERROR: Flight ID already exists. Please try another flight ID.");
+ return;
+ }
+ boolean sameDepartDestination =
+ currFlight.getDepartDestination().equals(flight.getDepartDestination());
+ boolean sameReturnDestination =
+ currFlight.getReturnDestination().equals(flight.getReturnDestination());
+ boolean sameDepartDate =
+ currFlight.getDepartDate().equals(flight.getDepartDate());
+ boolean sameReturnDate =
+ currFlight.getReturnDate().equals(flight.getReturnDate());
+ if (sameDepartDestination && sameReturnDestination
+ && sameDepartDate && sameReturnDate) {
+ System.out.println("ERROR: Flight with same fields already exists with different ID.");
+ return;
+ }
+ }
+ flights.add(flight);
+ ui.showAdd(flight);
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/flights/CutFlightCommand.java b/src/main/java/seedu/duke/commands/flights/CutFlightCommand.java
new file mode 100644
index 0000000000..f2e4ecc19d
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/flights/CutFlightCommand.java
@@ -0,0 +1,64 @@
+package seedu.duke.commands.flights;
+
+import seedu.duke.TourPlannerException;
+import seedu.duke.commands.Command;
+import seedu.duke.data.ClientPackage;
+import seedu.duke.data.Flight;
+
+import java.util.ArrayList;
+
+public class CutFlightCommand extends Command {
+ private final String flightId;
+ private Flight flight;
+
+ /**
+ * Class constructor for CutFlightCommand.
+ *
+ * @param flightId ID of to-be-deleted flight in the flight list
+ */
+ public CutFlightCommand(String flightId) {
+ this.flightId = flightId;
+ }
+
+ /**
+ * Executes deletion of the specific flight and related client packages, according to the flightId.
+ */
+ @Override
+ public void execute() {
+ try {
+ cutFlight();
+ cutFlightPackage();
+ } catch (IndexOutOfBoundsException e1) {
+ System.out.println("INVALID: Index out of bounds");
+ } catch (TourPlannerException e2) {
+ System.out.println(e2.getMessage());
+ }
+ }
+
+ /**
+ * Executes deletion of the specific flight from the flight list.
+ *
+ * @throws TourPlannerException if flight cannot be found based on the flight id
+ */
+ private void cutFlight() throws TourPlannerException {
+ this.flight = flights.getFlightById(flightId);
+ int newFlightCount = flights.getFlightCount() - 1;
+ ui.showCut(flight);
+ flights.cut(flight);
+ assert newFlightCount == flights.getFlightCount();
+ assert newFlightCount >= 0;
+ }
+
+ /**
+ * Executes deletion of the client packages containing the specific flight from the client package list.
+ */
+ private void cutFlightPackage() {
+ ArrayList clientPackagesWithFlight = clientPackages.getClientPackageByFlight(flight);
+ for (ClientPackage clientPackage: clientPackagesWithFlight) {
+ System.out.println();
+ ui.showCut(clientPackage);
+ clientPackages.cut(clientPackage);
+ }
+ }
+}
+
diff --git a/src/main/java/seedu/duke/commands/flights/FindFlightCommand.java b/src/main/java/seedu/duke/commands/flights/FindFlightCommand.java
new file mode 100644
index 0000000000..8a80cce3a4
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/flights/FindFlightCommand.java
@@ -0,0 +1,32 @@
+package seedu.duke.commands.flights;
+
+import seedu.duke.TourPlannerException;
+import seedu.duke.commands.Command;
+
+/**
+ * Finds a flight based on a certain id.
+ */
+public class FindFlightCommand extends Command {
+ private final String id;
+
+ /**
+ * Class constructor for FindFlightCommand.
+ *
+ * @param id id used to determine which flight to find.
+ */
+ public FindFlightCommand(String id) {
+ this.id = id;
+ }
+
+ /**
+ * Executes the finding of flight, as well as finding said flight's passengers.
+ * If there are no found flights, it will be shown to the user.
+ */
+ public void execute() {
+ try {
+ ui.showFindFlight(flights, clientPackages, id);
+ } catch (TourPlannerException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/duke/commands/flights/ListFlightCommand.java b/src/main/java/seedu/duke/commands/flights/ListFlightCommand.java
new file mode 100644
index 0000000000..504ba78c86
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/flights/ListFlightCommand.java
@@ -0,0 +1,15 @@
+package seedu.duke.commands.flights;
+
+import seedu.duke.commands.Command;
+
+/**
+ * Lists all flights in the database.
+ */
+public class ListFlightCommand extends Command {
+ /**
+ * Executes the listing of flights to the terminal.
+ */
+ public void execute() {
+ ui.showListFlight(flights);
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/flights/SortFlightCommand.java b/src/main/java/seedu/duke/commands/flights/SortFlightCommand.java
new file mode 100644
index 0000000000..8740286f78
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/flights/SortFlightCommand.java
@@ -0,0 +1,59 @@
+package seedu.duke.commands.flights;
+
+import seedu.duke.TourPlannerException;
+import seedu.duke.commands.Command;
+
+import java.util.ArrayList;
+
+/**
+ * Print a sorted list of flights, with the method of sorting specified by a filter.
+ * Sorts departure time and return time of flight by time proximity.
+ * Sorts flight ID by alphabetical order.
+ */
+public class SortFlightCommand extends Command {
+ private final String filter;
+ public static final String ERROR_MISSING_FILTER = "Missing filter! Sort flights with the format: \n"
+ + "sort -f /d (sort by departure date and time) \n"
+ + "sort -f /r (sort by return date and time) \n"
+ + "sort -f /id (sort by id alphabetically)";
+
+ /**
+ * Class constructor for SortFlightCommand.
+ * Defines filter input by user.
+ *
+ * @param filter the user input filter that specifies how the flights should be sorted
+ */
+ public SortFlightCommand(String filter) {
+ this.filter = filter;
+ }
+
+ /**
+ * Executes the printing of the sorted flight list, specified by the filter.
+ * If filter given is /d, sorts the departure times (by natural order of time).
+ * If filter given is /r, sorts the return times (by natural order of time).
+ * If filter given is /id, sorts the flights by id (in alphabetical order).
+ */
+ @Override
+ public void execute() {
+ try {
+ switch (filter) {
+ case "/d":
+ ArrayList sortedFlightByDepartureDates = flights.getSortedDepartDates();
+ ui.showSortedFlightByDeparture(flights, sortedFlightByDepartureDates);
+ break;
+ case "/r":
+ ArrayList sortedFlightByReturnDates = flights.getSortedReturnDates();
+ ui.showSortedFlightByReturn(flights, sortedFlightByReturnDates);
+ break;
+ case "/id":
+ ArrayList sortedIds = flights.getSortedFlightIds();
+ ui.showSortedFlightById(flights, sortedIds);
+ break;
+ default:
+ throw new TourPlannerException(ERROR_MISSING_FILTER);
+ }
+ } catch (TourPlannerException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/tours/AddTourCommand.java b/src/main/java/seedu/duke/commands/tours/AddTourCommand.java
new file mode 100644
index 0000000000..1c8ceb7dea
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/tours/AddTourCommand.java
@@ -0,0 +1,58 @@
+package seedu.duke.commands.tours;
+
+import seedu.duke.commands.Command;
+import seedu.duke.data.Tour;
+
+/**
+ * Adds a tour into the database.
+ */
+public class AddTourCommand extends Command {
+
+ private final Tour tour;
+
+ /**
+ * Class constructor for AddTourCommand.
+ *
+ * @param tour tour to be added
+ */
+ public AddTourCommand(Tour tour) {
+ this.tour = tour;
+ }
+
+ /**
+ * Returns the tour object that was added.
+ *
+ * @return the added tour object
+ */
+ public Tour getTour() {
+ return tour;
+ }
+
+ /**
+ * Executes the addition of tour to tour list.
+ * If given tour code already exists, the tour will not be added.
+ */
+ @Override
+ public void execute() {
+ final int newTourCount = tours.getTourCount() + 1;
+ int count = tours.getTourCount();
+ for (int i = 0; i < count; i++) {
+ Tour currTour = tours.getTourByIndex(i);
+ boolean sameId = currTour.getId().equals(tour.getId());
+ if (sameId) {
+ System.out.println("ERROR: Tour code already exists. Please try another tour code.");
+ return;
+ }
+ boolean sameName = currTour.getName().equals(tour.getName());
+ boolean samePrice = currTour.getPrice().equals(tour.getPrice());
+ if (sameName && samePrice) {
+ System.out.println("ERROR: Tour with same fields already exists with different ID.");
+ return;
+ }
+ }
+ tours.add(tour);
+ ui.showAdd(tour);
+ assert newTourCount == tours.getTourCount();
+ }
+
+}
diff --git a/src/main/java/seedu/duke/commands/tours/CutTourCommand.java b/src/main/java/seedu/duke/commands/tours/CutTourCommand.java
new file mode 100644
index 0000000000..929d985bff
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/tours/CutTourCommand.java
@@ -0,0 +1,64 @@
+package seedu.duke.commands.tours;
+
+import seedu.duke.TourPlannerException;
+import seedu.duke.commands.Command;
+import seedu.duke.data.ClientPackage;
+import seedu.duke.data.Tour;
+
+import java.util.ArrayList;
+
+public class CutTourCommand extends Command {
+ private final String tourId;
+ private Tour tour;
+
+ /**
+ * Class constructor for CutTourCommand.
+ *
+ * @param tourId ID of to-be-deleted tour in the tour list
+ */
+ public CutTourCommand(String tourId) {
+ this.tourId = tourId;
+ }
+
+ /**
+ * Executes deletion of the specific tour and related client packages, according to the tourId.
+ */
+ @Override
+ public void execute() {
+ try {
+ cutTour();
+ cutTourPackage();
+ } catch (IndexOutOfBoundsException e1) {
+ System.out.println("INVALID: Index out of bounds");
+ } catch (TourPlannerException e2) {
+ System.out.println(e2.getMessage());
+ }
+ }
+
+ /**
+ * Executes deletion of the specific tour from the tour list.
+ *
+ * @throws TourPlannerException if tour cannot be found based on the tour id
+ */
+ private void cutTour() throws TourPlannerException {
+ this.tour = tours.getTourById(tourId);
+ int newTourCount = tours.getTourCount() - 1;
+ ui.showCut(tour);
+ tours.cut(tour);
+ assert newTourCount == tours.getTourCount();
+ assert newTourCount >= 0;
+ }
+
+ /**
+ * Executes deletion of the client packages containing the specific tour from the client package list.
+ */
+ private void cutTourPackage() {
+ ArrayList clientPackagesWithTour = clientPackages.getClientPackageByTour(tour);
+ for (ClientPackage clientPackage: clientPackagesWithTour) {
+ System.out.println();
+ ui.showCut(clientPackage);
+ clientPackages.cut(clientPackage);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/duke/commands/tours/FindTourCommand.java b/src/main/java/seedu/duke/commands/tours/FindTourCommand.java
new file mode 100644
index 0000000000..ee801cce24
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/tours/FindTourCommand.java
@@ -0,0 +1,32 @@
+package seedu.duke.commands.tours;
+
+import seedu.duke.TourPlannerException;
+import seedu.duke.commands.Command;
+
+/**
+ * Finds a tour based on a certain id.
+ */
+public class FindTourCommand extends Command {
+ private final String id;
+
+ /**
+ * Class constructor for FindTourCommand.
+ *
+ * @param id id used to determine which tour to find.
+ */
+ public FindTourCommand(String id) {
+ this.id = id;
+ }
+
+ /**
+ * Executes the finding of tour, as well as finding said tour's subscribers.
+ * If there are no found tours, it will be shown to the user.
+ */
+ public void execute() {
+ try {
+ ui.showFindTour(tours, clientPackages, id);
+ } catch (TourPlannerException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/tours/ListTourCommand.java b/src/main/java/seedu/duke/commands/tours/ListTourCommand.java
new file mode 100644
index 0000000000..fc89ca2096
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/tours/ListTourCommand.java
@@ -0,0 +1,15 @@
+package seedu.duke.commands.tours;
+
+import seedu.duke.commands.Command;
+
+/**
+ * Lists all tours in the database.
+ */
+public class ListTourCommand extends Command {
+ /**
+ * Executes the listing of tours to the terminal.
+ */
+ public void execute() {
+ ui.showListTour(tours);
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/tours/SortTourCommand.java b/src/main/java/seedu/duke/commands/tours/SortTourCommand.java
new file mode 100644
index 0000000000..16a7621fd3
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/tours/SortTourCommand.java
@@ -0,0 +1,57 @@
+package seedu.duke.commands.tours;
+
+import seedu.duke.TourPlannerException;
+import seedu.duke.commands.Command;
+
+import java.util.ArrayList;
+
+/**
+ * Print a sorted list of tours, with the method of sorting specified by a filter.
+ * Sorts tour prices by ascending order.
+ * Sorts tour ID by alphabetical order.
+ */
+public class SortTourCommand extends Command {
+ private final String filter;
+ public static final String ERROR_MISSING_FILTER = "Missing filter! Sort tours with the format: \n"
+ + "sort -t /id (sort by ID alphabetically) \n"
+ + "sort -t /p (sort by increasing price)";
+
+ /**
+ * Class constructor for SortTourCommand.
+ * Defines filter input by user.
+ *
+ * @param filter the user input filter that specifies how the tours should be sorted
+ */
+ public SortTourCommand(String filter) {
+ this.filter = filter;
+ }
+
+ /**
+ * Executes the printing of the sorted flight list, specified by the filter.
+ * If filter given is /id, sorts the tours by id (in alphabetical order).
+ * If filter given is /r, sorts the tours by price (in ascending order).
+ */
+ @Override
+ public void execute() {
+ try {
+ switch (filter) {
+ case "/id":
+ ArrayList sortedIds = tours.getSortedTourIds();
+ ui.showSortedTourById(tours, sortedIds);
+ break;
+ case "/p":
+ ArrayList sortedPrices = tours.getSortedTourPrices();
+ ui.showSortedTourByPrice(tours, sortedPrices);
+ break;
+ case "/n":
+ ArrayList sortedNames = tours.getSortedTourNames();
+ ui.showSortedTourByName(tours, sortedNames);
+ break;
+ default:
+ throw new TourPlannerException(ERROR_MISSING_FILTER);
+ }
+ } catch (TourPlannerException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/data/Client.java b/src/main/java/seedu/duke/data/Client.java
new file mode 100644
index 0000000000..5093be2a57
--- /dev/null
+++ b/src/main/java/seedu/duke/data/Client.java
@@ -0,0 +1,74 @@
+package seedu.duke.data;
+
+/**
+ * Represents a client under the Tour agency.
+ */
+public class Client {
+ private final String id;
+ private final String name;
+ private final String contactNum;
+ private final String email;
+
+ /**
+ * Client object constructor, that inputs the client's information obtained from Add command.
+ *
+ * @param values the array of values of the client, ordered in this manner:
+ * name, contact number, email
+ */
+ public Client(String[] values) {
+ id = values[0];
+ name = values[1];
+ contactNum = values[2];
+ email = values[3];
+ }
+
+ /**
+ * Getter for id value in Client object.
+ *
+ * @return client's id
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Getter for name value in Client object.
+ *
+ * @return client's name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Getter for contact number value in Client object.
+ *
+ * @return client's contact number
+ */
+ public String getContactNum() {
+ return contactNum;
+ }
+
+ /**
+ * Getter for email value in Client object.
+ *
+ * @return client's email
+ */
+ public String getEmail() {
+ return email;
+ }
+
+ /**
+ * Formats client's information as a string that is viewable as an indexed list item.
+ *
+ * @return the formatted string containing client's information
+ */
+ @Override
+ public String toString() {
+
+ return "Client ID: " + id + "\n"
+ + "Name: " + name + "\n"
+ + "Contact Number: " + contactNum + "\n"
+ + "Email: " + email;
+ }
+}
diff --git a/src/main/java/seedu/duke/data/ClientList.java b/src/main/java/seedu/duke/data/ClientList.java
new file mode 100644
index 0000000000..ea430635a5
--- /dev/null
+++ b/src/main/java/seedu/duke/data/ClientList.java
@@ -0,0 +1,151 @@
+package seedu.duke.data;
+
+import seedu.duke.TourPlannerException;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * List of clients.
+ */
+public class ClientList {
+ public static final String CLIENT_NOT_FOUND_MESSAGE = "ERROR: Client cannot be found. "
+ + "Please try another client ID";
+
+ private final ArrayList clients;
+ private final ArrayList clientIds;
+ private final ArrayList clientNames;
+ private ArrayList iteratedClientIds;
+ private int clientCount;
+
+ /**
+ * Class constructor for ClientList.
+ */
+ public ClientList() {
+ clients = new ArrayList<>();
+ clientIds = new ArrayList<>();
+ clientNames = new ArrayList<>();
+ clientCount = 0;
+ }
+
+ /**
+ * Getter for clients.
+ *
+ * @return ArrayList containing all clients in the database.
+ */
+ public ArrayList getClients() {
+ return clients;
+ }
+
+ /**
+ * Getter for number of clients in the client list.
+ *
+ * @return the number of clients in client list.
+ */
+ public int getClientCount() {
+ return clientCount;
+ }
+
+ /**
+ * Getter for client object in the client list, corresponding to the index given.
+ *
+ * @param index the index of the specific client in the client list
+ * @return the client object corresponding to the index
+ */
+ public Client getClientByIndex(int index) {
+ return clients.get(index);
+ }
+
+ /**
+ * Getter for client object in the client list, corresponding to the client id given.
+ *
+ * @param clientId client id of the specific client in the client list
+ * @return the client object corresponding to the client id
+ * @throws TourPlannerException if client object with clientId cannot be found in clients
+ */
+ public Client getClientById(String clientId) throws TourPlannerException {
+ for (Client client : clients) {
+ if (client.getId().equals(clientId)) {
+ return client;
+ }
+ }
+ throw new TourPlannerException(CLIENT_NOT_FOUND_MESSAGE);
+ }
+
+ /**
+ * Getter for client object in the client list, corresponding to the client name given.
+ *
+ * @param clientName the name of a client in the clientList
+ * @return the client object corresponding to the name
+ */
+ public Client getClientByName(String clientName) throws TourPlannerException {
+ for (Client currClient : clients) {
+ String clientId = currClient.getId();
+ String currClientName = currClient.getName();
+ if (currClientName.equals(clientName) && !iteratedClientIds.contains(clientId)) {
+ iteratedClientIds.add(clientId);
+ return currClient;
+ }
+ }
+ throw new TourPlannerException(CLIENT_NOT_FOUND_MESSAGE);
+ }
+
+ /**
+ * Getter for the sorted list of client IDs.
+ * Sorts the client IDs by the natural ordering of String, ignoring case sensitivities.
+ *
+ * @return the list of sorted client IDs, by natural(alphabetical, numerical) order
+ * @see Collections#sort(List, Comparator)
+ */
+ public ArrayList getSortedClientIds() {
+ Collections.sort(clientIds, String.CASE_INSENSITIVE_ORDER);
+ return clientIds;
+ }
+
+ /**
+ * Getter for the sorted list of client names.
+ * Sorts the client names by the natural ordering of String, ignoring case sensitivities.
+ *
+ * @return the list of sorted client names, by natural(alphabetical, numerical) order
+ * @see Collections#sort(List, Comparator)
+ */
+ public ArrayList getSortedClientNames() {
+ Collections.sort(clientNames, String.CASE_INSENSITIVE_ORDER);
+ return clientNames;
+ }
+
+ /**
+ * Creates a new temporary array each time the function is called.
+ * The flight IDs that have been iterated by Ui in the sort command will be added into FlightList's
+ * temporary array to prevent duplicates.
+ */
+ public void initTempArray() {
+ iteratedClientIds = new ArrayList<>();
+ }
+
+ /**
+ * Main method for adding a client.
+ *
+ * @param client the client to-be-added
+ */
+ public void add(Client client) {
+ clientCount++;
+ clients.add(client);
+ clientIds.add(client.getId());
+ clientNames.add(client.getName());
+ }
+
+ /**
+ * Main method for deleting a client.
+ *
+ * @param client the client-to-be-deleted
+ */
+ public void cut(Client client) {
+ clientIds.remove(client.getId());
+ clientNames.remove(client.getName());
+ clients.remove(client);
+ clientCount--;
+ }
+}
diff --git a/src/main/java/seedu/duke/data/ClientPackage.java b/src/main/java/seedu/duke/data/ClientPackage.java
new file mode 100644
index 0000000000..375f4168b5
--- /dev/null
+++ b/src/main/java/seedu/duke/data/ClientPackage.java
@@ -0,0 +1,78 @@
+package seedu.duke.data;
+
+/**
+ * Represents a packaged client under the Tour agency.
+ */
+public class ClientPackage {
+ private final String clientPackageId;
+ private final Client client;
+ private final Tour tour;
+ private final Flight flight;
+
+ /**
+ * Class constructor for ClientPackage.
+ *
+ * @param clientPackageId unique ID of client package to identify it by
+ * @param client client to be added to the client package
+ * @param tour tour to be added to the client package
+ * @param flight flight to be added to the client package
+ */
+ public ClientPackage(String clientPackageId, Client client, Tour tour, Flight flight) {
+ this.clientPackageId = clientPackageId;
+ this.client = client;
+ this.tour = tour;
+ this.flight = flight;
+ }
+
+ /**
+ * Getter for Client object of client in the client package.
+ *
+ * @return client in the client package
+ */
+ public Client getClient() {
+ return client;
+ }
+
+ /**
+ * Getter for Tour object of tour in the client package.
+ *
+ * @return tour in the client package
+ */
+ public Tour getTour() {
+ return tour;
+ }
+
+ /**
+ * Getter for Flight object of flight in the client package.
+ *
+ * @return flight in the client package
+ */
+ public Flight getFlight() {
+ return flight;
+ }
+
+ /**
+ * Getter for id value of ClientPackage object.
+ *
+ * @return id of the client package
+ */
+ public String getId() {
+ return clientPackageId;
+ }
+
+ /**
+ * Formats client package's information as a string that is viewable as an indexed list item.
+ *
+ * @return the formatted string containing client package's information
+ */
+ @Override
+ public String toString() {
+ return "Package ID: " + clientPackageId + "\n\n"
+ + "Client: " + "\n"
+ + client + "\n\n"
+ + "Tour: " + "\n"
+ + tour + "\n\n"
+ + "Flight: " + "\n"
+ + flight;
+ }
+}
diff --git a/src/main/java/seedu/duke/data/ClientPackageList.java b/src/main/java/seedu/duke/data/ClientPackageList.java
new file mode 100644
index 0000000000..70cdab4d19
--- /dev/null
+++ b/src/main/java/seedu/duke/data/ClientPackageList.java
@@ -0,0 +1,140 @@
+package seedu.duke.data;
+
+import seedu.duke.TourPlannerException;
+
+import java.util.ArrayList;
+
+/**
+ * List of client packages.
+ */
+public class ClientPackageList {
+ public static final String CLIENTPACKAGE_NOT_FOUND_MESSAGE
+ = "Client Package cannot be found. Please try another client package ID";
+
+ private final ArrayList clientPackages;
+ private int clientPackageCount;
+
+ /**
+ * Class constructor for ClientPackageList.
+ */
+ public ClientPackageList() {
+ clientPackages = new ArrayList<>();
+ clientPackageCount = 0;
+ }
+
+ /**
+ * Getter for clientPackages.
+ *
+ * @return ArrayList containing all client packages in the database
+ */
+ public ArrayList getClientPackages() {
+ return clientPackages;
+ }
+
+ /**
+ * Getter for ClientPackage object according to its index in clientPackages.
+ *
+ * @param index index of the client package in clientPackages
+ * @return ClientPackage object of the corresponding index
+ */
+ public ClientPackage getClientPackageByIndex(int index) {
+ return clientPackages.get(index);
+ }
+
+ /**
+ * Getter for ClientPackage object according to its clientPackageId.
+ *
+ * @param clientPackageId id of the client package to get from clientPackages
+ * @return ClientPackage object of the corresponding client package id
+ * @throws TourPlannerException if client package containing clientPackageId cannot be found in clientPackages
+ */
+ public ClientPackage getClientPackageById(String clientPackageId) throws TourPlannerException {
+ for (int i = 0; i < clientPackageCount; i++) {
+ if (clientPackages.get(i).getId().equals(clientPackageId)) {
+ return clientPackages.get(i);
+ }
+ }
+ throw new TourPlannerException(CLIENTPACKAGE_NOT_FOUND_MESSAGE);
+ }
+
+ /**
+ * Getter for ClientPackage objects that contain client.
+ *
+ * @param client client that is contained inside the client package(s)
+ * @return ArrayList of client packages that contain that client
+ */
+ public ArrayList getClientPackageByClient(Client client) {
+ ArrayList clientPackagesWithClient = new ArrayList<>();
+ for (int i = 0; i < clientPackageCount; i++) {
+ ClientPackage clientPackage = clientPackages.get(i);
+ if (clientPackage.getClient().equals(client)) {
+ clientPackagesWithClient.add(clientPackage);
+ }
+ }
+ return clientPackagesWithClient;
+ }
+
+ /**
+ * Getter for ClientPackage objects that contain tour.
+ *
+ * @param tour tour that is contained inside the client package(s)
+ * @return ArrayList of client packages that contain tour
+ */
+ public ArrayList getClientPackageByTour(Tour tour) {
+ ArrayList clientPackagesWithTour = new ArrayList<>();
+ for (int i = 0; i < clientPackageCount; i++) {
+ ClientPackage clientPackage = clientPackages.get(i);
+ if (clientPackage.getTour().equals(tour)) {
+ clientPackagesWithTour.add(clientPackage);
+ }
+ }
+ return clientPackagesWithTour;
+ }
+
+ /**
+ * Getter for ClientPackage objects that contain flight.
+ *
+ * @param flight flight that is contained inside the client package(s)
+ * @return ArrayList of client packages that contain flight
+ */
+ public ArrayList getClientPackageByFlight(Flight flight) {
+ ArrayList clientPackagesWithFlight = new ArrayList<>();
+ for (int i = 0; i < clientPackageCount; i++) {
+ ClientPackage clientPackage = clientPackages.get(i);
+ if (clientPackage.getFlight().equals(flight)) {
+ clientPackagesWithFlight.add(clientPackage);
+ }
+ }
+ return clientPackagesWithFlight;
+ }
+
+ /**
+ * Getter for number of client packages in ClientPackageList clientPackages.
+ *
+ * @return clientPackageCount of clientPackages
+ */
+ public int getClientPackageCount() {
+ return clientPackageCount;
+ }
+
+ /**
+ * Adds clientPackage to clientPackages.
+ *
+ * @param clientPackage ClientPackage object to be added to clientPackages
+ */
+ public void add(ClientPackage clientPackage) {
+ clientPackageCount++;
+ clientPackages.add(clientPackage);
+ }
+
+ /**
+ * Cuts clientPackage from clientPackages.
+ *
+ * @param clientPackage ClientPackage object to be deleted from clientPackages
+ */
+ public void cut(ClientPackage clientPackage) {
+ clientPackages.remove(clientPackage);
+ clientPackageCount--;
+ }
+
+}
diff --git a/src/main/java/seedu/duke/data/Flight.java b/src/main/java/seedu/duke/data/Flight.java
new file mode 100644
index 0000000000..eb3ebbb2ff
--- /dev/null
+++ b/src/main/java/seedu/duke/data/Flight.java
@@ -0,0 +1,83 @@
+package seedu.duke.data;
+
+/**
+ * Represents a flight under the Tour agency.
+ */
+public class Flight {
+ private final String id;
+ private final String departDestination;
+ private final String returnDestination;
+ private final String departDate;
+ private final String returnDate;
+
+ /**
+ * Flight object constructor, that inputs the flight's information obtained from Add command.
+ *
+ * @param values the array of values of the flight, ordered in this manner:
+ * flight ID, departure destination, return destination, departure date-time, return date-time
+ */
+ public Flight(String[] values) {
+ id = values[0];
+ departDestination = values[1];
+ returnDestination = values[2];
+ departDate = values[3];
+ returnDate = values[4];
+ }
+
+ /**
+ * Getter for id value in Flight object.
+ *
+ * @return flight's id
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Getter for return destination in Flight object.
+ *
+ * @return flight's return destination
+ */
+ public String getReturnDestination() {
+ return returnDestination;
+ }
+
+ /**
+ * Getter for departure destination in Flight object.
+ *
+ * @return flight's departure destination
+ */
+ public String getDepartDestination() {
+ return departDestination;
+ }
+
+ /**
+ * Getter for return date-time in Flight's object.
+ *
+ * @return flight's return date-time
+ */
+ public String getReturnDate() {
+ return returnDate;
+ }
+
+ /**
+ * Getter for departure date-time in Flight's object.
+ *
+ * @return flight's departure date-time
+ */
+ public String getDepartDate() {
+ return departDate;
+ }
+
+ /**
+ * Formats flight's information as a string that is viewable as an indexed list item.
+ *
+ * @return the formatted string containing flight's information
+ */
+ @Override
+ public String toString() {
+ return "Flight ID: " + id + "\n"
+ + "Departure Flight: " + departDestination + ", " + departDate + "\n"
+ + "Return Flight: " + returnDestination + ", " + returnDate;
+ }
+}
diff --git a/src/main/java/seedu/duke/data/FlightList.java b/src/main/java/seedu/duke/data/FlightList.java
new file mode 100644
index 0000000000..c2a5be1cf8
--- /dev/null
+++ b/src/main/java/seedu/duke/data/FlightList.java
@@ -0,0 +1,218 @@
+package seedu.duke.data;
+
+import seedu.duke.TourPlannerException;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * List of flights.
+ */
+public class FlightList {
+ public static final String FLIGHT_NOT_FOUND_MESSAGE = "ERROR: Flight cannot be found. "
+ + "Please try another flight ID.";
+
+ private final ArrayList flights;
+ private final ArrayList flightIds;
+ private final ArrayList flightReturnDates;
+ private final ArrayList flightDepartureDates;
+ private ArrayList iteratedFlightIds;
+ private int flightCount;
+
+ /**
+ * Class constructor for FlightList.
+ */
+ public FlightList() {
+ flights = new ArrayList<>();
+ flightIds = new ArrayList<>();
+ flightReturnDates = new ArrayList<>();
+ flightDepartureDates = new ArrayList<>();
+ flightCount = 0;
+ }
+
+ /**
+ * Getter for flight list.
+ *
+ * @return the list of flights
+ */
+ public ArrayList getFlights() {
+ return flights;
+ }
+
+
+ /**
+ * Getter for number of flights in the flight list.
+ *
+ * @return the number of flights in flight list.
+ */
+ public int getFlightCount() {
+ return flightCount;
+ }
+
+ /**
+ * Getter for flight object in the flight list, corresponding to the index given.
+ *
+ * @param index the index of the specific flight in the flight list
+ * @return the flight object corresponding to the index
+ */
+ public Flight getFlightByIndex(int index) {
+ return flights.get(index);
+ }
+
+ /**
+ * Getter for flight object in the flight list, corresponding to the flight ID given.
+ *
+ * @param flightId the given flight ID, unique to each set of flights
+ * @return the flight object, specified by the flight ID
+ * @throws TourPlannerException if the flight with the given flight ID is not found in the database
+ */
+ public Flight getFlightById(String flightId) throws TourPlannerException {
+ for (int i = 0; i < flightCount; i++) {
+ if (flights.get(i).getId().equals(flightId)) {
+ return flights.get(i);
+ }
+ }
+ throw new TourPlannerException(FLIGHT_NOT_FOUND_MESSAGE);
+ }
+
+ /**
+ * Comparator for extension of Collections.sort() which does not include date-time sort functionality.
+ * Overrides compare method within the Comparator to compare between date-times.
+ *
+ * @see Comparator
+ */
+ private static final Comparator dateTimeStringComparator = new Comparator<>() {
+ final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yy HH:mm");
+
+ @Override
+ public int compare(String dateTimeStringOne, String dateTimeStringTwo) {
+ LocalDateTime dateTimeOne = LocalDateTime.parse(dateTimeStringOne, formatter);
+ LocalDateTime dateTimeTwo = LocalDateTime.parse(dateTimeStringTwo, formatter);
+ if (dateTimeOne.isBefore(dateTimeTwo)) {
+ return -1;
+ } else if (dateTimeOne.isEqual(dateTimeTwo)) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+ };
+
+ /**
+ * Getter for flight object in the flight list, corresponding to the flight return date given.
+ *
+ * @param flightReturnDate the date-time of the return flight, in String
+ * @return the flight object, specified by the return flight's date-time
+ * @throws TourPlannerException if the flight with the given return flight's date-time is not found in the database
+ */
+ public Flight getFlightByReturnDate(String flightReturnDate) throws TourPlannerException {
+ for (Flight currFlight : flights) {
+ String flightId = currFlight.getId();
+ String currFlightReturnDate = currFlight.getReturnDate();
+ if (currFlightReturnDate.equals(flightReturnDate) && !iteratedFlightIds.contains(flightId)) {
+ iteratedFlightIds.add(flightId);
+ return currFlight;
+ }
+ }
+ throw new TourPlannerException(FLIGHT_NOT_FOUND_MESSAGE);
+ }
+
+ /**
+ * Getter for flight object in the flight list, corresponding to the flight departure date given.
+ *
+ * @param flightDepartDate the date-time of the departure flight, in String
+ * @return the flight object, specified by the departure flight's date-time
+ * @throws TourPlannerException if the flight with the given departure flight's date-time
+ * is not found in the database
+ */
+ public Flight getFlightByDepartDate(String flightDepartDate) throws TourPlannerException {
+ for (Flight currFlight : flights) {
+ String flightId = currFlight.getId();
+ String currFlightDepartDate = currFlight.getDepartDate();
+ if (currFlightDepartDate.equals(flightDepartDate) && !iteratedFlightIds.contains(flightId)) {
+ iteratedFlightIds.add(flightId);
+ return currFlight;
+ }
+ }
+ throw new TourPlannerException(FLIGHT_NOT_FOUND_MESSAGE);
+ }
+
+ /**
+ * Getter for the sorted list of flight IDs.
+ * Sorts the flight IDs by the natural ordering of String, ignoring case sensitivities.
+ *
+ * @return the list of sorted flight IDs, by natural(alphabetical, numerical) order
+ * @see Collections#sort(List, Comparator)
+ */
+ public ArrayList getSortedFlightIds() {
+ Collections.sort(flightIds, String.CASE_INSENSITIVE_ORDER);
+ return flightIds;
+ }
+
+ /**
+ * Getter for the sorted list of flight return date-times.
+ * Sorts using the dateTimeStringComparator in FlightList.
+ *
+ * @return the list of sorted return flight date-times, as specified in dateTimeStringComparator
+ * @see FlightList#dateTimeStringComparator
+ * @see Collections#sort(List)
+ */
+ public ArrayList getSortedReturnDates() {
+ flightReturnDates.sort(dateTimeStringComparator);
+ return flightReturnDates;
+ }
+
+ /**
+ * Getter for the sorted list of flight departure date-times.
+ * Sorts using the dateTimeStringComparator in FlightList.
+ *
+ * @return the list of sorted departure flight date-times, as specified in dateTimeStringComparator
+ * @see FlightList#dateTimeStringComparator
+ * @see Collections#sort(List)
+ */
+ public ArrayList getSortedDepartDates() {
+ flightDepartureDates.sort(dateTimeStringComparator);
+ return flightDepartureDates;
+ }
+
+ /**
+ * Creates a new temporary array each time the function is called.
+ * The flight IDs that have been iterated by Ui in the sort command will be added into FlightList's
+ * temporary array to prevent duplicates.
+ */
+ public void initTempArray() {
+ iteratedFlightIds = new ArrayList<>();
+ }
+
+ /**
+ * Main method for adding a flight.
+ * Add all information of different fields into their respective ArrayLists.
+ *
+ * @param flight the flight to be added
+ */
+ public void add(Flight flight) {
+ flights.add(flight);
+ flightIds.add(flight.getId());
+ flightReturnDates.add(flight.getReturnDate());
+ flightDepartureDates.add(flight.getDepartDate());
+ flightCount++;
+ }
+
+ /**
+ * Main method for cutting a flight.
+ * Removes all information of different fields from their respective ArrayLists.
+ *
+ * @param flight the flight to be added
+ */
+ public void cut(Flight flight) {
+ flights.remove(flight);
+ flightIds.remove(flight.getId());
+ flightReturnDates.remove(flight.getReturnDate());
+ flightDepartureDates.remove(flight.getDepartDate());
+ flightCount--;
+ }
+}
diff --git a/src/main/java/seedu/duke/data/Tour.java b/src/main/java/seedu/duke/data/Tour.java
new file mode 100644
index 0000000000..3b03abc6fc
--- /dev/null
+++ b/src/main/java/seedu/duke/data/Tour.java
@@ -0,0 +1,61 @@
+package seedu.duke.data;
+
+/**
+ * Represents a tour under the Tour agency.
+ */
+public class Tour {
+ private final String id;
+ private final String name;
+ private final Float price;
+
+ /**
+ * Tour object constructor, that inputs the tour's information obtained from Add command.
+ *
+ * @param values the array of values of the tour, ordered in this manner:
+ * id, name, price
+ */
+ public Tour(String[] values) {
+ id = values[0];
+ name = values[1];
+ price = Float.parseFloat(values[2]);
+ }
+
+ /**
+ * Getter for id value in Tour object.
+ *
+ * @return tour's ID.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Getter for name value in Tour object.
+ *
+ * @return tour's name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Getter for price value in Tour object.
+ *
+ * @return tour's price.
+ */
+ public Float getPrice() {
+ return price;
+ }
+
+ /**
+ * Formats tour's information as a string that is viewable as an indexed list item.
+ *
+ * @return the formatted string containing tour's information
+ */
+ @Override
+ public String toString() {
+ return "Tour ID: " + id + "\n"
+ + "Name: " + name + "\n"
+ + "Price per pax: $" + String.format("%.02f", price);
+ }
+}
diff --git a/src/main/java/seedu/duke/data/TourList.java b/src/main/java/seedu/duke/data/TourList.java
new file mode 100644
index 0000000000..3658ed8361
--- /dev/null
+++ b/src/main/java/seedu/duke/data/TourList.java
@@ -0,0 +1,174 @@
+package seedu.duke.data;
+
+import seedu.duke.TourPlannerException;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * List of tours.
+ */
+public class TourList {
+ public static final String TOUR_NOT_FOUND_MESSAGE = "ERROR: Tour cannot be found. Please try another tour ID.";
+
+ private final ArrayList tours;
+ private final ArrayList tourIds;
+ private final ArrayList tourNames;
+ private final ArrayList tourPrices;
+ private ArrayList iteratedTourIds;
+ private int tourCount;
+
+ /**
+ * Class constructor for TourList.
+ */
+ public TourList() {
+ tours = new ArrayList<>();
+ tourIds = new ArrayList<>();
+ tourPrices = new ArrayList<>();
+ tourNames = new ArrayList<>();
+ tourCount = 0;
+ }
+
+ /**
+ * Getter for clients.
+ *
+ * @return ArrayList containing all clients in the database.
+ */
+ public ArrayList getTours() {
+ return tours;
+ }
+
+ public int getTourCount() {
+ return tourCount;
+ }
+
+ public Tour getTourByIndex(int index) {
+ return tours.get(index);
+ }
+
+ /**
+ * Getter for tour object in the tour list, corresponding to the tour price given.
+ *
+ * @param price the given tour price
+ * @return the tour object, specified by the tour price
+ * @throws TourPlannerException if the tour with the given tour price is not found in the database
+ */
+ public Tour getTourByPrice(Float price) throws TourPlannerException {
+ for (Tour currTour : tours) {
+ String currTourId = currTour.getId();
+ if (Objects.equals(currTour.getPrice(), price) && !iteratedTourIds.contains(currTourId)) {
+ iteratedTourIds.add(currTourId);
+ return currTour;
+ }
+ }
+ throw new TourPlannerException(TOUR_NOT_FOUND_MESSAGE);
+ }
+
+ /**
+ * Getter for tour object in the tour list, corresponding to the tour name given.
+ *
+ * @param name the given tour name
+ * @return the tour object, specified by the tour ID
+ * @throws TourPlannerException if the tour with the given tour ID is not found in the database
+ */
+ public Tour getTourByName(String name) throws TourPlannerException {
+ for (int i = 0; i < tourCount; i++) {
+ Tour currentTour = tours.get(i);
+ if (currentTour.getName().equals(name)) {
+ return currentTour;
+ }
+ }
+ throw new TourPlannerException(TOUR_NOT_FOUND_MESSAGE);
+ }
+
+ /**
+ * Getter for tour object in the tour list, corresponding to the tour ID given.
+ *
+ * @param id the given tour ID, unique to each tour
+ * @return the tour object, specified by the tour ID
+ * @throws TourPlannerException if the tour with the given tour ID is not found in the database
+ */
+ public Tour getTourById(String id) throws TourPlannerException {
+ for (int i = 0; i < tourCount; i++) {
+ Tour currentTour = tours.get(i);
+ if (currentTour.getId().equals(id)) {
+ return currentTour;
+ }
+ }
+ throw new TourPlannerException(TOUR_NOT_FOUND_MESSAGE);
+ }
+
+ /**
+ * Getter for the sorted list of tour prices.
+ * Sorts the tour prices by the natural ordering of Float.
+ *
+ * @return the list of sorted tour prices, by ascending order
+ * @see Collections#sort
+ */
+ public ArrayList getSortedTourPrices() {
+ Collections.sort(tourPrices);
+ return tourPrices;
+ }
+
+ /**
+ * Getter for the sorted list of tour IDs.
+ * Sorts the tour IDs by the natural ordering of String, ignoring case sensitivities.
+ *
+ * @return the list of sorted tour IDs, by natural(alphabetical, numerical) order
+ * @see Collections#sort(List, Comparator)
+ */
+ public ArrayList getSortedTourIds() {
+ Collections.sort(tourIds, String.CASE_INSENSITIVE_ORDER);
+ return tourIds;
+ }
+
+ /**
+ * Getter for the sorted list of tour names.
+ * Sorts the tour names by the natural ordering of String, ignoring case sensitivities.
+ *
+ * @return the list of sorted tour names, by natural(alphabetical, numerical) order
+ * @see Collections#sort(List, Comparator)
+ */
+ public ArrayList getSortedTourNames() {
+ Collections.sort(tourNames, String.CASE_INSENSITIVE_ORDER);
+ return tourNames;
+ }
+
+ /**
+ * Creates a new temporary array each time the function is called.
+ * The tour IDs that have been iterated by Ui in the sort command will be added into TourList's
+ * temporary array to prevent duplicates.
+ */
+ public void initTempArray() {
+ iteratedTourIds = new ArrayList<>();
+ }
+
+ /**
+ * Main method for adding a tour.
+ *
+ * @param tour the tour to-be-added
+ */
+ public void add(Tour tour) {
+ tours.add(tour);
+ tourIds.add(tour.getId());
+ tourPrices.add(tour.getPrice());
+ tourNames.add(tour.getName());
+ tourCount++;
+ }
+
+ /**
+ * Main method for deleting a tour.
+ *
+ * @param tour the tour to-be-added
+ */
+ public void cut(Tour tour) {
+ tourIds.remove(tour.getId());
+ tourPrices.remove(tour.getPrice());
+ tourNames.remove(tour.getName());
+ tours.remove(tour);
+ tourCount--;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/seedu/duke/storage/ClientPackageStorage.java b/src/main/java/seedu/duke/storage/ClientPackageStorage.java
new file mode 100644
index 0000000000..3fe1eb1c2c
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/ClientPackageStorage.java
@@ -0,0 +1,160 @@
+
+
+package seedu.duke.storage;
+
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.clientpackages.AddClientPackageCommand;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackage;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.TourList;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+//@@author Demonshaha
+
+/**
+ * Storage class for all ClientPackages. Creates and loads up the ClientPackageList.
+ */
+public class ClientPackageStorage {
+ public static final String EMPTY_STRING = "";
+ private final ClientPackageList clientPackages = new ClientPackageList();
+ private static final String root = System.getProperty("user.dir");
+ private static final Path filePath = Paths.get(root, "data", "TourPlannerClientPackages.txt");
+ private static final Path dirPath = Paths.get(root, "data");
+ private static boolean hasClient = false;
+ private static boolean hasTour = false;
+ private static boolean hasFlight = false;
+ private static boolean hasClientPackage = false;
+ private static boolean isPackageAdded = false;
+ private ArrayList rawClientPackage = new ArrayList<>();
+
+ /**
+ * Class constructor for ClientPackageStorage.
+ * Creates directory and files for storage if they do not already exist.
+ *
+ * @throws TourPlannerException if IOException is thrown when creating a new directory/file
+ * @see File
+ * @see IOException
+ */
+ public ClientPackageStorage() throws TourPlannerException {
+ try {
+ File fileDirectory = new File(dirPath.toString());
+ if (!fileDirectory.exists()) {
+ fileDirectory.mkdir();
+ }
+
+ File dataFile = new File(filePath.toString());
+ dataFile.createNewFile();
+ } catch (IOException e) {
+ throw new TourPlannerException(EMPTY_STRING);
+ }
+ }
+
+ /**
+ * Getter for ClientPackageList created and populated with client packages retrieved from data file
+ * containing previously stored client packages.
+ *
+ * @return clientPackages retrieved
+ */
+ public ClientPackageList getClientPackages() {
+ return clientPackages;
+ }
+
+ /**
+ * Reads each line of the file and creates the ClientPackage and adds it to the ClientPackageList
+ * using AddClientPackageCommand.
+ *
+ * @param clients list of clients retrieved from storage file
+ * @param tours list of tours retrieved from storage file
+ * @param flights list of flights retrieved from storage file
+ * @param ui user interface used to display messages to the user
+ * @throws TourPlannerException if FileNotFoundException or NumberFormatException thrown
+ * due to corrupted or missing file
+ * @see AddClientPackageCommand
+ * @see FileNotFoundException
+ * @see NumberFormatException
+ */
+ public void loadFile(ClientList clients, TourList tours, FlightList flights, Ui ui) throws TourPlannerException {
+ try {
+ File dataFile = new File(filePath.toString());
+ Scanner scanner = new Scanner(dataFile);
+
+ while (scanner.hasNext()) {
+ String line = scanner.nextLine();
+ String clientPackageId;
+ String clientId;
+ String tourId;
+ String flightId;
+
+ if (line.equals("Client Package Details: ")) {
+ resetCheckerStates();
+ } else if (line.contains("Package ID")) {
+ clientPackageId = line.substring(12);
+ rawClientPackage.add(clientPackageId);
+ hasClientPackage = true;
+ } else if (line.equals("Client: ")) {
+ line = scanner.nextLine();
+ clientId = line.substring(11);
+ rawClientPackage.add(clientId);
+ hasClient = true;
+ } else if (line.equals("Tour: ")) {
+ line = scanner.nextLine();
+ tourId = line.substring(9);
+ rawClientPackage.add(tourId);
+ hasTour = true;
+ } else if (line.equals("Flight: ")) {
+ line = scanner.nextLine();
+ flightId = line.substring(11);
+ rawClientPackage.add(flightId);
+ hasFlight = true;
+ }
+
+ if (hasClient && hasFlight && hasTour && hasClientPackage && !isPackageAdded) {
+ AddClientPackageCommand command =
+ new AddClientPackageCommand(rawClientPackage.toArray(new String[]{}));
+ command.setData(clients, flights, tours, clientPackages, ui);
+ command.executeStorage();
+ isPackageAdded = true;
+ }
+ }
+ } catch (FileNotFoundException | NumberFormatException e) {
+ throw new TourPlannerException(EMPTY_STRING);
+ }
+ }
+
+ private void resetCheckerStates() {
+ hasClient = false;
+ hasFlight = false;
+ hasTour = false;
+ hasClientPackage = false;
+ isPackageAdded = false;
+ rawClientPackage = new ArrayList<>();
+ }
+
+ /**
+ * Loops through clientPackages and writes into storage file for ClientPackage.
+ */
+ public void saveFile() {
+ try {
+ ArrayList clientPackageList = clientPackages.getClientPackages();
+ FileWriter writer = new FileWriter(filePath.toString());
+ for (ClientPackage clientPackage : clientPackageList) {
+ writer.write("Client Package Details: \n");
+ writer.write(clientPackage.toString() + "\n\n");
+ }
+ writer.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/ClientStorage.java b/src/main/java/seedu/duke/storage/ClientStorage.java
new file mode 100644
index 0000000000..cb4bc7293d
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/ClientStorage.java
@@ -0,0 +1,113 @@
+package seedu.duke.storage;
+
+import seedu.duke.TourPlannerException;
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientList;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+//@@author Demonshaha
+/**
+ * Storage class for all Clients. Creates and loads up the ClientList.
+ */
+public class ClientStorage {
+ public static final String EMPTY_STRING = "";
+ private final ClientList clients = new ClientList();
+ private static final String root = System.getProperty("user.dir");
+ private static final Path filePath = Paths.get(root, "data", "TourPlannerClients.txt");
+ private static final Path dirPath = Paths.get(root, "data");
+
+ /**
+ * Class constructor for ClientStorage.
+ * Creates directory and files for storage if they do not already exist.
+ *
+ * @throws TourPlannerException if IOException is thrown when creating a new directory/file
+ * @see File
+ * @see IOException
+ */
+ public ClientStorage() throws TourPlannerException {
+ try {
+ File fileDirectory = new File(dirPath.toString());
+ if (!fileDirectory.exists()) {
+ fileDirectory.mkdir();
+ }
+
+ File dataFile = new File(filePath.toString());
+ dataFile.createNewFile();
+ } catch (IOException e) {
+ throw new TourPlannerException(EMPTY_STRING);
+ }
+ }
+
+ /**
+ * Getter for ClientList created and populated with clients retrieved from data file
+ * containing previously stored clients.
+ *
+ * @return clients retrieved
+ */
+ public ClientList getClients() {
+ return clients;
+ }
+
+ /**
+ * Reads each line of the file and identifies client fields -- clientId, name, contactNum and mail.
+ * Creates new Client object based on the client fields and adds to ClientList.
+ *
+ * @throws TourPlannerException if FileNotFoundException due to corrupted or missing file
+ * @see FileNotFoundException
+ */
+ public void loadFile() throws TourPlannerException {
+ try {
+ File dataFile = new File(filePath.toString());
+ Scanner scanner = new Scanner(dataFile);
+ String clientId;
+ String clientName;
+ String clientContactNum;
+ String clientEmail;
+
+ while (scanner.hasNext()) {
+ String line = scanner.nextLine();
+
+ if (line.contains("Client Details:")) {
+ line = scanner.nextLine();
+ clientId = line.substring(11);
+ line = scanner.nextLine();
+ clientName = line.substring(6);
+ line = scanner.nextLine();
+ clientContactNum = line.substring(16);
+ line = scanner.nextLine();
+ clientEmail = line.substring(7);
+ String[] clientArray = {clientId, clientName, clientContactNum, clientEmail};
+ Client client = new Client(clientArray);
+ clients.add(client);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ throw new TourPlannerException(EMPTY_STRING);
+ }
+ }
+
+ /**
+ * Loops through clients and writes into storage file for Client.
+ */
+ public void saveFile() {
+ try {
+ ArrayList clientList = clients.getClients();
+ FileWriter writer = new FileWriter(filePath.toString());
+ for (Client client : clientList) {
+ writer.write("Client Details: \n");
+ writer.write(client.toString() + System.lineSeparator());
+ }
+ writer.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/FlightStorage.java b/src/main/java/seedu/duke/storage/FlightStorage.java
new file mode 100644
index 0000000000..0a918bf9ba
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/FlightStorage.java
@@ -0,0 +1,115 @@
+package seedu.duke.storage;
+
+import seedu.duke.TourPlannerException;
+import seedu.duke.data.Flight;
+import seedu.duke.data.FlightList;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+//@@author Demonshaha
+/**
+ * Storage class for flights. Creates and loads up the FlightList.
+ */
+public class FlightStorage {
+ private final FlightList flights = new FlightList();
+ private static final String root = System.getProperty("user.dir");
+ private static final Path filePath = Paths.get(root, "data", "TourPlannerFlights.txt");
+ private static final Path dirPath = Paths.get(root, "data");
+
+ /**
+ * Class constructor for FlightStorage.
+ * Creates directory and files for storage if they do not already exist.
+ *
+ * @throws TourPlannerException if there are IOException thrown when creating a new directory/file
+ * @see File
+ * @see IOException
+ */
+ public FlightStorage() throws TourPlannerException {
+ try {
+ File fileDirectory = new File(dirPath.toString());
+ if (!fileDirectory.exists()) {
+ fileDirectory.mkdir();
+ }
+
+ File dataFile = new File(filePath.toString());
+ dataFile.createNewFile();
+ } catch (IOException e) {
+ throw new TourPlannerException("File ERROR");
+ }
+ }
+
+ /**
+ * Getter for flight list.
+ *
+ * @return the list of flights stored in database
+ */
+ public FlightList getFlights() {
+ return flights;
+ }
+
+ /**
+ * Reads each line of the file and identifies flight fields -- flightId,
+ * departure destination, return destination, departure date-time, return date-time.
+ * Creates new Flight object based on the flight fields and adds to FlightList.
+ *
+ * @throws TourPlannerException if FileNotFoundException due to corrupted or missing file
+ * @see FileNotFoundException
+ */
+ public void loadFile() throws TourPlannerException {
+ try {
+ File dataFile = new File(filePath.toString());
+ Scanner scanner = new Scanner(dataFile);
+ String flightId;
+ String from;
+ String to;
+ String fromDate;
+ String toDate;
+
+ while (scanner.hasNext()) {
+ String line = scanner.nextLine();
+
+ if (line.contains("Flight Details:")) {
+ line = scanner.nextLine();
+ flightId = line.substring(11);
+ line = scanner.nextLine();
+ int index = line.indexOf(", ");
+ from = line.substring(18, index);
+ fromDate = line.substring(index + 2);
+ line = scanner.nextLine();
+ index = line.indexOf(", ");
+ to = line.substring(15, index);
+ toDate = line.substring(index + 2);
+ String[] flightArray = {flightId, to, from, toDate, fromDate};
+ Flight flight = new Flight(flightArray);
+ flights.add(flight);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ throw new TourPlannerException("File not found!");
+ }
+ }
+
+ /**
+ * Loops through flights and writes into storage file for Flights.
+ */
+ public void saveFile() {
+ ArrayList flightList = flights.getFlights();
+ try {
+ FileWriter writer = new FileWriter(filePath.toString());
+ for (Flight flight : flightList) {
+ writer.write("Flight Details: \n");
+ writer.write(flight.toString() + System.lineSeparator());
+ }
+ writer.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/TourStorage.java b/src/main/java/seedu/duke/storage/TourStorage.java
new file mode 100644
index 0000000000..5cb633984a
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/TourStorage.java
@@ -0,0 +1,111 @@
+package seedu.duke.storage;
+
+import seedu.duke.TourPlannerException;
+import seedu.duke.data.Tour;
+import seedu.duke.data.TourList;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+//@@author Demonshaha
+/**
+ * Storage class for all Tours. Creates and loads up the TourList.
+ */
+public class TourStorage {
+ public static final String EMPTY_STRING = "";
+ private final TourList tours = new TourList();
+ private static final String root = System.getProperty("user.dir");
+ private static final Path filePath = Paths.get(root, "data", "TourPlannerTours.txt");
+ private static final Path dirPath = Paths.get(root, "data");
+
+ /**
+ * Class constructor for TourStorage.
+ * Creates directory and files for storage if they do not already exist.
+ *
+ * @throws TourPlannerException if IOException is thrown when creating a new directory/file
+ * @see File
+ * @see IOException
+ */
+ public TourStorage() throws TourPlannerException {
+ try {
+ File fileDirectory = new File(dirPath.toString());
+ if (!fileDirectory.exists()) {
+ fileDirectory.mkdir();
+ }
+
+ File dataFile = new File(filePath.toString());
+ dataFile.createNewFile();
+ } catch (IOException e) {
+ throw new TourPlannerException(EMPTY_STRING);
+ }
+ }
+
+ /**
+ * Getter for ClientList created and populated with clients retrieved from data file
+ * containing previously stored clients.
+ *
+ * @return clients retrieved
+ */
+ public TourList getTours() {
+ return tours;
+ }
+
+ /**
+ * Reads each line of the file and identifies tour fields -- tourId, name and price.
+ * Creates new Tour object based on the tour fields and adds to TourList.
+ *
+ * @throws TourPlannerException if FileNotFoundException thrown due to corrupted or missing file
+ * @see FileNotFoundException
+ */
+ public void loadFile() throws TourPlannerException {
+ try {
+ File dataFile = new File(filePath.toString());
+ Scanner scanner = new Scanner(dataFile);
+ String tourName;
+ String tourId;
+ String tourPrice;
+
+ while (scanner.hasNext()) {
+ String line = scanner.nextLine();
+
+ if (line.contains("Tour Details:")) {
+ line = scanner.nextLine();
+ tourId = line.substring(9);
+ line = scanner.nextLine();
+ tourName = line.substring(6);
+ line = scanner.nextLine();
+ int index = line.indexOf("$");
+ tourPrice = line.substring(index + 1);
+ String[] tourArray = {tourId, tourName, tourPrice};
+ Tour tour = new Tour(tourArray);
+ tours.add(tour);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ throw new TourPlannerException(EMPTY_STRING);
+ }
+ }
+
+ /**
+ * Loops through tours and writes into storage file for Tour.
+ */
+ public void saveFile() {
+ ArrayList tourList = tours.getTours();
+ try {
+ FileWriter writer = new FileWriter(filePath.toString());
+ for (Tour tour : tourList) {
+ writer.write("Tour Details: \n");
+ writer.write(tour.toString() + System.lineSeparator());
+ }
+ writer.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/seedu/uml/AddClientPackageCommand.puml b/src/main/java/seedu/uml/AddClientPackageCommand.puml
new file mode 100644
index 0000000000..bcce4de7bb
--- /dev/null
+++ b/src/main/java/seedu/uml/AddClientPackageCommand.puml
@@ -0,0 +1,49 @@
+@startuml
+
+participant ":Parser" as Parser
+participant ":AddClientPackageCommand" as APC
+participant ":ClientPackageList" as ClientPackageList
+participant ":Ui" as Ui
+
+-> Parser : parse("add -p p001 ARGS")
+activate Parser
+ Parser -> Parser: parseAdd("-p p001 ARGS")
+ activate Parser
+ create APC
+ Parser -> APC : new AddClientPackageCommand
+ activate APC
+ APC --> Parser : AddClientPackageCommand
+ deactivate APC
+ Parser --> Parser: AddClientPackageCommand
+ deactivate Parser
+ <-- Parser: AddClientPackageCommand
+deactivate Parser
+
+-> APC : execute()
+ activate APC
+
+
+ alt#Gold #LightBlue if added package is unique and has unique fields
+
+ APC -> APC: createClientPackage()
+ activate APC
+ APC --> APC: clientPackage: package of extracted Objects from ID
+ deactivate APC
+
+ APC -> ClientPackageList: add(clientPackage)
+ activate ClientPackageList
+ APC <-- ClientPackageList:
+ deactivate ClientPackageList
+ APC -> Ui : showAdd(ClientPackage clientPackage)
+ activate Ui
+ APC <-- Ui : add clientPackage Ui display
+ deactivate Ui
+ <-- APC: add clientPackage Ui display
+
+ else #Pink if added package ID already exists or has duplicated fields
+ <-- APC : ERROR message
+
+ deactivate APC
+ destroy APC
+ end
+@enduml
\ No newline at end of file
diff --git a/src/main/java/seedu/uml/AddTourCommand.puml b/src/main/java/seedu/uml/AddTourCommand.puml
new file mode 100644
index 0000000000..1057621013
--- /dev/null
+++ b/src/main/java/seedu/uml/AddTourCommand.puml
@@ -0,0 +1,44 @@
+@startuml
+
+participant ":Parser" as Parser
+participant ":AddTourCommand" as ATC
+participant ":TourList" as TourList
+participant ":Ui" as Ui
+
+-> Parser : parse("add -t t001 ARGS")
+activate Parser
+ Parser -> Parser: parseAdd("-t t001 ARGS")
+ activate Parser
+ create ATC
+ Parser -> ATC : new AddTourCommand
+ activate ATC
+ ATC --> Parser : AddTourCommand
+ deactivate ATC
+ Parser --> Parser: AddTourCommand
+ deactivate Parser
+ <-- Parser: AddTourCommand
+deactivate Parser
+
+-> ATC : execute()
+ activate ATC
+
+
+ alt#Gold #LightBlue if added tour is unique and has unique fields
+
+ ATC -> TourList: add(Tour tour)
+ activate TourList
+ ATC <-- TourList:
+ deactivate TourList
+ ATC -> Ui : showAdd(Tour tour)
+ activate Ui
+ ATC <-- Ui : add tour Ui display
+ deactivate Ui
+ <-- ATC: add tour Ui display
+
+ else #Pink if added tour ID already exists or has duplicated fields
+ <-- ATC : ERROR message
+
+ deactivate ATC
+ destroy ATC
+ end
+@enduml
\ No newline at end of file
diff --git a/src/main/java/seedu/uml/Architecture.puml b/src/main/java/seedu/uml/Architecture.puml
new file mode 100644
index 0000000000..9f35ed3b90
--- /dev/null
+++ b/src/main/java/seedu/uml/Architecture.puml
@@ -0,0 +1,24 @@
+@startuml
+
+skinparam componentStyle rectangle
+component {
+ [TourPlanner] as TP
+ [Ui] as Ui
+ [Parser] as Parser
+ [Command] as Command
+ [ObjectList] as ObjectList
+ [Storage] as Storage
+}
+database Computer
+actor User
+
+User .> TP
+TP --> Ui
+TP --> Storage
+Storage ..> Computer
+Ui --> Parser
+Ui ..> ObjectList
+Parser -> Command
+Command --> ObjectList
+Command -> Ui
+@enduml
\ No newline at end of file
diff --git a/src/main/java/seedu/uml/CutClientCommand.puml b/src/main/java/seedu/uml/CutClientCommand.puml
new file mode 100644
index 0000000000..baa5d01043
--- /dev/null
+++ b/src/main/java/seedu/uml/CutClientCommand.puml
@@ -0,0 +1,51 @@
+@startuml
+
+participant ":Parser" as Parser
+participant ":CutClientCommand" as CCC
+participant ":ClientList" as ClientList
+participant ":ClientPackageList" as ClientPackageList
+
+-> Parser: parse("cut -c c001")
+activate Parser
+Parser -> Parser : parseCut("-c c001")
+activate Parser
+
+create CCC
+Parser -> CCC
+activate CCC
+CCC --> Parser
+deactivate CCC
+Parser --> Parser
+deactivate Parser
+
+<-- Parser: CutClientCommand
+deactivate Parser
+
+-> CCC: execute()
+activate CCC
+
+CCC -> ClientList : getClientById("c001")
+activate ClientList
+ClientList --> CCC : Client
+deactivate ClientList
+
+CCC -> ClientList : cut(Client)
+activate ClientList
+ClientList --> CCC
+deactivate ClientList
+
+CCC -> ClientPackageList : getClientPackageByClient("c001")
+activate ClientPackageList
+ClientPackageList --> CCC : ClientPackage
+deactivate ClientPackageList
+
+CCC -> ClientPackageList : cut(ClientPackage)
+activate ClientPackageList
+ClientPackageList --> CCC
+deactivate ClientPackageList
+
+<-- CCC
+deactivate CCC
+deactivate Parser
+destroy CCC
+@enduml
\ No newline at end of file
diff --git a/src/main/java/seedu/uml/CutClientPackageCommand.puml b/src/main/java/seedu/uml/CutClientPackageCommand.puml
new file mode 100644
index 0000000000..2dbae24de8
--- /dev/null
+++ b/src/main/java/seedu/uml/CutClientPackageCommand.puml
@@ -0,0 +1,42 @@
+@startuml
+
+participant ":Parser" as Parser
+participant ":CutClientPackageCommand" as CCC
+participant ":ClientPackageList" as ClientPackageList
+
+
+-> Parser: parse("cut -p p001")
+activate Parser
+Parser -> Parser : parseCut("-p p001")
+activate Parser
+
+create CCC
+Parser -> CCC
+activate CCC
+CCC --> Parser
+deactivate CCC
+Parser --> Parser
+deactivate Parser
+
+<-- Parser: CutClientPackageCommand
+deactivate Parser
+
+-> CCC: execute()
+activate CCC
+
+CCC -> ClientPackageList : getClientPackageById("p001")
+activate ClientPackageList
+ClientPackageList --> CCC : ClientPackage
+deactivate ClientPackageList
+
+CCC -> ClientPackageList: cut(ClientPackage)
+activate ClientPackageList
+ClientPackageList --> CCC
+deactivate ClientPackageList
+
+
+<-- CCC
+deactivate CCC
+deactivate Parser
+destroy CCC
+@enduml
\ No newline at end of file
diff --git a/src/main/java/seedu/uml/FindClientTest.puml b/src/main/java/seedu/uml/FindClientTest.puml
new file mode 100644
index 0000000000..203059da00
--- /dev/null
+++ b/src/main/java/seedu/uml/FindClientTest.puml
@@ -0,0 +1,51 @@
+@startuml
+
+participant ":Parser" as Parser
+participant ":FindClientCommand" as FCC
+participant ":Ui" as Ui
+participant ":ClientList" as TourList
+
+-> Parser: execute "find -c Bo Tuan"
+activate Parser
+Parser -> Parser : parseFind("-c Bo Tuan")
+activate Parser
+
+
+
+create FCC
+Parser -> FCC
+activate FCC
+FCC --> Parser
+deactivate FCC
+Parser --> Parser
+deactivate Parser
+<-- Parser: FindClientCommand
+deactivate Parser
+
+-> FCC: execute()
+activate FCC
+
+
+
+FCC -> Ui : showFindClient()
+activate Ui
+Ui -> TourList : getClient("Bo Tuan")
+activate TourList
+TourList --> Ui : id, name, contact no, email
+deactivate TourList
+loop until all clientPackage iterated
+ Ui -> ClientPackageList : getClientPackageByIndex()
+ activate ClientPackageList
+ ClientPackageList --> Ui: client packages of found client(s)
+ deactivate ClientPackageList
+ Ui -> Ui: show ClientPackage
+ activate Ui
+ Ui --> Ui
+ deactivate Ui
+end
+Ui --> FCC
+deactivate Ui
+
+<-- FCC
+deactivate FCC
+@enduml
\ No newline at end of file
diff --git a/src/main/java/seedu/uml/FindFlightTest.puml b/src/main/java/seedu/uml/FindFlightTest.puml
new file mode 100644
index 0000000000..0ebc5a82d1
--- /dev/null
+++ b/src/main/java/seedu/uml/FindFlightTest.puml
@@ -0,0 +1,53 @@
+@startuml
+
+participant ":Parser" as Parser
+participant ":FindFlightCommand" as FCC
+participant ":Ui" as Ui
+participant ":FlightList" as TourList
+
+-> Parser: execute "find -f SQ-JPN"
+activate Parser
+Parser -> Parser : parseFind("-f SQ-JPN")
+activate Parser
+
+
+
+create FCC
+Parser -> FCC
+activate FCC
+FCC --> Parser
+deactivate FCC
+Parser --> Parser
+deactivate Parser
+<-- Parser: FindFlightCommand
+deactivate Parser
+
+-> FCC: execute()
+activate FCC
+
+
+
+FCC -> Ui : showFindFlight()
+activate Ui
+Ui -> TourList : getFlight("SQ-JPN")
+activate TourList
+TourList --> Ui : id, departure / return destination, place
+deactivate TourList
+loop until all clientPackage iterated
+ Ui -> ClientPackageList : getClientPackageByIndex()
+ activate ClientPackageList
+ ClientPackageList --> Ui: passengers
+ deactivate ClientPackageList
+ Ui -> Ui: show Client
+ activate Ui
+ Ui --> Ui
+ deactivate Ui
+end
+Ui --> FCC
+deactivate Ui
+
+<-- FCC
+deactivate FCC
+
+destroy FCC
+@enduml
\ No newline at end of file
diff --git a/src/main/java/seedu/uml/FindTourTest.puml b/src/main/java/seedu/uml/FindTourTest.puml
new file mode 100644
index 0000000000..b5126cc476
--- /dev/null
+++ b/src/main/java/seedu/uml/FindTourTest.puml
@@ -0,0 +1,53 @@
+@startuml
+
+participant ":Parser" as Parser
+participant ":FindTourCommand" as FCC
+participant ":Ui" as Ui
+participant ":TourList" as TourList
+
+-> Parser: execute "find -t JPN"
+activate Parser
+Parser -> Parser : parseFind("-t JPN")
+activate Parser
+
+
+
+create FCC
+Parser -> FCC
+activate FCC
+FCC --> Parser
+deactivate FCC
+Parser --> Parser
+deactivate Parser
+<-- Parser: FindTourCommand
+deactivate Parser
+
+-> FCC: execute()
+activate FCC
+
+
+
+FCC -> Ui : showFindTour()
+activate Ui
+Ui -> TourList : getTour("JPN")
+activate TourList
+TourList --> Ui : id, name, price
+deactivate TourList
+loop until all clientPackage iterated
+ Ui -> ClientPackageList : getClientPackageByIndex()
+ activate ClientPackageList
+ ClientPackageList --> Ui: subscribed clients
+ deactivate ClientPackageList
+ Ui -> Ui: show Client
+ activate Ui
+ Ui --> Ui
+ deactivate Ui
+end
+Ui --> FCC
+deactivate Ui
+
+<-- FCC
+deactivate FCC
+
+destroy FCC
+@enduml
\ No newline at end of file
diff --git a/src/main/java/seedu/uml/ListClientCommand.puml b/src/main/java/seedu/uml/ListClientCommand.puml
new file mode 100644
index 0000000000..d26f40e0b3
--- /dev/null
+++ b/src/main/java/seedu/uml/ListClientCommand.puml
@@ -0,0 +1,46 @@
+@startuml
+
+participant ":Parser" as Parser
+participant ":ListClientCommand" as LCC
+participant ":Ui" as Ui
+participant ":ClientList" as ClientList
+
+-> Parser: parse("list -c")
+activate Parser
+Parser -> Parser : parseList("-c")
+activate Parser
+
+create LCC
+Parser -> LCC
+activate LCC
+LCC --> Parser
+deactivate LCC
+Parser --> Parser
+deactivate Parser
+
+<-- Parser: ListClientCommand
+deactivate Parser
+
+-> LCC: execute()
+activate LCC
+
+LCC -> Ui: showListClient(ClientList)
+activate Ui
+loop until all clients iterated
+ Ui -> ClientList : getClientByIndex(index)
+ activate ClientList
+ ClientList --> Ui: Client
+ deactivate ClientList
+ Ui -> Ui: show Client
+ activate Ui
+ Ui --> Ui
+ deactivate Ui
+end
+Ui --> LCC
+deactivate Ui
+
+<-- LCC
+deactivate LCC
+
+destroy LCC
+@enduml
\ No newline at end of file
diff --git a/src/main/java/seedu/uml/Parser.puml b/src/main/java/seedu/uml/Parser.puml
new file mode 100644
index 0000000000..ee5efbe47d
--- /dev/null
+++ b/src/main/java/seedu/uml/Parser.puml
@@ -0,0 +1,42 @@
+@startuml
+
+participant ":Parser" as Parser
+participant ":XYZCommand" as XYZ
+
+-> Parser: parse(input: String)
+activate Parser
+
+Parser -> Parser : splitCommandString(input: String)
+activate Parser
+Parser --> Parser : Command and Params
+deactivate Parser
+
+alt Command is "xyz", Params are valid
+
+Parser -> Parser : parseXYZ(params: String)
+note left
+"XYZ" refers to general
+operations such as
+add, list and sort.
+end note
+activate Parser
+
+create XYZ
+Parser -> XYZ : new XYZCommand()
+activate XYZ
+XYZ --> Parser : XYZCommand
+deactivate XYZ
+
+Parser --> Parser : XYZCommand
+deactivate Parser
+
+<-- Parser : XYZCommand
+
+
+else Command and/or Params are invalid
+ <-- Parser : TourPlannerException
+
+deactivate Parser
+
+end
+@enduml
\ No newline at end of file
diff --git a/src/main/java/seedu/uml/SortClientCommand.puml b/src/main/java/seedu/uml/SortClientCommand.puml
new file mode 100644
index 0000000000..2af18f25bf
--- /dev/null
+++ b/src/main/java/seedu/uml/SortClientCommand.puml
@@ -0,0 +1,52 @@
+@startuml
+
+participant ":Parser" as Parser
+participant ":SortClientCommand" as SCC
+participant ":ClientList" as ClientList
+participant ":Ui" as Ui
+
+-> Parser: parse("sort -c /id")
+activate Parser
+Parser -> Parser : parseSort("-c /id")
+activate Parser
+
+create SCC
+Parser -> SCC
+activate SCC
+SCC --> Parser
+deactivate SCC
+Parser --> Parser
+deactivate Parser
+
+<-- Parser: SortClientCommand
+deactivate Parser
+
+-> SCC: execute()
+activate SCC
+
+SCC -> ClientList : getSortedClientIds()
+activate ClientList
+ClientList --> SCC : sorted client IDs
+deactivate ClientList
+
+
+SCC -> Ui : showSortedClientById()
+activate Ui
+loop until all clientIds iterated
+ Ui -> ClientList : getClientById(clientId)
+ activate ClientList
+ ClientList --> Ui: Client
+ deactivate ClientList
+ Ui -> Ui: show Client
+ activate Ui
+ Ui --> Ui
+ deactivate Ui
+end
+Ui --> SCC
+deactivate Ui
+
+<-- SCC
+deactivate SCC
+
+destroy SCC
+@enduml
\ No newline at end of file
diff --git a/src/main/java/seedu/uml/Ui.puml b/src/main/java/seedu/uml/Ui.puml
new file mode 100644
index 0000000000..bb18054818
--- /dev/null
+++ b/src/main/java/seedu/uml/Ui.puml
@@ -0,0 +1,18 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+skinparam componentStyle rectangle
+component {
+ [Ui] as Ui
+ [Parser] as Parser
+ [Command] as Command
+
+}
+
+actor User
+
+User - Ui
+Ui -- Parser : read by >
+Parser .> "0..1" Command : returns >
+Command .> Ui : calls appropriate function >
+@enduml
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/ParserTest.java b/src/test/java/seedu/duke/ParserTest.java
new file mode 100644
index 0000000000..834a52a3fe
--- /dev/null
+++ b/src/test/java/seedu/duke/ParserTest.java
@@ -0,0 +1,171 @@
+package seedu.duke;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.commands.clientpackages.ListClientPackageCommand;
+import seedu.duke.commands.clients.AddClientCommand;
+import seedu.duke.commands.Command;
+import seedu.duke.commands.clients.ListClientCommand;
+import seedu.duke.commands.clients.SortClientCommand;
+import seedu.duke.commands.flights.AddFlightCommand;
+import seedu.duke.commands.flights.ListFlightCommand;
+import seedu.duke.commands.flights.SortFlightCommand;
+import seedu.duke.commands.tours.AddTourCommand;
+import seedu.duke.commands.tours.ListTourCommand;
+import seedu.duke.commands.tours.SortTourCommand;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ParserTest {
+
+ private static final String CORRECT_ADDCLIENT_ONE = "add -c j12 /n john /cn 91234567 /m johndoe@gmail.com";
+ private static final String CORRECT_ADDCLIENT_TWO = " add -c j12 /n john /cn 91234567 /m johndoe@gmail.com";
+ private static final String CORRECT_ADDCLIENT_THREE = "add -c j12 /n john /m johndoe@gmail.com /cn 91234567";
+ private static final String CORRECT_ADDFLIGHT_ONE =
+ "add -f SQ-JPN1 /d JPN /r SG /dd 23/10/21 13:00 /rd 27/10/21 02:00";
+ private static final String CORRECT_ADDFLIGHT_TWO =
+ "add -f SQ-JPN1 /d JPN /r SG /dd 23/10/21 13:00 /rd 27/10/21 02:00";
+ private static final String CORRECT_ADDFLIGHT_THREE =
+ "add -f SQ-JPN1 /d JPN /dd 23/10/21 13:00 /r SG /rd 27/10/21 02:00";
+ private static final String CORRECT_ADDTOUR_ONE = "add -t aus1369 /n australiaromance /p 1300";
+ private static final String CORRECT_ADDTOUR_TWO = "add -t aus1369 /n australiaromance /p 1300";
+ private static final String CORRECT_ADDTOUR_THREE = "add -t aus1369 /p 1300 /n australiaromance";
+ private static final String WRONG_ADDCLIENT_MISSING_PREFIX = "add -c j12 /n john /m johndoe@gmail.com";
+ private static final String WRONG_ADDTOUR_MISSING_PREFIX = "add -t aus1369 /p 1300";
+ private static final String WRONG_ADDFLIGHT_MISSING_PREFIX = "add -f SQ-JPN1 /d JPN /r SG /dd 23/10/21 13:00";
+ private static final String WRONG_ADDCLIENT_MISSING_ID = "add -c /n botuan /cn 91234567 /m johndoe@gmail.com";
+ private static final String WRONG_ADDFLIGHT_MISSING_ID =
+ "add -f /d JPN /r SG /dd 23/10/21 13:00 /rd 27/10/21 02:00";
+ private static final String WRONG_ADDTOUR_MISSING_ID = "add -t /n australiaromance /p 1300";
+ private static final String WRONG_ADDCLIENT_DUPLICATE_PREFIX =
+ "add -c c001 /n Bo Tuan /cn 91234567 /m botuan@gmail.com /cn 13465789";
+ private static final String WRONG_ADDTOUR_DUPLICATE_PREFIX = "add -t aus1369 /n australiaromance /p 1300 /p 2400";
+ private static final String WRONG_ADDFLIGHT_DUPLICATE_PREFIX =
+ "add -f SQ-JPN1 /d JPN /d JAPAN /r SG /dd 23/10/21 13:00 /rd 27/10/21 02:00";
+ private static final String WRONG_ADD_MISSING_IDENTIFIER = "add -o c001 /n botuan /cn 91234567 /m john@mail.com";
+ private static final String WRONG_SORT_MISSING_IDENTIFIER = "sort /d";
+ private static final String CORRECT_SORT_TOUR = "sort -t /p";
+ private static final String CORRECT_SORT_CLIENT = "sort -c /n";
+ private static final String CORRECT_SORT_FLIGHT = "sort -f /d";
+ public static final String CORRECT_LIST_FLIGHT = "list -f";
+ public static final String CORRECT_LIST_CLIENT = "list -c";
+ public static final String CORRECT_LIST_TOUR = "list -t";
+ public static final String CORRECT_LIST_PACKAGE = "list -p";
+ private static final String WRONG_ADD_FLIGHT_DATETIME_FORMAT =
+ "add -f SQ-JPN1 /d JPN /r SG /dd 23/10/21 25:00 /rd 27/10/21 02:00";
+ private static final String WRONG_ADD_FLIGHT_LOGIC_ERROR =
+ "add -f SQ-JPN1 /d JPN /r SG /dd 23/11/21 21:00 /rd 19/10/21 02:00";
+ public static final String WRONG_MISSING_IDENTIFIER_ONE = "list /f";
+ public static final String WRONG_IDENTIFIER_GIVEN = "sort -o /n";
+ public static final String WRONG_MISSING_IDENTIFIER_TWO = "cut c001";
+
+
+ @Test
+ void parse_addClientCommand_correctCommandCreated() throws TourPlannerException {
+ parseAndAssertCommandType(CORRECT_ADDCLIENT_ONE, AddClientCommand.class);
+
+ //whitespaces are allowed
+ parseAndAssertCommandType(CORRECT_ADDCLIENT_TWO, AddClientCommand.class);
+
+ //order of prefixes does not matter
+ parseAndAssertCommandType(CORRECT_ADDCLIENT_THREE, AddClientCommand.class);
+ }
+
+ @Test
+ void parse_addFlightCommand_correctFlightCreated() throws TourPlannerException {
+ parseAndAssertCommandType(CORRECT_ADDFLIGHT_ONE, AddFlightCommand.class);
+
+ //whitespaces are allowed
+ parseAndAssertCommandType(CORRECT_ADDFLIGHT_TWO, AddFlightCommand.class);
+
+ //order of prefixes does not matter
+ parseAndAssertCommandType(CORRECT_ADDFLIGHT_THREE, AddFlightCommand.class);
+ }
+
+ @Test
+ void parse_addTourCommand_correctTourCreated() throws TourPlannerException {
+ parseAndAssertCommandType(CORRECT_ADDTOUR_ONE, AddTourCommand.class);
+
+ //whitespaces are allowed
+ parseAndAssertCommandType(CORRECT_ADDTOUR_TWO, AddTourCommand.class);
+
+ //order of prefixes does not matter
+ parseAndAssertCommandType(CORRECT_ADDTOUR_THREE, AddTourCommand.class);
+ }
+
+ @Test
+ void parse_sortCommand_correctCommand() throws TourPlannerException {
+ parseAndAssertCommandType(CORRECT_SORT_TOUR, SortTourCommand.class);
+ parseAndAssertCommandType(CORRECT_SORT_CLIENT, SortClientCommand.class);
+ parseAndAssertCommandType(CORRECT_SORT_FLIGHT, SortFlightCommand.class);
+ }
+
+ @Test
+ void parse_listCommand_correctCommand() throws TourPlannerException {
+ parseAndAssertCommandType(CORRECT_LIST_FLIGHT, ListFlightCommand.class);
+ parseAndAssertCommandType(CORRECT_LIST_CLIENT, ListClientCommand.class);
+ parseAndAssertCommandType(CORRECT_LIST_TOUR, ListTourCommand.class);
+ parseAndAssertCommandType(CORRECT_LIST_PACKAGE, ListClientPackageCommand.class);
+ }
+
+ @Test
+ void parse_missingIdentifier_failure() {
+ assertParseFailure(WRONG_MISSING_IDENTIFIER_ONE, Parser.ERROR_MISSING_IDENTIFIER);
+ assertParseFailure(WRONG_IDENTIFIER_GIVEN, Parser.ERROR_MISSING_IDENTIFIER);
+ assertParseFailure(WRONG_MISSING_IDENTIFIER_TWO, Parser.ERROR_MISSING_IDENTIFIER);
+ }
+
+ @Test
+ void parse_missingPrefixes_failure() {
+ //missing contact number
+ assertParseFailure(WRONG_ADDCLIENT_MISSING_PREFIX, Parser.ERROR_MISSING_PREFIXES);
+
+ //missing tour name
+ assertParseFailure(WRONG_ADDTOUR_MISSING_PREFIX, Parser.ERROR_MISSING_PREFIXES);
+
+ //missing return flight date
+ assertParseFailure(WRONG_ADDFLIGHT_MISSING_PREFIX, Parser.ERROR_MISSING_PREFIXES);
+ }
+
+ @Test
+ void parse_missingIdOrName_failure() {
+ assertParseFailure(WRONG_ADDCLIENT_MISSING_ID, Parser.ERROR_MISSING_FIELDS);
+ assertParseFailure(WRONG_ADDFLIGHT_MISSING_ID, Parser.ERROR_MISSING_FIELDS);
+ assertParseFailure(WRONG_ADDTOUR_MISSING_ID, Parser.ERROR_MISSING_FIELDS);
+ }
+
+ @Test
+ void parse_duplicatePrefixes_failure() {
+ assertParseFailure(WRONG_ADDCLIENT_DUPLICATE_PREFIX, Parser.ERROR_DUPLICATE_PREFIXES);
+ assertParseFailure(WRONG_ADDTOUR_DUPLICATE_PREFIX, Parser.ERROR_DUPLICATE_PREFIXES);
+ assertParseFailure(WRONG_ADDFLIGHT_DUPLICATE_PREFIX, Parser.ERROR_DUPLICATE_PREFIXES);
+ }
+
+ @Test
+ void parse_wrongOrMissingIdentifier_failure() {
+ assertParseFailure(WRONG_ADD_MISSING_IDENTIFIER, Parser.ERROR_MISSING_IDENTIFIER);
+ assertParseFailure(WRONG_SORT_MISSING_IDENTIFIER, Parser.ERROR_MISSING_IDENTIFIER);
+ }
+
+ @Test
+ void parse_wrongDateTimeEntry_failure() {
+ assertParseFailure(WRONG_ADD_FLIGHT_DATETIME_FORMAT, Parser.ERROR_FLIGHT_TIME_FORMAT);
+ assertParseFailure(WRONG_ADD_FLIGHT_LOGIC_ERROR, Parser.ERROR_FLIGHT_TIME_INVERT);
+ }
+
+
+ private void parseAndAssertCommandType(String input, Class expectedCommandClass)
+ throws TourPlannerException {
+ final Command result = Parser.parse(input);
+ assertTrue(result.getClass().isAssignableFrom(expectedCommandClass));
+ }
+
+ private void assertParseFailure(String input, String expectedMessage) {
+ try {
+ Parser.parse(input);
+ throw new AssertionError("The expected exception was not thrown");
+ } catch (TourPlannerException e) {
+ assertEquals(expectedMessage, e.getMessage());
+ }
+ }
+}
diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/TourPlannerTest.java
similarity index 88%
rename from src/test/java/seedu/duke/DukeTest.java
rename to src/test/java/seedu/duke/TourPlannerTest.java
index 2dda5fd651..89663895e5 100644
--- a/src/test/java/seedu/duke/DukeTest.java
+++ b/src/test/java/seedu/duke/TourPlannerTest.java
@@ -4,9 +4,10 @@
import org.junit.jupiter.api.Test;
-class DukeTest {
+class TourPlannerTest {
@Test
public void sampleTest() {
assertTrue(true);
}
+
}
diff --git a/src/test/java/seedu/duke/commands/ByeCommandTest.java b/src/test/java/seedu/duke/commands/ByeCommandTest.java
new file mode 100644
index 0000000000..78dd2d14b6
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/ByeCommandTest.java
@@ -0,0 +1,43 @@
+package seedu.duke.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.TourList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ByeCommandTest {
+ PrintStream previousConsole;
+ ByteArrayOutputStream newConsole;
+
+ private ClientList clients;
+ private FlightList flights;
+ private TourList tours;
+ private ClientPackageList clientPackages;
+ private Ui ui = new Ui();
+
+ @Test
+ public void byeCommand_validUserInput_showMessage() throws TourPlannerException {
+ previousConsole = System.out;
+ newConsole = new ByteArrayOutputStream();
+
+ System.setOut(new PrintStream(newConsole));
+ Command byeCommand = new ByeCommand();
+ byeCommand.setData(clients, flights, tours, clientPackages, ui);
+ byeCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String expectedString = "Thanks for using TourPlanner. Goodbye!\n"
+ + "____________________________________________________________";
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+}
diff --git a/src/test/java/seedu/duke/commands/HelpCommandTest.java b/src/test/java/seedu/duke/commands/HelpCommandTest.java
new file mode 100644
index 0000000000..0679f14bce
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/HelpCommandTest.java
@@ -0,0 +1,76 @@
+package seedu.duke.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.TourList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class HelpCommandTest {
+ PrintStream previousConsole;
+ ByteArrayOutputStream newConsole;
+
+ private ClientList clients;
+ private FlightList flights;
+ private TourList tours;
+ private ClientPackageList clientPackages;
+ private Ui ui = new Ui();
+
+ @Test
+ public void helpCommand_validUserInput_showMessage() throws TourPlannerException {
+ previousConsole = System.out;
+ newConsole = new ByteArrayOutputStream();
+
+ System.setOut(new PrintStream(newConsole));
+ Command helpCommand = new HelpCommand();
+ helpCommand.setData(clients, flights, tours, clientPackages, ui);
+ helpCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String expectedString = "add: Add information of all data types into the database.\n"
+ + "Prefixes can be input in any order.\n"
+ + " Add client: add -c CLIENT_ID /n NAME /cn CONTACT_NUM /m EMAIL\n"
+ + " Add flight: add -f FLIGHT_ID /d DEPART_DESTINATION /r RETURN_DESTINATION\n"
+ + " /dd DEPARTURE_DATETIME /rd RETURN_DATETIME\n"
+ + " Add tour: add -t TOUR_ID /n DEPART_DESTINATION /p TOUR_PRICE\n"
+ + " Add client package: add -p PACKAGE_ID /c CLIENT_ID /t TOUR_ID /f FLIGHT_ID\n\n"
+ + "list: Shows a list of all available entries of a specific data type, along with their "
+ + "respective fields.\n"
+ + " List client: list -c\n"
+ + " List flight: list -f\n"
+ + " List tour: list -t\n"
+ + " List client package: list -p\n\n"
+ + "cut: Deletes entry of a certain data type and all client packages corresponding to the entry.\n"
+ + " Cut client: cut -c CLIENT_ID\n"
+ + " Cut flight: cut -f FLIGHT_ID\n"
+ + " Cut tour: cut -t TOUR_ID\n"
+ + " Cut client package: cut -p PACKAGE_ID\n\n"
+ + "find: Finds specific entry of data type, returns the entry and other relevant information.\n"
+ + " Find client: find -c CLIENT_NAME\n"
+ + " Find flight: find -f FLIGHT_ID\n"
+ + " Find tour: find -t TOUR_ID\n\n"
+ + "sort: Sorts entries in the data type based on the criteria.\n "
+ + " Sort client:\n"
+ + " Sort by id: sort -c /id\n"
+ + " Sort by name: sort -c /n\n"
+ + " Sort flight:\n"
+ + " Sort by id: sort -f /id\n"
+ + " Sort by departure date: sort -f /d\n"
+ + " Sort by return date: sort -f /r\n"
+ + " Sort tour:\n"
+ + " Sort by id: sort -t /id\n"
+ + " Sort by name: sort -t /n\n"
+ + " Sort by price: sort -t /p\n\n"
+ + "bye: Exits the program.";
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+}
diff --git a/src/test/java/seedu/duke/commands/clientpackages/AddClientPackageCommandTest.java b/src/test/java/seedu/duke/commands/clientpackages/AddClientPackageCommandTest.java
new file mode 100644
index 0000000000..dea3232b48
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/clientpackages/AddClientPackageCommandTest.java
@@ -0,0 +1,123 @@
+package seedu.duke.commands.clientpackages;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackage;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.Flight;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.Tour;
+import seedu.duke.data.TourList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AddClientPackageCommandTest {
+ PrintStream previousConsole;
+ ByteArrayOutputStream newConsole;
+
+ private ClientList clients;
+ private FlightList flights;
+ private TourList tours;
+ private ClientPackageList clientPackages;
+ private Ui ui = new Ui();
+
+ private Client botuan;
+ private Tour kor;
+ private Flight sqkor;
+ private ClientPackageList populatedClientPackages;
+
+ @BeforeEach
+ public void setUp() {
+ previousConsole = System.out;
+ newConsole = new ByteArrayOutputStream();
+
+ botuan = new Client(new String[]{"c001", "Bo Tuan", "93338333", "bt@mail.com"});
+ Client wayne = new Client(new String[]{"c002", "Wayne", "56667888", "wen@mail.com"});
+ clients = createClientList(botuan, wayne);
+
+ Tour jpn = new Tour(new String[]{"JPN", "Japan Basic Tour", "1500.00"});
+ kor = new Tour(new String[]{"KOR", "Korea Cultural Tour", "3000.00"});
+ tours = createTourList(jpn, kor);
+
+ Flight sqjpn = new Flight(new String[]{"SQ-JPN", "JPN", "SG", "20/10/21 18:00", "21/10/21 03:00"});
+ sqkor = new Flight(new String[]{"SQ-KOR", "KOR", "SG", "23/10/2021 18:00", "30/10/2021 03:00"});
+ flights = createFlightList(sqjpn, sqkor);
+
+ ClientPackage botuanPack = new ClientPackage("p001", botuan, kor, sqkor);
+ populatedClientPackages = createClientPackageList(botuanPack);
+
+ clientPackages = createClientPackageList();
+ }
+
+ @Test
+ void addClientPackageCommand_validData_correctlyConstructed() throws TourPlannerException {
+ String[] rawClientPackage = new String[] {"p001", "c001", "KOR", "SQ-KOR"};
+ Command addClientPackage = new AddClientPackageCommand(rawClientPackage);
+ addClientPackage.setData(clients, flights, tours, clientPackages, ui);
+ addClientPackage.execute();
+
+ ClientPackage retrieveClientPackage = ((AddClientPackageCommand) addClientPackage).getClientPackage();
+ assertEquals("p001", retrieveClientPackage.getId());
+ assertEquals(botuan, retrieveClientPackage.getClient());
+ assertEquals(kor, retrieveClientPackage.getTour());
+ assertEquals(sqkor, retrieveClientPackage.getFlight());
+ }
+
+ @Test
+ void addClientPackageCommand_emptyClientPackageList_populatedClientPackageList() throws TourPlannerException {
+ String[] rawClientPackage = new String[] {"p001", "c001", "KOR", "SQ-KOR"};
+ Command addClientPackage = new AddClientPackageCommand(rawClientPackage);
+ addClientPackage.setData(clients, flights, tours, clientPackages, ui);
+ addClientPackage.execute();
+
+ assertEquals(populatedClientPackages.getClientPackageCount(), clientPackages.getClientPackageCount());
+ for (int i = 0; i < populatedClientPackages.getClientPackageCount(); i++) {
+ ClientPackage expected = populatedClientPackages.getClientPackageByIndex(i);
+ ClientPackage actual = clientPackages.getClientPackageByIndex(i);
+ assertEquals(expected.getId(), actual.getId());
+ assertEquals(expected.getClient(), actual.getClient());
+ assertEquals(expected.getTour(), actual.getTour());
+ assertEquals(expected.getFlight(), actual.getFlight());
+ }
+ }
+
+ private ClientList createClientList(Client...clientList) {
+ ClientList newClientList = new ClientList();
+ for (Client client : clientList) {
+ newClientList.add(client);
+ }
+ return newClientList;
+ }
+
+ private FlightList createFlightList(Flight...flightList) {
+ FlightList newFlightList = new FlightList();
+ for (Flight flight : flightList) {
+ newFlightList.add(flight);
+ }
+ return newFlightList;
+ }
+
+ private TourList createTourList(Tour...tourList) {
+ TourList newTourList = new TourList();
+ for (Tour tour : tourList) {
+ newTourList.add(tour);
+ }
+ return newTourList;
+ }
+
+ private ClientPackageList createClientPackageList(ClientPackage...clientPackageList) {
+ ClientPackageList newClientPackageList = new ClientPackageList();
+ for (ClientPackage clientPackage: clientPackageList) {
+ newClientPackageList.add(clientPackage);
+ }
+ return newClientPackageList;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/commands/clientpackages/CutClientPackageCommandTest.java b/src/test/java/seedu/duke/commands/clientpackages/CutClientPackageCommandTest.java
new file mode 100644
index 0000000000..9086e0c0e4
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/clientpackages/CutClientPackageCommandTest.java
@@ -0,0 +1,113 @@
+package seedu.duke.commands.clientpackages;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackage;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.Flight;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.Tour;
+import seedu.duke.data.TourList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CutClientPackageCommandTest {
+ public static final String[] CLIENT_BOTUAN = {"c001", "Bo Tuan", "93338333", "bt@mail.com"};
+ public static final String[] CLIENT_WAYNE = {"c002", "Wayne", "56667888", "wen@mail.com"};
+
+ public static final String[] TOUR_JPN = {"JPN", "Japan Basic Tour", "1500.00"};
+ public static final String[] TOUR_KOR = {"KOR", "Korea Cultural Tour", "3000.00"};
+
+ public static final String[] FLIGHT_SQJPN = {"SQ-JPN", "JPN", "SG", "20/10/21 18:00", "21/10/21 03:00"};
+ public static final String[] FLIGHT_SQKOR = {"SQ-KOR", "KOR", "SG", "23/10/2021 18:00", "30/10/2021 03:00"};
+
+ public static final String PACKAGE_ID_1 = "p001";
+ public static final String PACKAGE_ID_2 = "p002";
+ public static final String PACKAGE_ID_3 = "p003";
+ public static final String PACKAGE_ID_WRONG = "1234po0o";
+
+ PrintStream previousConsole;
+ ByteArrayOutputStream newConsole;
+
+ private ClientList clients;
+ private FlightList flights;
+ private TourList tours;
+ private ClientPackageList clientPackages;
+ private Ui ui = new Ui();
+
+ private ClientPackageList allClientPackages;
+ private ClientPackageList clientPackagesWithoutP002;
+
+ @BeforeEach
+ public void setUp() {
+ previousConsole = System.out;
+ newConsole = new ByteArrayOutputStream();
+
+ Client botuan = new Client(CLIENT_BOTUAN);
+ Client wayne = new Client(CLIENT_WAYNE);
+
+ Tour jpn = new Tour(TOUR_JPN);
+ Tour kor = new Tour(TOUR_KOR);
+
+ Flight sqjpn = new Flight(FLIGHT_SQJPN);
+ Flight sqkor = new Flight(FLIGHT_SQKOR);
+
+ ClientPackage botuanPack1 = new ClientPackage(PACKAGE_ID_1, botuan, jpn, sqjpn);
+ ClientPackage botuanPack2 = new ClientPackage(PACKAGE_ID_2, botuan, kor, sqkor);
+ ClientPackage waynePack = new ClientPackage(PACKAGE_ID_3, wayne, jpn, sqkor);
+ clientPackages = createClientPackageList(botuanPack1, botuanPack2, waynePack);
+
+ allClientPackages = createClientPackageList(botuanPack1, botuanPack2, waynePack);
+ clientPackagesWithoutP002 = createClientPackageList(botuanPack1, waynePack);
+ }
+
+ @Test
+ public void cutClientPackageCommand_validClientPackageId_clientPackageDeleted() throws TourPlannerException {
+ assertEquals(clientPackages.getClientPackageCount(), 3);
+ Command cutClientPackageCommand = new CutClientPackageCommand(PACKAGE_ID_2);
+ cutClientPackageCommand.setData(clients, flights, tours, clientPackages, ui);
+ cutClientPackageCommand.execute();
+ assertEquals(clientPackages.getClientPackageCount(), clientPackagesWithoutP002.getClientPackageCount());
+ for (int i = 0; i < clientPackagesWithoutP002.getClientPackageCount(); i++) {
+ assertEquals(clientPackages.getClientPackageByIndex(i),
+ clientPackagesWithoutP002.getClientPackageByIndex(i));
+ }
+ }
+
+ @Test
+ public void cutClientPackageCommand_invalidClientPackageId_clientPackageNotFoundMessage()
+ throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ Command cutClientPackageCommand = new CutClientPackageCommand(PACKAGE_ID_WRONG);
+ cutClientPackageCommand.setData(clients, flights, tours, clientPackages, ui);
+ cutClientPackageCommand.execute();
+
+ for (int i = 0; i < allClientPackages.getClientPackageCount(); i++) {
+ assertEquals(clientPackages.getClientPackageByIndex(i),
+ allClientPackages.getClientPackageByIndex(i));
+ }
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String expectedString = ClientPackageList.CLIENTPACKAGE_NOT_FOUND_MESSAGE;
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+
+ private ClientPackageList createClientPackageList(ClientPackage...clientPackageList) {
+ ClientPackageList newClientPackageList = new ClientPackageList();
+ for (ClientPackage clientPackage: clientPackageList) {
+ newClientPackageList.add(clientPackage);
+ }
+ return newClientPackageList;
+ }
+}
diff --git a/src/test/java/seedu/duke/commands/clientpackages/ListClientPackageCommandTest.java b/src/test/java/seedu/duke/commands/clientpackages/ListClientPackageCommandTest.java
new file mode 100644
index 0000000000..e9ebc1430a
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/clientpackages/ListClientPackageCommandTest.java
@@ -0,0 +1,60 @@
+package seedu.duke.commands.clientpackages;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.Client;
+import seedu.duke.data.Tour;
+import seedu.duke.data.Flight;
+import seedu.duke.data.ClientPackage;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.TourList;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.ClientPackageList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ListClientPackageCommandTest {
+
+ private static Client TEST_CLIENT = new Client(new String[]{"c001", "Bo Tuan", "93338333", "bt@mail.com"});
+ private static Tour TEST_TOUR = new Tour(new String[]{"JPN", "Japan Basic Tour", "1500.00"});
+ private static Flight TEST_FLIGHT = new Flight(new String[]{"SQ-JPN", "JPN", "SG", "20/10/2021 18:00",
+ "21/10/2021 03:00"});
+ private static ClientPackage TEST_CLIENTPACKAGE = new ClientPackage("p001",
+ TEST_CLIENT, TEST_TOUR, TEST_FLIGHT);
+ private static final String VALID_DATA_OUTPUT = "Here is a list of all packages:\n"
+ + "1. " + TEST_CLIENTPACKAGE + "\n\n\n"
+ + "Total Packages: 1";
+
+ PrintStream previousConsole = System.out;
+ ByteArrayOutputStream newConsole = new ByteArrayOutputStream();
+
+ ClientList dummyClientList = new ClientList();
+ TourList dummyTourList = new TourList();
+ FlightList dummyFlightList = new FlightList();
+ ClientPackageList testPackageList = new ClientPackageList();
+ Ui testUi = new Ui();
+
+ @Test
+ void listClientCommand_validData_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ dummyClientList.add(TEST_CLIENT);
+ dummyTourList.add(TEST_TOUR);
+ dummyFlightList.add(TEST_FLIGHT);
+ testPackageList.add(TEST_CLIENTPACKAGE);
+
+ Command listPackage = new ListClientPackageCommand();
+ listPackage.setData(dummyClientList, dummyFlightList, dummyTourList, testPackageList, testUi);
+ listPackage.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(VALID_DATA_OUTPUT, actualString);
+ }
+}
diff --git a/src/test/java/seedu/duke/commands/clients/AddClientCommandTest.java b/src/test/java/seedu/duke/commands/clients/AddClientCommandTest.java
new file mode 100644
index 0000000000..54299c92a8
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/clients/AddClientCommandTest.java
@@ -0,0 +1,62 @@
+package seedu.duke.commands.clients;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.TourList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AddClientCommandTest {
+
+ public static final String TEST_CLIENT_ID = "c001";
+ public static final String TEST_CLIENT_NAME = "Bo Tuan";
+ public static final String TEST_CLIENT_CONTACT = "91234567";
+ public static final String TEST_CLIENT_EMAIL = "bobotea@gmail.com";
+
+ ClientList testClientList = new ClientList();
+ FlightList dummyFlightList = new FlightList();
+ TourList dummyTourList = new TourList();
+ ClientPackageList dummyPackageList = new ClientPackageList();
+ Ui testUi = new Ui();
+
+ @Test
+ void addClientCommand_validData_correctlyConstructed() {
+ Client testClient =
+ new Client(new String[]{
+ TEST_CLIENT_ID,
+ TEST_CLIENT_NAME,
+ TEST_CLIENT_CONTACT,
+ TEST_CLIENT_EMAIL});
+ AddClientCommand addClient = new AddClientCommand(testClient);
+
+ Client retrieveClient = addClient.getClient();
+ assertEquals(TEST_CLIENT_ID, retrieveClient.getId());
+ assertEquals(TEST_CLIENT_NAME, retrieveClient.getName());
+ assertEquals(TEST_CLIENT_CONTACT, retrieveClient.getContactNum());
+ assertEquals(TEST_CLIENT_EMAIL, retrieveClient.getEmail());
+ }
+
+ @Test
+ void addClientCommand_emptyClientList_populatedClientList() throws TourPlannerException {
+ Client testClient =
+ new Client(new String[]{
+ TEST_CLIENT_ID,
+ TEST_CLIENT_NAME,
+ TEST_CLIENT_CONTACT,
+ TEST_CLIENT_EMAIL});
+ AddClientCommand addClient = new AddClientCommand(testClient);
+ addClient.setData(testClientList, dummyFlightList, dummyTourList, dummyPackageList, testUi);
+ addClient.execute();
+
+ Client retrieveClient = testClientList.getClientById(TEST_CLIENT_ID);
+ assertEquals(TEST_CLIENT_ID, retrieveClient.getId());
+ assertEquals(TEST_CLIENT_NAME, retrieveClient.getName());
+ assertEquals(TEST_CLIENT_CONTACT, retrieveClient.getContactNum());
+ assertEquals(TEST_CLIENT_EMAIL, retrieveClient.getEmail());
+ }
+}
diff --git a/src/test/java/seedu/duke/commands/clients/CutClientCommandTest.java b/src/test/java/seedu/duke/commands/clients/CutClientCommandTest.java
new file mode 100644
index 0000000000..030b29f0ac
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/clients/CutClientCommandTest.java
@@ -0,0 +1,125 @@
+package seedu.duke.commands.clients;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackage;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.Flight;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.Tour;
+import seedu.duke.data.TourList;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CutClientCommandTest {
+ public static final String CLIENT_ID_1 = "c001";
+
+ public static final String[] CLIENT_BOTUAN = {CLIENT_ID_1, "Bo Tuan", "93338333", "bt@mail.com"};
+ public static final String[] CLIENT_WAYNE = {"c002", "Wayne", "56667888", "wen@mail.com"};
+ public static final String[] CLIENT_CHENGXU = {"c003", "ChengXu", "10101010", "demonshaha@mail.com"};
+
+ public static final String[] TOUR_JPN = {"JPN", "Japan Basic Tour", "1500.00"};
+ public static final String[] TOUR_KOR = {"KOR", "Korea Cultural Tour", "3000.00"};
+
+ public static final String[] FLIGHT_SQJPN = {"SQ-JPN", "JPN", "SG", "20/10/21 18:00", "21/10/21 03:00"};
+ public static final String[] FLIGHT_SQKOR = {"SQ-KOR", "KOR", "SG", "23/10/2021 18:00", "30/10/2021 03:00"};
+
+ public static final String PACKAGE_ID_1 = "p001";
+ public static final String PACKAGE_ID_2 = "p002";
+ public static final String CLIENT_ID_WRONG = "1234po0o";
+
+ PrintStream previousConsole;
+ ByteArrayOutputStream newConsole;
+
+ private ClientList clients;
+ private FlightList flights;
+ private TourList tours;
+ private ClientPackageList clientPackages;
+ private Ui ui = new Ui();
+
+ private ClientList allClients;
+ private ClientList clientsWithoutBotuan;
+
+ @BeforeEach
+ public void setUp() {
+ previousConsole = System.out;
+ newConsole = new ByteArrayOutputStream();
+
+ Client botuan = new Client(CLIENT_BOTUAN);
+ Client wayne = new Client(CLIENT_WAYNE);
+ Client chengxu = new Client(CLIENT_CHENGXU);
+ clients = createClientList(botuan, wayne, chengxu);
+ allClients = createClientList(botuan, wayne, chengxu);
+ clientsWithoutBotuan = createClientList(wayne, chengxu);
+
+ Tour jpn = new Tour(TOUR_JPN);
+ Tour kor = new Tour(TOUR_KOR);
+
+ Flight sqjpn = new Flight(FLIGHT_SQJPN);
+ Flight sqkor = new Flight(FLIGHT_SQKOR);
+
+ ClientPackage botuanPack1 = new ClientPackage(PACKAGE_ID_1, botuan, jpn, sqjpn);
+ ClientPackage botuanPack2 = new ClientPackage(PACKAGE_ID_2, botuan, kor, sqkor);
+ clientPackages = createClientPackageList(botuanPack1, botuanPack2);
+ }
+
+ @Test
+ public void cutClientCommand_validClientId_clientDeleted() throws TourPlannerException {
+ assertEquals(clients.getClientCount(), 3);
+ Command cutClientCommand = new CutClientCommand(CLIENT_ID_1);
+ cutClientCommand.setData(clients, flights, tours, clientPackages, ui);
+ cutClientCommand.execute();
+ assertEquals(clients.getClientCount(), clientsWithoutBotuan.getClientCount());
+ for (int i = 0; i < clientsWithoutBotuan.getClientCount(); i++) {
+ assertEquals(clients.getClientByIndex(i), clientsWithoutBotuan.getClientByIndex(i));
+ }
+ }
+
+ @Test
+ public void cutClientCommand_validClientId_clientPackagesDeleted() throws TourPlannerException {
+ Command cutClientCommand = new CutClientCommand(CLIENT_ID_1);
+ cutClientCommand.setData(clients, flights, tours, clientPackages, ui);
+ cutClientCommand.execute();
+ assertEquals(clientPackages.getClientPackageCount(), 0);
+ }
+
+ @Test
+ public void cutClientCommand_invalidClientId_clientNotFoundMessage() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ Command cutClientCommand = new CutClientCommand(CLIENT_ID_WRONG);
+ cutClientCommand.setData(clients, flights, tours, clientPackages, ui);
+ cutClientCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String expectedString = ClientList.CLIENT_NOT_FOUND_MESSAGE;
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+
+ private ClientList createClientList(Client...clientList) {
+ ClientList newClientList = new ClientList();
+ for (Client client : clientList) {
+ newClientList.add(client);
+ }
+ return newClientList;
+ }
+
+ private ClientPackageList createClientPackageList(ClientPackage...clientPackageList) {
+ ClientPackageList newClientPackageList = new ClientPackageList();
+ for (ClientPackage clientPackage: clientPackageList) {
+ newClientPackageList.add(clientPackage);
+ }
+ return newClientPackageList;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/commands/clients/FindClientCommandTest.java b/src/test/java/seedu/duke/commands/clients/FindClientCommandTest.java
new file mode 100644
index 0000000000..45b214de8e
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/clients/FindClientCommandTest.java
@@ -0,0 +1,88 @@
+package seedu.duke.commands.clients;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.TourList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+public class FindClientCommandTest {
+
+ private static Client TEST_CLIENT_ONE = new Client(new String[]{"c001", "Bo Tuan", "93338333", "bt@mail.com"});
+ private static Client TEST_CLIENT_TWO = new Client(new String[]{"c002", "Wayne", "56667888", "wen@mail.com"});
+ private static Client TEST_CLIENT_THREE = new Client(new String[]{"c002", "Bo Tuan", "56667888", "bbt@mail.com"});
+
+ private static final String VALID_DATA_OUTPUT = "This is the client(s) that matches your search\n"
+ + "1. " + TEST_CLIENT_ONE;
+ private static final String TWO_OR_MORE_OUTPUT = "This is the client(s) that matches your search\n"
+ + "1. " + TEST_CLIENT_ONE + "\n\n"
+ + "2. " + TEST_CLIENT_THREE;
+ private static final String INVALID_DATA_OUTPUT = "I'm sorry, "
+ + "there seems to be no client(s) that matches your search";
+
+ PrintStream previousConsole = System.out;
+ ByteArrayOutputStream newConsole = new ByteArrayOutputStream();
+
+ ClientList testClientList = new ClientList();
+ TourList dummyTourList = new TourList();
+ FlightList dummyFlightList = new FlightList();
+ ClientPackageList dummyPackageList = new ClientPackageList();
+ Ui testUi = new Ui();
+
+ @Test
+ void findClientCommand_validData_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ testClientList.add(TEST_CLIENT_ONE);
+ testClientList.add(TEST_CLIENT_TWO);
+ Command findClient = new FindClientCommand("Bo Tuan");
+ findClient.setData(testClientList, dummyFlightList, dummyTourList, dummyPackageList, testUi);
+ findClient.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(VALID_DATA_OUTPUT, actualString);
+ }
+
+ @Test
+ void findClientCommand_TwoOrMoreSameName_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ testClientList.add(TEST_CLIENT_ONE);
+ testClientList.add(TEST_CLIENT_THREE);
+ Command findClient = new FindClientCommand("Bo Tuan");
+ findClient.setData(testClientList, dummyFlightList, dummyTourList, dummyPackageList, testUi);
+ findClient.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(TWO_OR_MORE_OUTPUT, actualString);
+ }
+
+ @Test
+ void findClientCommand_invalidData_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ testClientList.add(TEST_CLIENT_ONE);
+ testClientList.add(TEST_CLIENT_TWO);
+ Command findClient = new FindClientCommand("Sem");
+ findClient.setData(testClientList, dummyFlightList, dummyTourList, dummyPackageList, testUi);
+ findClient.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(INVALID_DATA_OUTPUT, actualString);
+ }
+}
diff --git a/src/test/java/seedu/duke/commands/clients/ListClientCommandTest.java b/src/test/java/seedu/duke/commands/clients/ListClientCommandTest.java
new file mode 100644
index 0000000000..496630774b
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/clients/ListClientCommandTest.java
@@ -0,0 +1,66 @@
+package seedu.duke.commands.clients;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.TourList;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.ClientPackageList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ListClientCommandTest {
+
+ private static Client TEST_CLIENT_ONE = new Client(new String[]{"c001", "Bo Tuan", "93338333", "bt@mail.com"});
+ private static Client TEST_CLIENT_TWO = new Client(new String[]{"c002", "Wayne", "56667888", "wen@mail.com"});
+ private static final String VALID_DATA_OUTPUT = "Here is a list of all clients:\n"
+ + "1. " + TEST_CLIENT_ONE + "\n\n"
+ + "2. " + TEST_CLIENT_TWO + "\n\n"
+ + "Total Clients: 2";
+ private static final String NO_DATA_OUTPUT = "I'm sorry, there seems to be no clients";
+
+ PrintStream previousConsole = System.out;
+ ByteArrayOutputStream newConsole = new ByteArrayOutputStream();
+
+ ClientList testClientList = new ClientList();
+ TourList dummyTourList = new TourList();
+ FlightList dummyFlightList = new FlightList();
+ ClientPackageList dummyPackageList = new ClientPackageList();
+ Ui testUi = new Ui();
+
+ @Test
+ void listClientCommand_validData_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ testClientList.add(TEST_CLIENT_ONE);
+ testClientList.add(TEST_CLIENT_TWO);
+ Command listClient = new ListClientCommand();
+ listClient.setData(testClientList, dummyFlightList, dummyTourList, dummyPackageList, testUi);
+ listClient.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(VALID_DATA_OUTPUT, actualString);
+ }
+
+ @Test
+ void listClientCommand_noData_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ Command listClient = new ListClientCommand();
+ listClient.setData(testClientList, dummyFlightList, dummyTourList, dummyPackageList, testUi);
+ listClient.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(NO_DATA_OUTPUT, actualString);
+ }
+}
diff --git a/src/test/java/seedu/duke/commands/clients/SortClientCommandTest.java b/src/test/java/seedu/duke/commands/clients/SortClientCommandTest.java
new file mode 100644
index 0000000000..68749b6c8a
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/clients/SortClientCommandTest.java
@@ -0,0 +1,99 @@
+package seedu.duke.commands.clients;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.TourList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SortClientCommandTest {
+
+ public static final String VALID_SORT_BY_NAME_FILTER = "/n";
+ private static final String VALID_SORT_BY_ID_FILTER = "/id";
+ public static final String INVALID_FILTER = "/t";
+
+ PrintStream previousConsole = System.out;
+ ByteArrayOutputStream newConsole = new ByteArrayOutputStream();
+
+ ClientList testClientList = new ClientList();
+ TourList dummyTourList = new TourList();
+ FlightList dummyFlightList = new FlightList();
+ ClientPackageList dummyPackageList = new ClientPackageList();
+ Ui testUi = new Ui();
+
+ void initialiseClientListForTesting() {
+ Client botuan = new Client(new String[]{"c001", "Bo Tuan", "93338333", "bt@mail.com"});
+ Client wayne = new Client(new String[]{"c002", "Wayne", "56667888", "wen@mail.com"});
+ testClientList.add(botuan);
+ testClientList.add(wayne);
+ }
+
+ @Test
+ void sortClientCommand_validFilterByName_correctlySortedNameAlphabetically() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+ initialiseClientListForTesting();
+
+ Command testSortClientCommand = new SortClientCommand(VALID_SORT_BY_NAME_FILTER);
+ testSortClientCommand.setData(testClientList, dummyFlightList, dummyTourList, dummyPackageList, testUi);
+ testSortClientCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+
+ testClientList.initTempArray();
+ String expectedString = Ui.SORT_CLIENT_NAME_MESSAGE + "\n"
+ + "1. " + testClientList.getClientByName("Bo Tuan") + "\n\n"
+ + "2. " + testClientList.getClientByName("Wayne");
+
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+
+ @Test
+ void sortClientCommand_validFilterById_correctlySortedIdAlphabetically() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+ initialiseClientListForTesting();
+
+ Command testSortClientCommand = new SortClientCommand(VALID_SORT_BY_ID_FILTER);
+ testSortClientCommand.setData(testClientList, dummyFlightList, dummyTourList, dummyPackageList, testUi);
+ testSortClientCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+
+ testClientList.initTempArray();
+ String expectedString = Ui.SORT_CLIENT_ID_MESSAGE + "\n"
+ + "1. " + testClientList.getClientById("c001") + "\n\n"
+ + "2. " + testClientList.getClientById("c002");
+
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+
+ @Test
+ void sortClientCommand_invalidFilter_throwException() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+ initialiseClientListForTesting();
+
+ Command testSortClientCommand = new SortClientCommand(INVALID_FILTER);
+ testSortClientCommand.setData(testClientList, dummyFlightList, dummyTourList, dummyPackageList, testUi);
+ testSortClientCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+
+ testClientList.initTempArray();
+ String expectedString = SortClientCommand.ERROR_MISSING_FILTER;
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/commands/flights/AddFlightCommandTest.java b/src/test/java/seedu/duke/commands/flights/AddFlightCommandTest.java
new file mode 100644
index 0000000000..8c3f114c4e
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/flights/AddFlightCommandTest.java
@@ -0,0 +1,67 @@
+package seedu.duke.commands.flights;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.Ui;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.Flight;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.TourList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AddFlightCommandTest {
+
+ public static final String TEST_FLIGHT_ID = "SQ-JPN1";
+ public static final String TEST_FLIGHT_DEPART = "JPN";
+ public static final String TEST_FLIGHT_RETURN = "SG";
+ public static final String TEST_DEPART_TIME = "23/10/21 13:00";
+ public static final String TEST_RETURN_TIME = "27/10/21 02:00";
+
+ FlightList testFlightList = new FlightList();
+ ClientList dummyClientList = new ClientList();
+ TourList dummyTourList = new TourList();
+ ClientPackageList dummyPackageList = new ClientPackageList();
+ Ui testUi = new Ui();
+
+ @Test
+ void addFlightCommand_validData_correctlyConstructed() {
+ Flight testFlight =
+ new Flight(new String[]{
+ TEST_FLIGHT_ID,
+ TEST_FLIGHT_DEPART,
+ TEST_FLIGHT_RETURN,
+ TEST_DEPART_TIME,
+ TEST_RETURN_TIME});
+ AddFlightCommand addFlight = new AddFlightCommand(testFlight);
+
+ Flight retrieveFlight = addFlight.getFlight();
+ assertEquals(TEST_FLIGHT_ID, retrieveFlight.getId());
+ assertEquals(TEST_FLIGHT_DEPART, retrieveFlight.getDepartDestination());
+ assertEquals(TEST_FLIGHT_RETURN, retrieveFlight.getReturnDestination());
+ assertEquals(TEST_DEPART_TIME, retrieveFlight.getDepartDate());
+ assertEquals(TEST_RETURN_TIME, retrieveFlight.getReturnDate());
+ }
+
+ @Test
+ void addFlightCommand_emptyFlightList_populatedFlightList() {
+ Flight testFlight =
+ new Flight(new String[]{
+ TEST_FLIGHT_ID,
+ TEST_FLIGHT_DEPART,
+ TEST_FLIGHT_RETURN,
+ TEST_DEPART_TIME,
+ TEST_RETURN_TIME});
+ AddFlightCommand addFlight = new AddFlightCommand(testFlight);
+ addFlight.setData(dummyClientList, testFlightList, dummyTourList, dummyPackageList, testUi);
+ addFlight.execute();
+
+ Flight retrieveFlight = testFlightList.getFlightByIndex(0);
+ assertEquals(TEST_FLIGHT_ID, retrieveFlight.getId());
+ assertEquals(TEST_FLIGHT_DEPART, retrieveFlight.getDepartDestination());
+ assertEquals(TEST_FLIGHT_RETURN, retrieveFlight.getReturnDestination());
+ assertEquals(TEST_DEPART_TIME, retrieveFlight.getDepartDate());
+ assertEquals(TEST_RETURN_TIME, retrieveFlight.getReturnDate());
+ }
+
+}
diff --git a/src/test/java/seedu/duke/commands/flights/CutFlightCommandTest.java b/src/test/java/seedu/duke/commands/flights/CutFlightCommandTest.java
new file mode 100644
index 0000000000..e28970899b
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/flights/CutFlightCommandTest.java
@@ -0,0 +1,124 @@
+package seedu.duke.commands.flights;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackage;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.Flight;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.Tour;
+import seedu.duke.data.TourList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CutFlightCommandTest {
+ public static final String FLIGHT_ID_2 = "SQ-KOR";
+
+ public static final String[] FLIGHT_SQJPN = {"SQ-JPN", "JPN", "SG", "20/10/21 18:00", "21/10/21 03:00"};
+ public static final String[] FLIGHT_SQKOR = {FLIGHT_ID_2, "KOR", "SG", "23/10/2021 18:00", "30/10/2021 03:00"};
+ public static final String[] FLIGHT_SQZWM = {"SQ-ZWM", "ZWM", "SG", "5/11/21 09:00", "7/11/21 15:00"};
+
+ public static final String[] CLIENT_BOTUAN = {"c001", "Bo Tuan", "93338333", "bt@mail.com"};
+ public static final String[] CLIENT_WAYNE = {"c002", "Wayne", "56667888", "wen@mail.com"};
+
+ public static final String[] TOUR_JPN = {"JPN", "Japan Basic Tour", "1500.00"};
+ public static final String[] TOUR_KOR = {"KOR", "Korea Cultural Tour", "3000.00"};
+
+ public static final String CLIENT_PACKAGE_ID_1 = "p001";
+ public static final String CLIENT_PACKAGE_ID_2 = "p002";
+ public static final String FLIGHT_ID_WRONG = "1234po0o";
+
+ PrintStream previousConsole;
+ ByteArrayOutputStream newConsole;
+
+ private ClientList clients;
+ private FlightList flights;
+ private TourList tours;
+ private ClientPackageList clientPackages;
+ private Ui ui = new Ui();
+
+ private FlightList flightsWithoutKor;
+
+ @BeforeEach
+ public void setUp() {
+ previousConsole = System.out;
+ newConsole = new ByteArrayOutputStream();
+
+ Flight sqjpn = new Flight(FLIGHT_SQJPN);
+ Flight sqkor = new Flight(FLIGHT_SQKOR);
+ Flight sqzwm = new Flight(FLIGHT_SQZWM);
+
+ Client botuan = new Client(CLIENT_BOTUAN);
+ Client wayne = new Client(CLIENT_WAYNE);
+
+ Tour jpn = new Tour(TOUR_JPN);
+ Tour kor = new Tour(TOUR_KOR);
+
+ ClientPackage korPack1 = new ClientPackage(CLIENT_PACKAGE_ID_1, botuan, jpn, sqkor);
+ ClientPackage korPack2 = new ClientPackage(CLIENT_PACKAGE_ID_2, wayne, kor, sqkor);
+
+ flights = createFlightList(sqjpn, sqkor, sqzwm);
+ flightsWithoutKor = createFlightList(sqjpn, sqzwm);
+
+ clientPackages = createClientPackageList(korPack1, korPack2);
+ }
+
+ @Test
+ public void cutFlightCommand_validFlightId_flightDeleted() throws TourPlannerException {
+ assertEquals(flights.getFlightCount(), 3);
+ Command cutFlightCommand = new CutFlightCommand(FLIGHT_ID_2);
+ cutFlightCommand.setData(clients, flights, tours, clientPackages, ui);
+ cutFlightCommand.execute();
+ assertEquals(flights.getFlightCount(), flightsWithoutKor.getFlightCount());
+ for (int i = 0; i < flightsWithoutKor.getFlightCount(); i++) {
+ assertEquals(flights.getFlightByIndex(i), flightsWithoutKor.getFlightByIndex(i));
+ }
+ }
+
+ @Test
+ public void cutFlightCommand_validFlightId_clientPackagesDeleted() throws TourPlannerException {
+ Command cutFlightCommand = new CutFlightCommand(FLIGHT_ID_2);
+ cutFlightCommand.setData(clients, flights, tours, clientPackages, ui);
+ cutFlightCommand.execute();
+ assertEquals(clientPackages.getClientPackageCount(), 0);
+ }
+
+ @Test
+ public void cutFlightCommand_invalidFlightId_flightNotFoundMessage() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ Command cutFlightCommand = new CutFlightCommand(FLIGHT_ID_WRONG);
+ cutFlightCommand.setData(clients, flights, tours, clientPackages, ui);
+ cutFlightCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String expectedString = FlightList.FLIGHT_NOT_FOUND_MESSAGE;
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+
+ private FlightList createFlightList(Flight...flightList) {
+ FlightList newFlightList = new FlightList();
+ for (Flight flight : flightList) {
+ newFlightList.add(flight);
+ }
+ return newFlightList;
+ }
+
+ private ClientPackageList createClientPackageList(ClientPackage...clientPackageList) {
+ ClientPackageList newClientPackageList = new ClientPackageList();
+ for (ClientPackage clientPackage: clientPackageList) {
+ newClientPackageList.add(clientPackage);
+ }
+ return newClientPackageList;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/commands/flights/FindFlightCommandTest.java b/src/test/java/seedu/duke/commands/flights/FindFlightCommandTest.java
new file mode 100644
index 0000000000..4cd188e7e2
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/flights/FindFlightCommandTest.java
@@ -0,0 +1,103 @@
+package seedu.duke.commands.flights;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackage;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.Flight;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.Tour;
+import seedu.duke.data.TourList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class FindFlightCommandTest {
+
+ private static Flight TEST_FLIGHT_ONE = new Flight(new String[]{"SQ-JPN", "JPN", "SG", "20/10/2021 18:00",
+ "21/10/2021 03:00"});
+ private static Flight TEST_FLIGHT_TWO = new Flight(new String[]{"SQ-KOR", "KOR", "SG", "23/10/2021 18:00",
+ "30/10/2021 03:00"});
+ private static Client TEST_CLIENT = new Client(new String[]{"c001", "Bo Tuan", "93338333", "bt@mail.com"});
+ private static Tour TEST_TOUR = new Tour(new String[]{"JPN", "Japan Basic Tour", "1500.00"});
+ private static ClientPackage TEST_CLIENTPACKAGE = new ClientPackage("p001",
+ TEST_CLIENT, TEST_TOUR, TEST_FLIGHT_ONE);
+
+ private static final String VALID_DATA_OUTPUT = "This is the flight that matches your search\n"
+ + TEST_FLIGHT_ONE + "\n\n\n"
+ + "Passengers:\n\n"
+ + "Total Passengers: 0";
+ private static final String VALID_PASSENGER_OUTPUT = "This is the flight that matches your search\n"
+ + TEST_FLIGHT_ONE + "\n\n\n"
+ + "Passengers:\n"
+ + "1. Bo Tuan (ID: c001)\n" + "\n"
+ + "Total Passengers: 1";
+ private static final String INVALID_DATA_OUTPUT = "ERROR: Flight cannot be found. Please try another flight ID.";
+
+ PrintStream previousConsole = System.out;
+ ByteArrayOutputStream newConsole = new ByteArrayOutputStream();
+
+ ClientList dummyClientList = new ClientList();
+ TourList dummyTourList = new TourList();
+ FlightList testFlightList = new FlightList();
+ ClientPackageList dummyPackageList = new ClientPackageList();
+ Ui testUi = new Ui();
+
+ @Test
+ void findFlightCommand_validData_correctlyConstructed() throws TourPlannerException {
+
+ System.setOut(new PrintStream(newConsole));
+
+ testFlightList.add(TEST_FLIGHT_ONE);
+ testFlightList.add(TEST_FLIGHT_TWO);
+ Command findFlight = new FindFlightCommand("SQ-JPN");
+ findFlight.setData(dummyClientList, testFlightList, dummyTourList, dummyPackageList, testUi);
+ findFlight.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(VALID_DATA_OUTPUT, actualString);
+ }
+
+ @Test
+ void findFlightCommand_validPassenger_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ dummyClientList.add(TEST_CLIENT);
+ dummyTourList.add(TEST_TOUR);
+ testFlightList.add(TEST_FLIGHT_ONE);
+ dummyPackageList.add(TEST_CLIENTPACKAGE);
+
+ Command findFlight = new FindFlightCommand("SQ-JPN");
+ findFlight.setData(dummyClientList, testFlightList, dummyTourList, dummyPackageList, testUi);
+ findFlight.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(VALID_PASSENGER_OUTPUT, actualString);
+ }
+
+ @Test
+ void findFlightCommand_invalidData_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ testFlightList.add(TEST_FLIGHT_ONE);
+ testFlightList.add(TEST_FLIGHT_TWO);
+ Command findFlight = new FindFlightCommand("SQ-ZBW");
+ findFlight.setData(dummyClientList, testFlightList, dummyTourList, dummyPackageList, testUi);
+ findFlight.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(INVALID_DATA_OUTPUT, actualString);
+ }
+}
diff --git a/src/test/java/seedu/duke/commands/flights/ListFlightCommandTest.java b/src/test/java/seedu/duke/commands/flights/ListFlightCommandTest.java
new file mode 100644
index 0000000000..a35ff7f5d2
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/flights/ListFlightCommandTest.java
@@ -0,0 +1,68 @@
+package seedu.duke.commands.flights;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.Flight;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.TourList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ListFlightCommandTest {
+
+ private static Flight TEST_FLIGHT_ONE = new Flight(new String[]{"SQ-JPN", "JPN", "SG", "20/10/2021 18:00",
+ "21/10/2021 03:00"});
+ private static Flight TEST_FLIGHT_TWO = new Flight(new String[]{"SQ-KOR", "KOR", "SG", "23/10/2021 18:00",
+ "30/10/2021 03:00"});
+ private static final String VALID_DATA_OUTPUT = "Here is a list of all flights:\n"
+ + "1. " + TEST_FLIGHT_ONE + "\n\n"
+ + "2. " + TEST_FLIGHT_TWO + "\n\n"
+ + "Total Flights: 2";
+ private static final String NO_DATA_OUTPUT = "I'm sorry, there seems to be no flights";
+
+ PrintStream previousConsole = System.out;
+ ByteArrayOutputStream newConsole = new ByteArrayOutputStream();
+
+ ClientList dummyClientList = new ClientList();
+ TourList dummyTourList = new TourList();
+ FlightList testFlightList = new FlightList();
+ ClientPackageList dummyPackageList = new ClientPackageList();
+ Ui testUi = new Ui();
+
+ @Test
+ void listClientCommand_validData_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ testFlightList.add(TEST_FLIGHT_ONE);
+ testFlightList.add(TEST_FLIGHT_TWO);
+ Command listTour = new ListFlightCommand();
+ listTour.setData(dummyClientList, testFlightList, dummyTourList, dummyPackageList, testUi);
+ listTour.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(VALID_DATA_OUTPUT, actualString);
+ }
+
+ @Test
+ void listTourCommand_noData_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ Command listTour = new ListFlightCommand();
+ listTour.setData(dummyClientList, testFlightList, dummyTourList, dummyPackageList, testUi);
+ listTour.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(NO_DATA_OUTPUT, actualString);
+ }
+}
diff --git a/src/test/java/seedu/duke/commands/flights/SortFlightCommandTest.java b/src/test/java/seedu/duke/commands/flights/SortFlightCommandTest.java
new file mode 100644
index 0000000000..f793e1e3cc
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/flights/SortFlightCommandTest.java
@@ -0,0 +1,126 @@
+package seedu.duke.commands.flights;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.Flight;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.TourList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class SortFlightCommandTest {
+
+ private static final String VALID_SORT_BY_DEPART_FILTER = "/d";
+ private static final String VALID_SORT_BY_ARRIVE_FILTER = "/r";
+ public static final String VALID_SORT_BY_ID_FILTER = "/id";
+ private static final String INVALID_FILTER = "/rubbish";
+
+ PrintStream previousConsole = System.out;
+ ByteArrayOutputStream newConsole = new ByteArrayOutputStream();
+
+ TourList dummyTourList = new TourList();
+ ClientList dummyClientList = new ClientList();
+ FlightList testFlightList = new FlightList();
+ ClientPackageList dummyPackageList = new ClientPackageList();
+ Ui testUi = new Ui();
+
+ void initialiseFlightListForTesting() {
+ Flight sqjpn = new Flight(new String[]{"SQ-JPN", "JPN", "SG", "20/10/21 18:00", "24/10/21 03:00"});
+ Flight msiaaus = new Flight(new String[]{"MSIA-AUS", "AUS", "MSIA", "2/11/21 23:00", "9/11/21 11:00"});
+ Flight sqkor = new Flight(new String[]{"SQ-KOR", "KOR", "SG", "26/10/21 14:00", "28/10/21 05:00"});
+ testFlightList.add(sqjpn);
+ testFlightList.add(msiaaus);
+ testFlightList.add(sqkor);
+ }
+
+ @Test
+ void sortFlightCommand_validFilterByDepartureDate_correctlySortedTime() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+ initialiseFlightListForTesting();
+
+ Command testSortFlightCommand = new SortFlightCommand(VALID_SORT_BY_DEPART_FILTER);
+ testSortFlightCommand.setData(dummyClientList, testFlightList, dummyTourList, dummyPackageList, testUi);
+ testSortFlightCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+
+ testFlightList.initTempArray();
+ String expectedString = Ui.SORT_FLIGHT_BY_DEPARTURE_MESSAGE + "\n"
+ + "1. " + testFlightList.getFlightByDepartDate("20/10/21 18:00") + "\n\n"
+ + "2. " + testFlightList.getFlightByDepartDate("26/10/21 14:00") + "\n\n"
+ + "3. " + testFlightList.getFlightByDepartDate("2/11/21 23:00");
+
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+
+ @Test
+ void sortFlightCommand_validFilterByArriveDate_correctlySortedTime() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+ initialiseFlightListForTesting();
+
+ Command testSortFlightCommand = new SortFlightCommand(VALID_SORT_BY_ARRIVE_FILTER);
+ testSortFlightCommand.setData(dummyClientList, testFlightList, dummyTourList, dummyPackageList, testUi);
+ testSortFlightCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+
+ testFlightList.initTempArray();
+ String expectedString = Ui.SORT_FLIGHT_BY_ARRIVAL_MESSAGE + "\n"
+ + "1. " + testFlightList.getFlightByReturnDate("24/10/21 03:00") + "\n\n"
+ + "2. " + testFlightList.getFlightByReturnDate("28/10/21 05:00") + "\n\n"
+ + "3. " + testFlightList.getFlightByReturnDate("9/11/21 11:00");
+
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+
+ @Test
+ void sortFlightCommand_validFilterById_correctlySortedIdAlphabetically() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+ initialiseFlightListForTesting();
+
+ Command testSortFlightCommand = new SortFlightCommand(VALID_SORT_BY_ID_FILTER);
+ testSortFlightCommand.setData(dummyClientList, testFlightList, dummyTourList, dummyPackageList, testUi);
+ testSortFlightCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+
+ testFlightList.initTempArray();
+ String expectedString = Ui.SORT_FLIGHT_ID_MESSAGE + "\n"
+ + "1. " + testFlightList.getFlightById("MSIA-AUS") + "\n\n"
+ + "2. " + testFlightList.getFlightById("SQ-JPN") + "\n\n"
+ + "3. " + testFlightList.getFlightById("SQ-KOR");
+
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+
+ @Test
+ void sortTourCommand_invalidFilter_throwException() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+ initialiseFlightListForTesting();
+
+ Command testSortFlightCommand = new SortFlightCommand(INVALID_FILTER);
+ testSortFlightCommand.setData(dummyClientList, testFlightList, dummyTourList, dummyPackageList, testUi);
+ testSortFlightCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+
+ testFlightList.initTempArray();
+ String expectedString = SortFlightCommand.ERROR_MISSING_FILTER;
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/commands/tours/AddTourCommandTest.java b/src/test/java/seedu/duke/commands/tours/AddTourCommandTest.java
new file mode 100644
index 0000000000..7f7006c2b0
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/tours/AddTourCommandTest.java
@@ -0,0 +1,57 @@
+package seedu.duke.commands.tours;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.Tour;
+import seedu.duke.data.TourList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AddTourCommandTest {
+
+ public static final String TEST_TOUR_ID = "t001";
+ public static final String TEST_TOUR_NAME = "AustralianRomance";
+ public static final String TEST_TOUR_PRICE_STRING = "1500";
+
+ TourList testTourList = new TourList();
+ ClientList dummyClientList = new ClientList();
+ FlightList dummyFlightList = new FlightList();
+ ClientPackageList dummyPackageList = new ClientPackageList();
+ Ui testUi = new Ui();
+
+
+ @Test
+ void addTourCommand_validData_correctlyConstructed() {
+ Tour testTour =
+ new Tour(new String[]{TEST_TOUR_ID,
+ TEST_TOUR_NAME,
+ TEST_TOUR_PRICE_STRING});
+ AddTourCommand addTour = new AddTourCommand(testTour);
+
+ Tour retrieveTour = addTour.getTour();
+ assertEquals(TEST_TOUR_ID, retrieveTour.getId());
+ assertEquals(TEST_TOUR_NAME, retrieveTour.getName());
+ assertEquals(Float.parseFloat(TEST_TOUR_PRICE_STRING), retrieveTour.getPrice());
+ }
+
+ @Test
+ void addTourCommand_emptyTourList_populatedTourList() {
+ Tour testTour =
+ new Tour(new String[]{
+ TEST_TOUR_ID,
+ TEST_TOUR_NAME,
+ TEST_TOUR_PRICE_STRING});
+ AddTourCommand addTour = new AddTourCommand(testTour);
+ addTour.setData(dummyClientList, dummyFlightList, testTourList, dummyPackageList, testUi);
+ addTour.execute();
+
+ Tour retrieveTour = addTour.getTour();
+ assertEquals(TEST_TOUR_ID, retrieveTour.getId());
+ assertEquals(TEST_TOUR_NAME, retrieveTour.getName());
+ assertEquals(Float.parseFloat(TEST_TOUR_PRICE_STRING), retrieveTour.getPrice());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/commands/tours/CutTourCommandTest.java b/src/test/java/seedu/duke/commands/tours/CutTourCommandTest.java
new file mode 100644
index 0000000000..1ab074bc78
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/tours/CutTourCommandTest.java
@@ -0,0 +1,124 @@
+package seedu.duke.commands.tours;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackage;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.Flight;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.Tour;
+import seedu.duke.data.TourList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CutTourCommandTest {
+ public static final String TOUR_ID_2 = "KOR";
+
+ public static final String[] TOUR_JPN = {"JPN", "Japan Basic Tour", "1500.00"};
+ public static final String[] TOUR_KOR = {TOUR_ID_2, "Korea Cultural Tour", "3000.00"};
+ public static final String[] TOUR_ZWM = {"ZWM", "Zimbabwe Tour", "1700.00"};
+
+ public static final String[] FLIGHT_SQJPN = {"SQ-JPN", "JPN", "SG", "20/10/21 18:00", "21/10/21 03:00"};
+ public static final String[] FLIGHT_SQKOR = {"SQ-KOR", "KOR", "SG", "23/10/2021 18:00", "30/10/2021 03:00"};
+
+ public static final String[] CLIENT_BOTUAN = {"c001", "Bo Tuan", "93338333", "bt@mail.com"};
+ public static final String[] CLIENT_WAYNE = {"c002", "Wayne", "56667888", "wen@mail.com"};
+
+ public static final String CLIENT_PACKAGE_ID_1 = "p001";
+ public static final String CLIENT_PACKAGE_ID_2 = "p002";
+ public static final String TOUR_ID_WRONG = "1234po0o";
+
+ PrintStream previousConsole;
+ ByteArrayOutputStream newConsole;
+
+ private ClientList clients;
+ private FlightList flights;
+ private TourList tours;
+ private ClientPackageList clientPackages;
+ private Ui ui = new Ui();
+
+ private TourList toursWithoutKor;
+
+ @BeforeEach
+ public void setUp() {
+ previousConsole = System.out;
+ newConsole = new ByteArrayOutputStream();
+
+ Tour jpn = new Tour(TOUR_JPN);
+ Tour kor = new Tour(TOUR_KOR);
+ Tour zwm = new Tour(TOUR_ZWM);
+
+ Flight sqjpn = new Flight(FLIGHT_SQJPN);
+ Flight sqkor = new Flight(FLIGHT_SQKOR);
+
+ Client botuan = new Client(CLIENT_BOTUAN);
+ Client wayne = new Client(CLIENT_WAYNE);
+
+ ClientPackage korPack1 = new ClientPackage(CLIENT_PACKAGE_ID_1, botuan, kor, sqjpn);
+ ClientPackage korPack2 = new ClientPackage(CLIENT_PACKAGE_ID_2, wayne, kor, sqkor);
+
+ tours = createTourList(jpn, kor, zwm);
+ toursWithoutKor = createTourList(jpn, zwm);
+
+ clientPackages = createClientPackageList(korPack1, korPack2);
+ }
+
+ @Test
+ public void cutTourCommand_validTourId_tourDeleted() throws TourPlannerException {
+ assertEquals(tours.getTourCount(), 3);
+ Command cutTourCommand = new CutTourCommand(TOUR_ID_2);
+ cutTourCommand.setData(clients, flights, tours, clientPackages, ui);
+ cutTourCommand.execute();
+ assertEquals(tours.getTourCount(), toursWithoutKor.getTourCount());
+ for (int i = 0; i < toursWithoutKor.getTourCount(); i++) {
+ assertEquals(tours.getTourByIndex(i), toursWithoutKor.getTourByIndex(i));
+ }
+ }
+
+ @Test
+ public void cutTourCommand_validTourId_clientPackagesDeleted() throws TourPlannerException {
+ Command cutTourCommand = new CutTourCommand(TOUR_ID_2);
+ cutTourCommand.setData(clients, flights, tours, clientPackages, ui);
+ cutTourCommand.execute();
+ assertEquals(clientPackages.getClientPackageCount(), 0);
+ }
+
+ @Test
+ public void cutTourCommand_invalidTourId_tourNotFoundMessage() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ Command cutTourCommand = new CutTourCommand(TOUR_ID_WRONG);
+ cutTourCommand.setData(clients, flights, tours, clientPackages, ui);
+ cutTourCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String expectedString = TourList.TOUR_NOT_FOUND_MESSAGE;
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+
+ private TourList createTourList(Tour... tourList) {
+ TourList newTourList = new TourList();
+ for (Tour tour : tourList) {
+ newTourList.add(tour);
+ }
+ return newTourList;
+ }
+
+ private ClientPackageList createClientPackageList(ClientPackage...clientPackageList) {
+ ClientPackageList newClientPackageList = new ClientPackageList();
+ for (ClientPackage clientPackage: clientPackageList) {
+ newClientPackageList.add(clientPackage);
+ }
+ return newClientPackageList;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/duke/commands/tours/FindTourCommandTest.java b/src/test/java/seedu/duke/commands/tours/FindTourCommandTest.java
new file mode 100644
index 0000000000..17d465bd5a
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/tours/FindTourCommandTest.java
@@ -0,0 +1,101 @@
+package seedu.duke.commands.tours;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.Client;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackage;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.Flight;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.Tour;
+import seedu.duke.data.TourList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class FindTourCommandTest {
+
+ private static Tour TEST_TOUR_ONE = new Tour(new String[]{"JPN", "Japan Basic Tour", "1500.00"});
+ private static Tour TEST_TOUR_TWO = new Tour(new String[]{"KOR", "Korea Cultural Tour", "3000.00"});
+ private static Client TEST_CLIENT = new Client(new String[]{"c001", "Bo Tuan", "93338333", "bt@mail.com"});
+ private static Flight TEST_FLIGHT = new Flight(new String[]{"SQ-JPN", "JPN", "SG", "20/10/2021 18:00",
+ "21/10/2021 03:00"});
+ private static ClientPackage TEST_CLIENTPACKAGE = new ClientPackage("p001",
+ TEST_CLIENT, TEST_TOUR_ONE, TEST_FLIGHT);
+
+ private static final String VALID_DATA_OUTPUT = "This is the tour that matches your search\n"
+ + TEST_TOUR_ONE + "\n\n\n"
+ + "Subscribed Clients:\n\n"
+ + "Total Subscribed Clients: 0";
+ private static final String VALID_SUBSCRIPTION_OUTPUT = "This is the tour that matches your search\n"
+ + TEST_TOUR_ONE + "\n\n\n"
+ + "Subscribed Clients:\n"
+ + "1. Bo Tuan (ID: c001)\n"
+ + "\n"
+ + "Total Subscribed Clients: 1";
+ private static final String INVALID_DATA_OUTPUT = "ERROR: Tour cannot be found. "
+ + "Please try another tour ID.";
+
+ PrintStream previousConsole = System.out;
+ ByteArrayOutputStream newConsole = new ByteArrayOutputStream();
+
+ ClientList dummyClientList = new ClientList();
+ TourList testTourList = new TourList();
+ FlightList dummyFlightList = new FlightList();
+ ClientPackageList dummyPackageList = new ClientPackageList();
+ Ui testUi = new Ui();
+
+ @Test
+ void findTourCommand_validData_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+ testTourList.add(TEST_TOUR_ONE);
+ testTourList.add(TEST_TOUR_TWO);
+ Command findTour = new FindTourCommand("JPN");
+ findTour.setData(dummyClientList, dummyFlightList, testTourList, dummyPackageList, testUi);
+ findTour.execute();
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(VALID_DATA_OUTPUT, actualString);
+ }
+
+ @Test
+ void findTourCommand_validSubscription_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ dummyClientList.add(TEST_CLIENT);
+ testTourList.add(TEST_TOUR_ONE);
+ dummyFlightList.add(TEST_FLIGHT);
+ dummyPackageList.add(TEST_CLIENTPACKAGE);
+
+ Command findTour = new FindTourCommand("JPN");
+ findTour.setData(dummyClientList, dummyFlightList, testTourList, dummyPackageList, testUi);
+ findTour.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(VALID_SUBSCRIPTION_OUTPUT, actualString);
+ }
+
+ @Test
+ void findTourCommand_invalidData_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+ testTourList.add(TEST_TOUR_ONE);
+ testTourList.add(TEST_TOUR_TWO);
+ Command findTour = new FindTourCommand("SGP");
+ findTour.setData(dummyClientList, dummyFlightList, testTourList, dummyPackageList, testUi);
+ findTour.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(INVALID_DATA_OUTPUT, actualString);
+ }
+
+}
diff --git a/src/test/java/seedu/duke/commands/tours/ListTourCommandTest.java b/src/test/java/seedu/duke/commands/tours/ListTourCommandTest.java
new file mode 100644
index 0000000000..31e4f699b4
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/tours/ListTourCommandTest.java
@@ -0,0 +1,66 @@
+package seedu.duke.commands.tours;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.Tour;
+import seedu.duke.data.TourList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ListTourCommandTest {
+
+ private static Tour TEST_TOUR_ONE = new Tour(new String[]{"JPN", "Japan Basic Tour", "1500.00"});
+ private static Tour TEST_TOUR_TWO = new Tour(new String[]{"KOR", "Korea Cultural Tour", "3000.00"});
+ private static final String VALID_DATA_OUTPUT = "Here is a list of all tours:\n"
+ + "1. " + TEST_TOUR_ONE + "\n\n"
+ + "2. " + TEST_TOUR_TWO + "\n\n"
+ + "Total Tours: 2";
+ private static final String NO_DATA_OUTPUT = "I'm sorry, there seems to be no tours";
+
+ PrintStream previousConsole = System.out;
+ ByteArrayOutputStream newConsole = new ByteArrayOutputStream();
+
+ ClientList dummyClientList = new ClientList();
+ TourList testTourList = new TourList();
+ FlightList dummyFlightList = new FlightList();
+ ClientPackageList dummyPackageList = new ClientPackageList();
+ Ui testUi = new Ui();
+
+ @Test
+ void listClientCommand_validData_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ testTourList.add(TEST_TOUR_ONE);
+ testTourList.add(TEST_TOUR_TWO);
+ Command listTour = new ListTourCommand();
+ listTour.setData(dummyClientList, dummyFlightList, testTourList, dummyPackageList, testUi);
+ listTour.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(VALID_DATA_OUTPUT, actualString);
+ }
+
+ @Test
+ void listTourCommand_noData_correctlyConstructed() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+
+ Command listTour = new ListTourCommand();
+ listTour.setData(dummyClientList, dummyFlightList, testTourList, dummyPackageList, testUi);
+ listTour.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(NO_DATA_OUTPUT, actualString);
+ }
+}
diff --git a/src/test/java/seedu/duke/commands/tours/SortTourCommandTest.java b/src/test/java/seedu/duke/commands/tours/SortTourCommandTest.java
new file mode 100644
index 0000000000..80378dd715
--- /dev/null
+++ b/src/test/java/seedu/duke/commands/tours/SortTourCommandTest.java
@@ -0,0 +1,162 @@
+package seedu.duke.commands.tours;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.TourPlannerException;
+import seedu.duke.Ui;
+import seedu.duke.commands.Command;
+import seedu.duke.data.ClientList;
+import seedu.duke.data.ClientPackageList;
+import seedu.duke.data.FlightList;
+import seedu.duke.data.Tour;
+import seedu.duke.data.TourList;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class SortTourCommandTest {
+
+ public static final String VALID_SORT_BY_PRICE_FILTER = "/p";
+ private static final String VALID_SORT_BY_NAME_FILTER = "/n";
+ private static final String VALID_SORT_BY_ID_FILTER = "/id";
+ private static final String INVALID_FILTER = "/i";
+ private static final String EMPTY_FILTER = "";
+
+ public static final String ID_ORDER_TWO = "JPN";
+ public static final String ID_ORDER_THREE = "KOR";
+ public static final String ID_ORDER_ONE = "AUS";
+
+ public static final String NAME_ORDER_ONE = "Australia Romantic Tour";
+ public static final String NAME_ORDER_TWO = "Japan Basic Tour";
+ public static final String NAME_ORDER_THREE = "Korea Cultural Tour";
+
+ public static final String PRICE_ORDER_ONE = "1500.00";
+ public static final String PRICE_ORDER_THREE = "3000.00";
+ public static final String PRICE_ORDER_TWO = "1800.00";
+
+ PrintStream previousConsole = System.out;
+ ByteArrayOutputStream newConsole = new ByteArrayOutputStream();
+
+ TourList testTourList = new TourList();
+ ClientList dummyClientList = new ClientList();
+ FlightList dummyFlightList = new FlightList();
+ ClientPackageList dummyPackageList = new ClientPackageList();
+ Ui testUi = new Ui();
+
+ void initialiseTourListForTesting() {
+ Tour jpn = new Tour(new String[]{ID_ORDER_TWO, NAME_ORDER_TWO, PRICE_ORDER_ONE});
+ Tour kor = new Tour(new String[]{ID_ORDER_THREE, NAME_ORDER_THREE, PRICE_ORDER_THREE});
+ Tour aus = new Tour(new String[]{ID_ORDER_ONE, NAME_ORDER_ONE, PRICE_ORDER_TWO});
+ testTourList.add(jpn);
+ testTourList.add(kor);
+ testTourList.add(aus);
+ }
+
+ Float parseFloat(String value) {
+ return Float.parseFloat(value);
+ }
+
+ @Test
+ void sortTourCommand_validFilterByPrice_correctlySortedPriceAscending() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+ initialiseTourListForTesting();
+
+ Command testSortTourCommand = new SortTourCommand(VALID_SORT_BY_PRICE_FILTER);
+ testSortTourCommand.setData(dummyClientList, dummyFlightList, testTourList, dummyPackageList, testUi);
+ testSortTourCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+
+ testTourList.initTempArray();
+ String expectedString = Ui.SORT_TOUR_PRICE_MESSAGE + "\n"
+ + "1. " + testTourList.getTourByPrice(parseFloat(PRICE_ORDER_ONE)) + "\n\n"
+ + "2. " + testTourList.getTourByPrice(parseFloat(PRICE_ORDER_TWO)) + "\n\n"
+ + "3. " + testTourList.getTourByPrice(parseFloat(PRICE_ORDER_THREE));
+
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+
+ @Test
+ void sortTourCommand_validFilterById_correctlySortedIdAscending() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+ initialiseTourListForTesting();
+
+ Command testSortTourCommand = new SortTourCommand(VALID_SORT_BY_ID_FILTER);
+ testSortTourCommand.setData(dummyClientList, dummyFlightList, testTourList, dummyPackageList, testUi);
+ testSortTourCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+
+ testTourList.initTempArray();
+ String expectedString = Ui.SORT_TOUR_ID_MESSAGE + "\n"
+ + "1. " + testTourList.getTourById(ID_ORDER_ONE) + "\n\n"
+ + "2. " + testTourList.getTourById(ID_ORDER_TWO) + "\n\n"
+ + "3. " + testTourList.getTourById(ID_ORDER_THREE);
+
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+
+ @Test
+ void sortTourCommand_validFilterByName_correctlySortedNameAscending() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+ initialiseTourListForTesting();
+
+ Command testSortTourCommand = new SortTourCommand(VALID_SORT_BY_NAME_FILTER);
+ testSortTourCommand.setData(dummyClientList, dummyFlightList, testTourList, dummyPackageList, testUi);
+ testSortTourCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+
+ testTourList.initTempArray();
+ String expectedString = Ui.SORT_TOUR_NAME_MESSAGE + "\n"
+ + "1. " + testTourList.getTourByName(NAME_ORDER_ONE) + "\n\n"
+ + "2. " + testTourList.getTourByName(NAME_ORDER_TWO) + "\n\n"
+ + "3. " + testTourList.getTourByName(NAME_ORDER_THREE);
+
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+
+ @Test
+ void sortTourCommand_invalidFilter_throwException() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+ initialiseTourListForTesting();
+
+ Command testSortTourCommand = new SortTourCommand(INVALID_FILTER);
+ testSortTourCommand.setData(dummyClientList, dummyFlightList, testTourList, dummyPackageList, testUi);
+ testSortTourCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+
+ testTourList.initTempArray();
+ String expectedString = SortTourCommand.ERROR_MISSING_FILTER;
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+
+ @Test
+ void sortTourCommand_emptyFilter_throwException() throws TourPlannerException {
+ System.setOut(new PrintStream(newConsole));
+ initialiseTourListForTesting();
+
+ Command testSortTourCommand = new SortTourCommand(EMPTY_FILTER);
+ testSortTourCommand.setData(dummyClientList, dummyFlightList, testTourList, dummyPackageList, testUi);
+ testSortTourCommand.execute();
+
+ previousConsole.println(newConsole.toString());
+ System.setOut(previousConsole);
+
+ testTourList.initTempArray();
+ String expectedString = SortTourCommand.ERROR_MISSING_FILTER;
+ String actualString = newConsole.toString().trim().replace("\r\n", "\n");
+ assertEquals(expectedString, actualString);
+ }
+
+}
\ No newline at end of file
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 892cb6cae7..e69de29bb2 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,9 +0,0 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
-What is your name?
-Hello James Gosling
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index f6ec2e9f95..e69de29bb2 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -1 +0,0 @@
-James Gosling
\ No newline at end of file