diff --git a/README.md b/README.md index 13f5c77403f..dd8f84bfd7c 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) - -![Ui](docs/images/Ui.png) - -* This is **a sample project for Software Engineering (SE) students**.
- Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. - * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. - * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. -* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. +[![CI Status](https://github.com/AY2223S1-CS2103T-W15-2/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2223S1-CS2103T-W15-2/tp/actions) +[![codecov](https://codecov.io/gh/AY2223S1-CS2103T-W15-2/tp/branch/master/graph/badge.svg?token=H2G32SVMDR)](https://codecov.io/gh/AY2223S1-CS2103T-W15-2/tp) + +![Ui](docs/images/GUI.png) + +# REal-Time +REal-Time is a desktop application for managing your client viewings and scheduling your meetings. +It is optimized for use via a Command Line Interface (CLI) while still having the benefits of a Graphical User +Interface (GUI). + +* For the detailed documentation of this project, +see the **[REal-Time Website](https://ay2223s1-cs2103t-w15-2.github.io/tp/)**. + +**Acknowledgements** +* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). + diff --git a/build.gradle b/build.gradle index 108397716bd..71198c68895 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { id 'jacoco' } -mainClassName = 'seedu.address.Main' +mainClassName = 'seedu.realtime.Main' sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 @@ -42,7 +42,7 @@ task coverage(type: JacocoReport) { dependencies { String jUnitVersion = '5.4.0' - String javaFxVersion = '11' + String javaFxVersion = '18.0.1' implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win' implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac' @@ -66,7 +66,11 @@ dependencies { } shadowJar { - archiveFileName = 'addressbook.jar' + archiveFileName = 'REal-Time.jar' +} + +run { + enableAssertions = true } defaultTasks 'clean', 'test' diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..67ced37d429 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -9,51 +9,53 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` ## Project team -### John Doe - +### Jerome Hoo Jun Jie -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] + -* Role: Project Advisor +[[github](http://github.com/jeromehjj)] +[[portfolio](team/jeromehjj.md)] -### Jane Doe +* Role: Developer +* Responsibilities: Deliverables and Deadlines, Scheduling and Tracking + +### Isaac Li Haoyang - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/isaaclhy00)] +[[portfolio](team/isaaclhy00.md)] -* Role: Team Lead -* Responsibilities: UI +* Role: Developer +* Responsibilities: _To be determined_ -### Johnny Doe +### Nguyen Minh Hoang - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](http://github.com/hoang227)] +[[portfolio](team/hoang227.md)] * Role: Developer -* Responsibilities: Data +* Responsibilities: Deliverables, Testing and Documentation -### Jean Doe +### Amadeus Chi Song yi - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/ama-chi)] +[[portfolio](team/ama-chi.md)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: _To be determined_ -### James Doe +### Gavin Cho - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/gavzzz)] +[[portfolio](team/gavzzz.md)] * Role: Developer -* Responsibilities: UI +* Responsibilities: UI, Deliverables, Testing and Documentation diff --git a/docs/DevOps.md b/docs/DevOps.md index 26354050fa4..cc35078650d 100644 --- a/docs/DevOps.md +++ b/docs/DevOps.md @@ -74,7 +74,7 @@ Any warnings or errors will be printed out to the console. Here are the steps to create a new release. -1. Update the version number in [`MainApp.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java). +1. Update the version number in [`MainApp.java`](https://github.com/se-edu/realTime-level3/tree/master/src/main/java/seedu/address/MainApp.java). 1. Generate a fat JAR file using Gradle (i.e., `gradlew shadowJar`). 1. Tag the repo with the version number. e.g. `v0.1` 1. [Create a new release using GitHub](https://help.github.com/articles/creating-releases/). Upload the JAR file you created. diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..562f192daef 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -52,16 +52,21 @@ The rest of the App consists of four components. **How the architecture components interact with each other** -The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`. +The *Sequence Diagram* below shows how the components interact with each other for +the scenario where the user issues the command `delC 1`. Each of the four main components (also shown in the diagram above), * defines its *API* in an `interface` with the same name as the Component. -* implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point. +* implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API +`interface` mentioned in the previous point. -For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below. +For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality +using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given +component through its interface rather than the concrete class (reason: to prevent outside component's being coupled +to the implementation of a component), as illustrated in the (partial) class diagram below. @@ -73,16 +78,21 @@ The **API** of this component is specified in [`Ui.java`](https://github.com/se- ![Structure of the UI Component](images/UiClassDiagram.png) -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, +`PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract +`UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. -The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml) +The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files +that are in the `src/main/resources/view` folder. For example, the layout +of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) +is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml) The `UI` component, * executes user commands using the `Logic` component. * listens for changes to `Model` data so that the UI can be updated with the modified data. * keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands. -* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`. +* depends on some classes in the `Model` component, as it displays `Client` object residing in the `Model`. ### Logic component @@ -93,16 +103,20 @@ Here's a (partial) class diagram of the `Logic` component: How the `Logic` component works: -1. When `Logic` is called upon to execute a command, it uses the `AddressBookParser` class to parse the user command. -1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to add a person). -1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. -The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call. +1. When `Logic` is called upon to execute a command, it uses the `RealTimeParser` class to parse the user command. +2. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddClientCommand`) +which is executed by the `LogicManager`. +3. The command can communicate with the `Model` when it is executed (e.g. to add a person). +4. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. + +The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delC 1")` API call. ![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) -
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. +
:information_source: **Note:** The lifeline for +`DeleteClientCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline +reaches the end of diagram.
Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command: @@ -110,8 +124,12 @@ Here are the other classes in `Logic` (omitted from the class diagram above) tha How the parsing works: -* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object. -* All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. +* When called upon to parse a user command, the `RealTimeParser` class creates an +`XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddClientCommandParser`) which uses the +other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddClientCommand`) which the +`RealTimeParser` returns back as a `Command` object. +* All `XYZCommandParser` classes (e.g., `AddClientCommandParser`, `DeleteOfferCommandParser`, ...) inherit from +the `Parser` interface so that they can be treated similarly where possible e.g, during testing. ### Model component **API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) @@ -121,12 +139,18 @@ How the parsing works: The `Model` component, -* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object). -* stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. -* stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects. -* does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components) +* stores the data in RealTime i.e., all `Client`, `Offer`, `Listing` and `Meeting` objects (which are contained in a +`UniqueClientList`, `UniqueOfferList`, `UniqueListingList` and `UniqueMeetingList` object respectively). +* stores the currently 'selected' `Client`, `Offer`, `Listing` and `Meeting` objects (e.g., results of a search query) +as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList`, +`ObservableList`, `ObservableList` and `ObservableList` that can be 'observed' e.g. the UI +can be bound to this list so that the UI automatically updates when the data in the list change. +* stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a +`ReadOnlyUserPref` objects. +* does not depend on any of the other three components (as the `Model` represents data entities of the domain, +they should make sense on their own without depending on other components) -
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
+
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `RealTime`, which `Person` references. This allows `RealTime` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
@@ -140,13 +164,13 @@ The `Model` component, The `Storage` component, -* can save both address book data and user preference data in json format, and read them back into corresponding objects. -* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). +* can save both RealTime data and user preference data in json format, and read them back into corresponding objects. +* inherits from both `RealTimeStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). * depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`) ### Common classes -Classes used by multiple components are in the `seedu.addressbook.commons` package. +Classes used by multiple components are in the `seedu.realtime.commons` package. -------------------------------------------------------------------------------------------------------------------- @@ -158,37 +182,37 @@ This section describes some noteworthy details on how certain features are imple #### Proposed Implementation -The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations: +The proposed undo/redo mechanism is facilitated by `VersionedRealTime`. It extends `RealTime` with an undo/redo history, stored internally as an `RealTimeStateList` and `currentStatePointer`. Additionally, it implements the following operations: -* `VersionedAddressBook#commit()` — Saves the current address book state in its history. -* `VersionedAddressBook#undo()` — Restores the previous address book state from its history. -* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history. +* `VersionedRealTime#commit()` — Saves the current address book state in its history. +* `VersionedRealTime#undo()` — Restores the previous address book state from its history. +* `VersionedRealTime#redo()` — Restores a previously undone address book state from its history. -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +These operations are exposed in the `Model` interface as `Model#commitRealTime()`, `Model#undoRealTime()` and `Model#redoRealTime()` respectively. Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. -Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state. +Step 1. The user launches the application for the first time. The `VersionedRealTime` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state. ![UndoRedoState0](images/UndoRedoState0.png) -Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state. +Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitRealTime()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `realTimeStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state. ![UndoRedoState1](images/UndoRedoState1.png) -Step 3. The user executes `add n/David …​` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`. +Step 3. The user executes `add n/David …​` to add a new person. The `add` command also calls `Model#commitRealTime()`, causing another modified address book state to be saved into the `realTimeStateList`. ![UndoRedoState2](images/UndoRedoState2.png) -
:information_source: **Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`. +
:information_source: **Note:** If a command fails its execution, it will not call `Model#commitRealTime()`, so the address book state will not be saved into the `realTimeStateList`.
-Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state. +Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoRealTime()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state. ![UndoRedoState3](images/UndoRedoState3.png) -
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather +
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial RealTime state, then there are no previous RealTime states to restore. The `undo` command uses `Model#canUndoRealTime()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo.
@@ -201,17 +225,17 @@ The following sequence diagram shows how the undo operation works:
-The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state. +The `redo` command does the opposite — it calls `Model#redoRealTime()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state. -
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo. +
:information_source: **Note:** If the `currentStatePointer` is at index `realTimeStateList.size() - 1`, pointing to the latest address book state, then there are no undone realTime states to restore. The `redo` command uses `Model#canRedoRealTime()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
-Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged. +Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitRealTime()`, `Model#undoRealTime()` or `Model#redoRealTime()`. Thus, the `realTimeStateList` remains unchanged. ![UndoRedoState4](images/UndoRedoState4.png) -Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …​` command. This is the behavior that most modern desktop applications follow. +Step 6. The user executes `clear`, which calls `Model#commitRealTime()`. Since the `currentStatePointer` is not pointing at the end of the `realTimeStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …​` command. This is the behavior that most modern desktop applications follow. ![UndoRedoState5](images/UndoRedoState5.png) @@ -234,10 +258,526 @@ The following activity diagram summarizes what happens when a user executes a ne _{more aspects and alternatives to be added}_ -### \[Proposed\] Data archiving +### \[Implemented\] Add Client feature + +#### Client +REal-Time manages **_Clients_** of the user. + +#### Implementation + +To add an `Client` object, the `AddClientCommand` must be executed. This stores the `Client` object into the +`UniqueClientList`. Following the command execution pathway, the implementation of adding clients uses the exposed +`Model#addClient(Client)` method in the `Model` API. + +The `AddClientCommand` is parsed by the `AddClientCommandParser`. +`AddClientCommandParser#parse()` parses the user input to return a `AddClientCommand` object that will be executed. + +Given below is an example usage scenario and how the add clients mechanism behaves at each step. + +Step 1. The user opens up the `REal-Time` application for the first time and observes that there are some sample data +in the `UniqueClientList`. + +Step 2. The user keys in the command word for adding a client, `addC`, followed by the compulsory parameters needed to +add a client, namely the `Name` component prefixed by `n/`, `Phone number` component prefixed by `p/`, the `Email address` +component prefixed by `e/`, the `Address` component prefixed by `a/` and the optional `Tag` component prefixed by +`t/`. In this scenario, the user keys in `addC n/John Doe p/12345678 e/john@gmail.com a/123 John Avenue t/friend t/ neighbor` +into the command box which will execute the `AddClientCommandParser` to check through the arguments and ensure that the compulsory fields are +present. It will then create the `Name`, `Phone`, `Email`, `Address` and `Tag` objects needed to instantiate an `Client` object. The +parser returns a `AddClientCommand` object with the `Client` object ready to be added into `Model` + +Step 3. The `AddClientCommand` calls the `Model#addClient(Client)` to add the client and its relevant attributes. + +Step 4. Depending on whether the `UniqueClientList` already contains data or is an empty list, the added Client will +be sorted automatically by the `ListingId` in lexicographical order. This is done using the `Collections#sort()` method +and implementing a natural order by implementing the `Comparable` interface and overriding the `Comparable#compareTo()` +method. + +The sequence diagram below shows the add client process. + +![addClientSequenceDiagram](images/AddClientSequenceDiagram.png) + +
:information_source: **Note:** The lifeline for +`AddClientCommandParser` and `RealTimeParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline +reaches the end of diagram. +
+ +#### Design considerations + +**Aspect: Client-Listing interaction** + +* **Alternative 1 (current choice):** Separate Listings and Client objects + * Pros: Easier to maintain as they are separate objects, less dependency between each other + * Cons: No reference between the `Client` and `Listing` objects +* **Alternative 2:** Allow Clients to have a Listing attribute instead + * Pros: Easier to reference between `Client` and `Listing` + * Cons: Large dependency between the two. + +### \[Implemented\] Delete Client feature + +The Delete Client feature allows users to delete the specified `Client` in the `UniqueClientList`. + +#### Implementation + +To delete an `Client` object, the `DeleteClientCommand` must be executed. The `DeleteClientCommand` extends the `Command` +class. + +The `DeleteClientCommand` is parsed by the `DeleteClientCommandParser`. `DeleteClientCommandParser#parse()` parses the user +input to return a `DeleteClientCommand` that will be executed. + +Given below is an example usage scenario and how the delete client mechanism behaves at each step. + +Step 1. The user enters `delL 1` in the command box. This calls the `LogicManager#execute()` method which calls the +`RealTimeParser#parse` method to be executed and will determine that the command is a `DeleteClientCommand` after parsing. + +Step 2. A `DeleteClientCommandParser` object is instantiated with the necessary arguments. It will return a +`DeleteClientCommand` object with the specified `Index`. + +Step 3. The `DeleteClientCommand#execute()` method is called which calls the `Model#deleteClient(Client)` method to delete +the Client at the specified `Index` in `Model`. + +The sequence diagram below shows the delete client process. + +![deleteClientSequenceDiagram](images/DeleteClientSequenceDiagram.png) + +
:information_source: **Note:** The lifeline for +`DeleteClientCommandParser` and `DeleteClientCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline +reaches the end of diagram. +
+ +#### Design considerations + +**Aspect: Using index specific or attribute specific deletion** + +* **Alternative 1 (current choice):** Index specific + * Pros: Easier implementation and more suited for fast-typing + * Cons: Might accidentally delete the wrong `Client` if `Index` is mistyped +* **Alternative 2:** Attribute specific + * Pros: User does not have to find for specific `Index` + * Cons: Many `Client` objects might share same attribute such as `Address` and `Phone Number` + +### \[Implemented\] Edit Client feature + +The Edit Client feature allows users to edit any attribute of an `Client` in the `UniqueClientList`. + +#### Implementation + +To edit an `Client`, the `EditClientCommand` is executed. The `EditClientCommand` extends the `Command` class. + +The `EditClientCommand` is parsed by the `EditClientCommandParser`. `EditClientCommandParser#parse()` parses the user input +to return a `EditClientCommand` that will be executed. + +Given below is an example usage scenario and how the edit client mechanism behaves at each step. + +Step 1. The user enters `editC 1 n/Jane Doe p/87654321 a/123 Jane Lane` in the command box. This calls the `LogicManager#execute()` +method which calls the `RealTimeParser#parse` method to be executed and will determine that the command is a +`EditClientCommand` after parsing. + +Step 2. An `EditClientCommandParser` object is instantiated with the necessary arguments. It returns a `EditClientCommand` +with the `Index` and `EditClientDescriptor` as parameters. The `EditClientDescriptor` contains the edited information that +the edited `Client` is supposed to be updated to. In this scenario, we are changing the `Name`, `Phone` and `Address` attribute +of the `Client`. The `EditClientDescriptor` is then updated through the `setName()`, `setName()` and `setAddress()` methods by +the `EditClientDescriptor`. + +Step 3. The `EditClientCommand#execute()` method is called which creates the edited `Client` using the `createEditedClient` +method which takes in the `Client` to be edited and the `EditClientDescriptor` that was created from the previous step. +The edited `Client` is then checked if it is a duplicate or already exists in the `UniqueClientList` through the +`Client#isSameClient()` and `Model#hasClient()` methods. + +An `Client` is the same if it contains the exact same `Name`. + +Step 4. The `Model#setClient(Client)` method is called to replace the target `Client` at the specified `Index` from earlier. +The `Model#updateFilteredClientList()` is called to update the `UniqueClientList`. + +### \[Implemented\] Add Offer feature + +#### Offers + +REal-Time manages **_Offers_** made by Clients that are interested when bidding for Listings. +#### Implementation + +To add an `Offer` object, the `AddOfferCommand` must be executed. This stores the `Offer` object into the +`UniqueOfferList`. Following the command execution pathway, the implementation of adding clients uses the exposed +`Model#addOffer(Offer)` method in the `Model` API. + +The `AddOfferCommand` is parsed by the `AddOfferCommandParser`. +`AddOfferCommandParser#parse()` parses the user input to return a `AddOfferCommand` object that will be executed. + +Given below is an example usage scenario and how the add clients mechanism behaves at each step. + +Step 1. The user opens up the `REal-Time` application for the first time and observes that there are some sample data +in the `UniqueOfferList`. + +Step 2. The user keys in the command word for adding a client, `addO`, followed by the compulsory parameters needed to +add a client, namely the `Name` component prefixed by `n/`, `Price` component prefixed by `o/` and the `ListingId` +component prefixed by `l/`. In this scenario, the user keys in `addO n/John Doe l/BEDOK_SOUTH o/600000` into the command box +which will execute the `AddOfferCommandParser` to check through the arguments and ensure that the compulsory fields are +present. It will then create the `Price`, `ListingId` and `Name` objects needed to instantiate an `Offer` object. The +parser returns a `AddOfferCommand` object with the `Offer` object ready to be added into `Model` + +Step 3. The `AddOfferCommand` calls the `Model#addOffer(Offer)` to add the client and its relevant attributes. + +Step 4. Depending on whether the `UniqueOfferList` already contains data or is an empty list, the added Offer will +be sorted automatically by the `ListingId` in lexicographical order. This is done using the `Collections#sort()` method +and implementing a natural order by implementing the `Comparable` interface and overriding the `Comparable#compareTo()` +method. + +The sequence diagram below shows the add client process. + +![addOfferSequenceDiagram](images/AddOfferSequenceDiagram.png) + +
:information_source: **Note:** The lifeline for +`AddOfferCommandParser` and `RealTimeParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline +reaches the end of diagram. +
+ +#### Design considerations + +**Aspect: Client-Offer interaction** + +* **Alternative 1 (current choice):** Separate Offers and Client objects + * Pros: Easier to maintain as they are separate objects, less dependency between each other + * Cons: No reference between the `Client` and `Offer` objects +* **Alternative 2:** Allow Clients to have an Offer attribute instead + * Pros: Easier to reference between `Client` and `Offer` + * Cons: Large dependency between the two. + +### \[Implemented\] Delete Offer feature + +The Delete Offer feature allows users to delete the specified `Offer` in the `UniqueOfferList`. + +#### Implementation + +To delete an `Offer` object, the `DeleteOfferCommand` must be executed. The `DeleteOfferCommand` extends the `Command` +class. + +The `DeleteOfferCommand` is parsed by the `DeleteOfferCommandParser`. `DeleteOfferCommandParser#parse()` parses the user +input to return a `DeleteOfferCommand` that will be executed. + +Given below is an example usage scenario and how the delete offer mechanism behaves at each step. + +Step 1. The user enters `delO 1` in the command box. This calls the `LogicManager#execute()` method which calls the +`RealTimeParser#parse` method to be executed and will determine that the command is a `DeleteOfferCommand` after parsing. + +Step 2. A `DeleteOfferCommandParser` object is instantiated with the necessary arguments. It will return a +`DeleteOfferCommand` object with the specified `Index`. + +Step 3. The `DeleteOfferCommand#execute()` method is called which calls the `Model#deleteOffer(Offer)` method to delete +the Offer at the specified `Index` in `Model`. + +The sequence diagram below shows the delete offer process. + +![deleteOfferSequenceDiagram](images/DeleteOfferSequenceDiagram.png) + +
:information_source: **Note:** The lifeline for +`DeleteOfferCommandParser` and `DeleteOfferCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline +reaches the end of diagram. +
+ +#### Design considerations + +**Aspect: Using index specific or attribute specific deletion** + +* **Alternative 1 (current choice):** Index specific + * Pros: Easier implementation and more suited for fast-typing + * Cons: Might accidentally delete the wrong `Offer` if `Index` is mistyped +* **Alternative 2:** Attribute specific + * Pros: User does not have to find for specific `Index` + * Cons: Many `Offer` objects might share same attribute such as `ListingId` and `Price` + +### \[Implemented\] Edit Offer feature + +The Edit Offer feature allows users to edit any attribute of an `Offer` in the `UniqueOfferList`. + +#### Implementation + +To edit an `Offer`, the `EditOfferCommand` is executed. The `EditOfferCommand` extends the `Command` class. + +The `EditOfferCommand` is parsed by the `EditOfferCommandParser`. `EditOfferCommandParser#parse()` parses the user input +to return a `EditOfferCommand` that will be executed. + +Given below is an example usage scenario and how the edit offer mechanism behaves at each step. + +Step 1. The user enters `editO 1 l/Bukit_timah_rd o/1000000` in the command box. This calls the `LogicManager#execute()` +method which calls the `RealTimeParser#parse` method to be executed and will determine that the command is a +`EditOfferCommand` after parsing. + +Step 2. An `EditOfferCommandParser` object is instantiated with the necessary arguments. It returns a `EditOfferCommand` +with the `Index` and `EditOfferDescriptor` as parameters. The `EditOfferDescriptor` contains the edited information that +the edited `Offer` is supposed to be updated to. In this scenario, we are changing the `listingId` and `Price` attribute +of the `Offer`. The `EditOfferDescriptor` is then updated through the `setListing()` and `setOfferPrice()` methods by +the `EditOfferDescriptor`. + +Step 3. The `EditOfferCommand#execute()` method is called which creates the edited `Offer` using the `createEditedOffer` +method which takes in the `Offer` to be edited and the `EditOfferDescriptor` that was created from the previous step. +The edited `Offer` is then checked if it is a duplicate or already exists in the `UniqueOfferList` through the +`Offer#isSameOffer()` and `Model#hasOffer()` methods. + +An `Offer` is the same if it contains the exact same `Name`, `Price` and `ListingId`. + +Step 4. The `Model#setOffer(Offer)` method is called to replace the target `Offer` at the specified `Index` from earlier. +The `Model#updateFilteredOfferList()` is called to update the `UniqueOfferList`. + + +### \[Implemented\] Add Listing feature + +#### Listing +REal-Time manages **_Listings_** owned by Clients. + +#### Implementation + +To add an `Listing` object, the `AddListingCommand` must be executed. This stores the `Listing` object into the +`UniqueListingList`. Following the command execution pathway, the implementation of adding listings uses the exposed +`Model#addListing(Listing)` method in the `Model` API. + +The `AddListingCommand` is parsed by the `AddListingCommandParser`. +`AddListingCommandParser#parse()` parses the user input to return a `AddListingCommand` object that will be executed. + +Given below is an example usage scenario and how the add listings mechanism behaves at each step. + +Step 1. The user opens up the `REal-Time` application for the first time and observes that there are some sample data +in the `UniqueListingList`. + +Step 2. The user keys in the command word for adding a listing, `addL`, followed by the compulsory parameters needed to +add a listing, namely the `ListingId` component prefixed by `l/`, `Address` component prefixed by `a/`, the `Owner name` +component prefixed by `n/` and `Asking price` component prefixed by `ap/`. In this scenario, the user keys in +`addL l/007 a/100 Charming avenue n/John Doe ap/800000` into the command box +which will execute the `AddListingCommandParser` to check through the arguments and ensure that the compulsory fields are +present. It will then create the `ListingId`, `Address`, `Name` and `Price` objects needed to instantiate an `Listing` object. The +parser returns a `AddListingCommand` object with the `Listing` object ready to be added into `Model` + +Step 3. The `AddListingCommand` calls the `Model#addListing(Listing)` to add the listing and its relevant attributes. + +Step 4. Depending on whether the `UniqueListingList` already contains data or is an empty list, the added Listing will +be sorted automatically by the `ListingId` in lexicographical order. This is done using the `Collections#sort()` method +and implementing a natural order by implementing the `Comparable` interface and overriding the `Comparable#compareTo()` +method. + +The sequence diagram below shows the add listing process. + +![addListingSequenceDiagram](images/AddListingSequenceDiagram.png) + +
:information_source: **Note:** The lifeline for +`AddListingCommandParser` and `RealTimeParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline +reaches the end of diagram. +
+ +#### Design considerations + +**Aspect: Listing-Offer interaction** + +* **Alternative 1 (current choice):** Separate Listing and Offer objects + * Pros: Easier to maintain as they are separate objects, less dependency between each other + * Cons: `Offer` references the `ListingId` instead of `Listing` objects. +* **Alternative 2:** Allow Offers to have a Listing attribute instead + * Pros: Easier to reference between `Offer` and `Listing` + * Cons: Large dependency between the two. + +### \[Implemented\] Delete Listing feature + +The Delete Listing feature allows users to delete the specified `Listing` in the `UniqueListingList`. + +#### Implementation + +To delete an `Listing` object, the `DeleteListingCommand` must be executed. The `DeleteListingCommand` extends the `Command` +class. + +The `DeleteListingCommand` is parsed by the `DeleteListingCommandParser`. `DeleteListingCommandParser#parse()` parses the user +input to return a `DeleteListingCommand` that will be executed. + +Given below is an example usage scenario and how the delete listing mechanism behaves at each step. + +Step 1. The user enters `delL 1` in the command box. This calls the `LogicManager#execute()` method which calls the +`RealTimeParser#parse` method to be executed and will determine that the command is a `DeleteListingCommand` after parsing. + +Step 2. A `DeleteListingCommandParser` object is instantiated with the necessary arguments. It will return a +`DeleteListingCommand` object with the specified `Index`. + +Step 3. The `DeleteListingCommand#execute()` method is called which calls the `Model#deleteListing(Listing)` method to delete +the Listing at the specified `Index` in `Model`. + +The sequence diagram below shows the delete listing process. + +![deleteListingSequenceDiagram](images/DeleteListingSequenceDiagram.png) + +
:information_source: **Note:** The lifeline for +`DeleteListingCommandParser` and `DeleteListingCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline +reaches the end of diagram. +
+ +#### Design considerations + +**Aspect: Using index specific or attribute specific deletion** + +* **Alternative 1 (current choice):** Index specific + * Pros: Easier implementation and more suited for fast-typing + * Cons: Might accidentally delete the wrong `Listing` if `Index` is mistyped +* **Alternative 2:** Attribute specific + * Pros: User does not have to find for specific `Index` + * Cons: Many `Listing` objects might share same attribute such as `Asking Price` and `Owner`. + +### \[Implemented\] Edit Listing feature + +The Edit Listing feature allows users to edit any attribute of an `Listing` in the `UniqueListingList`. + +#### Implementation + +To edit an `Listing`, the `EditListingCommand` is executed. The `EditListingCommand` extends the `Command` class. + +The `EditListingCommand` is parsed by the `EditListingCommandParser`. `EditListingCommandParser#parse()` parses the user input +to return a `EditListingCommand` that will be executed. -_{Explain here how the data archiving feature will be implemented}_ +Given below is an example usage scenario and how the edit listing mechanism behaves at each step. +Step 1. The user enters `editL 1 l/Bukit_timah_rd ap/1000000` in the command box. This calls the `LogicManager#execute()` +method which calls the `RealTimeParser#parse` method to be executed and will determine that the command is a +`EditListingCommand` after parsing. + +Step 2. An `EditListingCommandParser` object is instantiated with the necessary arguments. It returns a `EditListingCommand` +with the `Index` and `EditListingDescriptor` as parameters. The `EditListingDescriptor` contains the edited information that +the edited `Listing` is supposed to be updated to. In this scenario, we are changing the `listingId` and `Price` attribute +of the `Listing`. The `EditListingDescriptor` is then updated through the `setListing()` and `setListingPrice()` methods by +the `EditListingDescriptor`. + +Step 3. The `EditListingCommand#execute()` method is called which creates the edited `Listing` using the `createEditedListing` +method which takes in the `Listing` to be edited and the `EditListingDescriptor` that was created from the previous step. +The edited `Listing` is then checked if it is a duplicate or already exists in the `UniqueListingList` through the +`Listing#isSameListing()` and `Model#hasListing()` methods. + +An `Listing` is the same if it contains the exact same `Listing Id` and `Address`. + +Step 4. The `Model#setListing(Listing)` method is called to replace the target `Listing` at the specified `Index` from earlier. +The `Model#updateFilteredListingList()` is called to update the `UniqueListingList`. + +### \[Implemented\] Add Meeting feature + +#### Meeting +REal-Time manages **_Meetings_** with Clients. + +#### Implementation + +To add an `Meeting` object, the `AddMeetingCommand` must be executed. This stores the `Meeting` object into the +`UniqueMeetingList`. Following the command execution pathway, the implementation of adding listings uses the exposed +`Model#addMeeting(Meeting)` method in the `Model` API. + +The `AddMeetingCommand` is parsed by the `AddMeetingCommandParser`. +`AddMeetingCommandParser#parse()` parses the user input to return a `AddMeetingCommand` object that will be executed. + +Given below is an example usage scenario and how the add listings mechanism behaves at each step. + +Step 1. The user opens up the `REal-Time` application for the first time and observes that there are some sample data +in the `UniqueMeetingList`. + +Step 2. The user keys in the command word for adding a listing, `addM`, followed by the compulsory parameters needed to +add a listing, namely the `ListingId` component prefixed by `l/`, `Name` component prefixed by `n/`and the `Date and Time` +component prefixed by `d/`. In this scenario, the user keys in +`addM l/007 n/John Doe d/2022-10-20 12:00` into the command box +which will execute the `AddMeetingCommandParser` to check through the arguments and ensure that the compulsory fields are +present. It will then create the `ListingId`, `Name` and `LocalDateTime` objects needed to instantiate an `Meeting` object. The +parser returns a `AddMeetingCommand` object with the `Meeting` object ready to be added into `Model` + +Step 3. The `AddMeetingCommand` calls the `Model#addMeeting(Meeting)` to add the listing and its relevant attributes. + +Step 4. Depending on whether the `UniqueMeetingList` already contains data or is an empty list, the added Meeting will +be sorted automatically by the `LocalDateTime` in lexicographical order. This is done using the `Collections#sort()` method +and implementing a natural order by implementing the `Comparable` interface and overriding the `Comparable#compareTo()` +method. + +The sequence diagram below shows the add listing process. + +![addMeetingSequenceDiagram](images/AddMeetingSequenceDiagram.png) + +
:information_source: **Note:** The lifeline for +`AddMeetingCommandParser` and `RealTimeParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline +reaches the end of diagram. +
+ +#### Design considerations + +**Aspect: Meeting-Listing interaction** + +* **Alternative 1 (current choice):** Separate Meeting and Listing objects + * Pros: Easier to maintain as they are separate objects, less dependency between each other + * Cons: `Offer` references the `ListingId` instead of `Listing` objects. +* **Alternative 2:** Allow Offers to have a Listing attribute instead + * Pros: Easier to reference between `Listing` and `Meeting` + * Cons: Large dependency between the two. + + +### \[Implemented\] Delete Meeting feature + +The Delete Meeting feature allows users to delete the specified `Meeting` in the `UniqueMeetingList`. + +#### Implementation + +To delete an `Meeting` object, the `DeleteMeetingCommand` must be executed. The `DeleteMeetingCommand` extends the `Command` +class. + +The `DeleteMeetingCommand` is parsed by the `DeleteMeetingCommandParser`. `DeleteMeetingCommandParser#parse()` parses the user +input to return a `DeleteMeetingCommand` that will be executed. + +Given below is an example usage scenario and how the delete listing mechanism behaves at each step. + +Step 1. The user enters `delM 1` in the command box. This calls the `LogicManager#execute()` method which calls the +`RealTimeParser#parse` method to be executed and will determine that the command is a `DeleteMeetingCommand` after parsing. + +Step 2. A `DeleteMeetingCommandParser` object is instantiated with the necessary arguments. It will return a +`DeleteMeetingCommand` object with the specified `Index`. + +Step 3. The `DeleteMeetingCommand#execute()` method is called which calls the `Model#deleteMeeting(Meeting)` method to delete +the Meeting at the specified `Index` in `Model`. + +The sequence diagram below shows the delete listing process. + +![deleteMeetingSequenceDiagram](images/DeleteMeetingSequenceDiagram.png) + +
:information_source: **Note:** The lifeline for +`DeleteMeetingCommandParser` and `DeleteMeetingCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline +reaches the end of diagram. +
+ +#### Design considerations + +**Aspect: Using index specific or attribute specific deletion** + +* **Alternative 1 (current choice):** Index specific + * Pros: Easier implementation and more suited for fast-typing + * Cons: Might accidentally delete the wrong `Meeting` if `Index` is mistyped +* **Alternative 2:** Attribute specific + * Pros: User does not have to find for specific `Index` + * Cons: Many `Meeting` objects might share same attribute such as `Listing Id` and `Name`. + +### \[Implemented\] Edit Meeting feature + +The Edit Meeting feature allows users to edit any attribute of an `Meeting` in the `UniqueMeetingList`. + +#### Implementation + +To edit an `Meeting`, the `EditMeetingCommand` is executed. The `EditMeetingCommand` extends the `Command` class. + +The `EditMeetingCommand` is parsed by the `EditMeetingCommandParser`. `EditMeetingCommandParser#parse()` parses the user input +to return a `EditMeetingCommand` that will be executed. + +Given below is an example usage scenario and how the edit listing mechanism behaves at each step. + +Step 1. The user enters `editM 1 l/Bukit_timah_rd d/2022-10-20 23:59` in the command box. This calls the `LogicManager#execute()` +method which calls the `RealTimeParser#parse` method to be executed and will determine that the command is a +`EditMeetingCommand` after parsing. + +Step 2. An `EditMeetingCommandParser` object is instantiated with the necessary arguments. It returns a `EditMeetingCommand` +with the `Index` and `EditMeetingDescriptor` as parameters. The `EditMeetingDescriptor` contains the edited information that +the edited `Meeting` is supposed to be updated to. In this scenario, we are changing the `listingId` and `Time` attribute +of the `Meeting`. The `EditMeetingDescriptor` is then updated through the `setMeeting()` and `setMeetingPrice()` methods by +the `EditMeetingDescriptor`. + +Step 3. The `EditMeetingCommand#execute()` method is called which creates the edited `Meeting` using the `createEditedMeeting` +method which takes in the `Meeting` to be edited and the `EditMeetingDescriptor` that was created from the previous step. +The edited `Meeting` is then checked if it is a duplicate or already exists in the `UniqueMeetingList` through the +`Meeting#isSameMeeting()` and `Model#hasMeeting()` methods. + +An `Meeting` is the same if it contains the exact same `LocalDateTime`. + +Step 4. The `Model#setMeeting(Meeting)` method is called to replace the target `Meeting` at the specified `Index` from earlier. +The `Model#updateFilteredMeetingList()` is called to update the `UniqueMeetingList`. -------------------------------------------------------------------------------------------------------------------- @@ -255,7 +795,7 @@ _{Explain here how the data archiving feature will be implemented}_ ### Product scope -**Target user profile**: +**Target user profile**: Real Estate Agents * has a need to manage a significant number of contacts * prefer desktop apps over other types @@ -263,36 +803,52 @@ _{Explain here how the data archiving feature will be implemented}_ * prefers typing to mouse interactions * is reasonably comfortable using CLI apps -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +**Value propositions**: +* manage clients faster than a typical mouse/GUI driven app +* Real-Estate agents with many clients may struggle to juggle their viewings and manage meetings. +* Track properties to their owners as well as potential buyers +* Centralised place to store client information, what they are looking for ### User stories Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` -| Priority | As a …​ | I want to …​ | So that I can…​ | -| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- | -| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App | -| `* * *` | user | add a new person | | -| `* * *` | user | delete a person | remove entries that I no longer need | -| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list | -| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident | -| `*` | user with many persons in the address book | sort persons by name | locate a person easily | +| Priority | As a …​ | I want to …​ | So that I can…​ | +|----------|--------------------------------------------|-------------------------|------------------------------------------------------------------------| +| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App | +| `* * *` | user | add a new client | see all my clients in one list | +| `* * *` | user | add a new listing | see all my listings in one list | +| `* * *` | user | add my client's offer | track all my clients' current offers | +| `* * *` | user | add my client's meeting | track all my meetings with clients | +| `* * *` | user | delete a client | remove entries that I no longer need | +| `* * *` | user | delete a listing | remove entries that I no longer need | +| `* * *` | user | delete an offer | remove entries that I no longer need | +| `* * *` | user | delete a meeeting | remove entries that I no longer need | +| `* * *` | user | find a client by name | locate details of persons without having to go through the entire list | +| `* * *` | user | edit an client | update any changes to a client's details | +| `* * *` | user | edit an listing | update any changes to a listing's details | +| `* * *` | user | edit an offer | update any changes to a client's offer | +| `* * *` | user | edit an meeting | update any changes to a client's meeting | +| `*` | user with many clients in the address book | sort client by name | locate a person easily | +| `*` | user | save my data | store my data for future use | + *{More to be added}* ### Use cases -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) -**Use case: Delete a person** +(For all use cases below, the **System** is the `RealTime` and the **Actor** is the `user`, unless specified otherwise) + +**Use case: UC01 - Reorder the list** **MSS** 1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person +2. RealTime shows a list of persons +3. User requests to switch a specific person in the list with another +4. RealTime swaps their index Use case ends. @@ -303,25 +859,93 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli Use case ends. * 3a. The given index is invalid. + * 3a1. RealTime shows an error message. + + Use case resumes at step 2. + + +**Use case: UC02 - Delete a person** + +**MSS** + +1. User requests to list persons +2. RealTime shows a list of persons +3. User requests to delete a specific person in the list +4. RealTime deletes the person + + Use case ends. - * 3a1. AddressBook shows an error message. +**Extensions** + +* 2a. The list is empty. + + Use case ends. + +* 3a. The given index is invalid. + * 3a1. RealTime shows an error message. Use case resumes at step 2. +**Use case: UC03 - Add an offer** + +**MSS** +1. User requests to add an offer. +2. RealTime adds the offer. + + Use case ends. + +**Extensions** + +* 1a. User enters invalid input + * 1a1. RealTime shows an error message. + + Use case resumes at Step 1. + +**Use case: UC04 - Edit an offer** + +**MSS** +1. User requests to edit an offer. +2. REal-Time edits the offer. + + Use case ends. + +**Extensions** +* 1a. User enters invalid input + * 1a1. REal-Time shows an error message. + Use case resumes at Step 1. + +**Use case: UC05 - Delete an offer** + +**MSS** +1. User requests to delete an offer. +2. REal-Time deletes the offer. + + Use case ends. + +**Extensions** +* 1a. User enters invalid input + * 1a1. REal-Time shows an error message. + + Use case resumes at Step 1 + *{More to be added}* ### Non-Functional Requirements -1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. -2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. -3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. +2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. +3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +4. Application should start in under a few seconds. *{More to be added}* ### Glossary * **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others +* **Client**: A person that is interested in purchasing or selling a property +* **Offer**: A price that the purchaser is willing to pay for a property +* **Listing**: A property that is being advertised +* **Meeting**: A scheduled appointment between the Real-Estate agent and client -------------------------------------------------------------------------------------------------------------------- @@ -351,6 +975,21 @@ testers are expected to do more *exploratory* testing. 1. _{ more test cases …​ }_ +### Adding a Listing + +1. Adding a listings + + 1. Prerequisites: The owner of the Listing must already be added as a Person. + 2. Test case: `listing id/001 a/100 Charming Avenue n/Alex Yeoh ap/100000`
+ Expected: First a new Listing is created. Its id will be the contents after the 'id/' prefix. + Its Address will be the contents after the 'a/' prefix. Its owner's name + will be the contents after the 'n/' prefix. And the asking price of the Listing + will be the contents after the 'ap/' prefix. + Details of the new listing will be shown in the status message. + 3. Other incorrect delete commands to try: `listing id/... ap/.. n.. a/..`, `listing`, `...`
+ Expected: Error message to warn incorrect input format. +2. _{ more test cases …​ }_ + ### Deleting a person 1. Deleting a person while all persons are being shown @@ -375,3 +1014,87 @@ testers are expected to do more *exploratory* testing. 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ 1. _{ more test cases …​ }_ + + +### Deleting a client + +1. Deleting a person while all client are being shown + + 1. Prerequisites: List all client using the `list` command. Multiple client in the list. + 2. Test case: `delete 1`
+ Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. +Timestamp in the status bar is updated. + 3. Test case: `delete 0`
+ Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. + 4. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
+ Expected: Similar to previous. + + +### Adding an Offer + +1. Adding an offer while all offers are being shown + 1. Prerequisites: The Offer being added must not exist in the current list. + 2. Test case: `addO n/John Doe l/BEDOK_SOUTH o/700000` + + Expected: An Offer with name John Doe, listing ID BEDOK_SOUTH and offer price of 700000 will be added to the list. + + 3. Test case: `addO n/Jackson Ang l/CALIFORNIA o/-10000` + + Expected: No Offer will be added. Error message will appear in the response box. + 4. Other incorrect commands to try: `addO n/Bruce Wayne l/YISHUN o/123p123` + +### Deleting an Offer + +1. Deleting an offer while all offers are being shown + 1. Prerequisites: At least one Offer has to exist in the list. + 2. Test case: `delO 1` + + Expected: First Offer is deleted from the list. Details of the deleted offer shown in the status message. + 3. Test case: `delO 0` + + Expected: No Offer is deleted. Error details shown in the status message. + 4. Other incorrect commands to try: `delO -1`, `delO a` + +### Editing an Offer + +1. Editing an offer while all offers are being shown + 1. Prerequisites: At least one Offer must exist to be edited. + 2. Test case: `editO 1 n/John Doe`
+ Expected: First Offer has the name changed to John Doe. + 3. Test case: `editO 1 o/-1000`
+ Expected: No Offer is edited. Error details shown in the status message. + + +### Adding a Meeting + +1. Adding a meeting while all meeting are being shown + 1. Prerequisites: The Meeting being added must not exist in the current list. + 2. Test case: `addM n/John Doe l/BEDOK_SOUTH d/2022-10-12 23:59` + + Expected: A Meeting with name John Doe, listing ID BEDOK_SOUTH and dateTime 2022-10-12 23:59 will be added to the list. + + 3. Test case: `addM n/Jackson Ang l/CALIFORNIA d/23:59 2022-12-3` + + Expected: No Meeting will be added. Error message will appear in the response box. + 4. Other incorrect commands to try: `addM n/Bruce Wayne l/YISHUN d/2022-100-100 27:20` + +### Deleting a Meeting + +1. Deleting a meeting while all meetings are being shown + 1. Prerequisites: At least one Meeting has to exist in the list. + 2. Test case: `delM 1` + + Expected: First Meeting is deleted from the list. Details of the deleted meeting shown in the status message. + 3. Test case: `delM 0` + + Expected: No Meeting is deleted. Error details shown in the status message. + 4. Other incorrect commands to try: `delM -1`, `delM a` + +### Editing a Meeting + +1. Editing a meeting while all meetings are being shown + 1. Prerequisites: At least one Meeting must exist to be edited. + 2. Test case: `editM 1 n/John Doe`
+ Expected: First Meeting has the name changed to John Doe. + 3. Test case: `editM 1 d/2023-30-20 12:00`
+ Expected: No Meeting is edited. Error details shown in the status message. diff --git a/docs/SettingUp.md b/docs/SettingUp.md index 275445bd551..080b45a40ea 100644 --- a/docs/SettingUp.md +++ b/docs/SettingUp.md @@ -23,7 +23,7 @@ If you plan to use Intellij IDEA (highly recommended): 1. **Import the project as a Gradle project**: Follow the guide [_[se-edu/guides] IDEA: Importing a Gradle project_](https://se-education.org/guides/tutorials/intellijImportGradleProject.html) to import the project into IDEA.
:exclamation: Note: Importing a Gradle project is slightly different from importing a normal Java project. 1. **Verify the setup**: - 1. Run the `seedu.address.Main` and try a few commands. + 1. Run the `seedu.realtime.Main` and try a few commands. 1. [Run the tests](Testing.md) to ensure they all pass. -------------------------------------------------------------------------------------------------------------------- @@ -45,7 +45,7 @@ If you plan to use Intellij IDEA (highly recommended): 1. **Learn the design** - When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [AddressBook’s architecture](DeveloperGuide.md#architecture). + When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [RealTime’s architecture](DeveloperGuide.md#architecture). 1. **Do the tutorials** These tutorials will help you get acquainted with the codebase. diff --git a/docs/Testing.md b/docs/Testing.md index 8a99e82438a..9e9e2d872ca 100644 --- a/docs/Testing.md +++ b/docs/Testing.md @@ -29,8 +29,8 @@ There are two ways to run tests. This project has three types of tests: 1. *Unit tests* targeting the lowest level methods/classes.
- e.g. `seedu.address.commons.StringUtilTest` + e.g. `seedu.realtime.commons.StringUtilTest` 1. *Integration tests* that are checking the integration of multiple code units (those code units are assumed to be working).
- e.g. `seedu.address.storage.StorageManagerTest` + e.g. `seedu.realtime.storage.StorageManagerTest` 1. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
- e.g. `seedu.address.logic.LogicManagerTest` + e.g. `seedu.realtime.logic.LogicManagerTest` diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3716f3ca8a4..1816c55b375 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -3,190 +3,1036 @@ layout: page title: User Guide --- -AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps. +# Introduction + +Welcome to the **REal-Time** User Guide! + +In this user guide, you will find everything you need to know about **REal-Time** in making your user +experience easy and smooth. + +The **REal-Time** user guide provides you with **step-by-step** instructions from installing the application +to teaching you our easy-to-use features! + + +## About REal-Time + +Tired of having to manage your client information and details through multiple spreadsheets, tracking through listings in another +application and scheduling your important meetings with clients through a calendar app? Look no further, as **REal-Time** +has got you covered! + + +**REal-Time** is a desktop application for **Real-Estate agents** to **manage client information, schedule meetings, +and track client offers and listings**, isn't that awesome? + +It is optimized for use via a _Command Line Interface_ ([CLI](#glossary)) while still having the added +benefits of a _Graphical User Interface_ ([GUI](#glossary)). +With just the usage of a standard keyboard, **REal-Time** can get all the necessary tasks +done faster than traditional GUI apps! + +## Fun fact +The **"REa"** in **REal-Time** stands for _Real-Estate agents_, which is the intended target user of our application! + +## Using this guide + +Now that you have read the introduction and learnt about what our product does, get started in using **REal-Time** by +following the guide provided in the [Quick Start](#quick-start) section. Otherwise, +* If you are still unsure of the commands used in REal-Time, the [Command Summary](#command-summary) is a good place to +start. +* The [Prefix Summary](#prefix-summary) and [Glossary](#glossary) are also great places to understand REal-Time +better. +* If you are a developer and would like to help improve our product, take a look at our [Developer Guide](https://ay2223s1-cs2103t-w15-2.github.io/tp/DeveloperGuide.html). + +# Table of Contents +{: .no_toc} * Table of Contents {:toc} -------------------------------------------------------------------------------------------------------------------- -## Quick start -1. Ensure you have Java `11` or above installed in your Computer. +# Quick start + +1. Ensure you have `Java 11` or above installed in your Computer. To install `Java 11`, +click [here](https://www.oracle.com/sg/java/technologies/downloads/#java11) and download the appropriate file depending +on your Operating System ([OS](#glossary)) (_e.g, Linux, Windows, macOS_). -1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +2. Download the latest `REal-Time.jar` file from [here](https://github.com/AY2223S1-CS2103T-W15-2/tp/releases). The +`REal-Time.jar` file is located in the "Assets" section as shown below. +![downloadRelease](./images/downloadRelease.png) -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +3. Copy the file to the folder you want to use as the _home folder_ for REal-Time. If you are unsure on how to create +a new folder, click [here](https://www.computerhope.com/issues/ch000742.htm) to learn more. -1. Double-click the file to start the app. The GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
+4. Double-click the file to start the app. The GUI similar to the one below should appear in a few seconds. +Note how the app contains some sample data.
![Ui](images/Ui.png) -1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
+5. Refer to the [Layout](#layout) if you are still unsure in navigating REal-Time's interface. + +6. Type the command in the command box and press Enter to execute it. e.g. typing [**`help`**](#viewing-help--help) and +pressing Enter will open the help window.
Some example commands you can try: - * **`list`** : Lists all contacts. + * [**`addO`**](#adding-an-offer-addo)`l/John street, block 123, #01-01 n/John Doe o/700000` : Adds an offer by `John Doe` to the list of offers. + + * [**`delC`**](#deleting-a-client--delc)`3` : Deletes the 3rd contact shown in the current list of clients. - * **`add`**`n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book. + * [**`clear`**](#clearing-all-entries--clear) : Deletes all clients, meetings, listings and offers. - * **`delete`**`3` : Deletes the 3rd contact shown in the current list. + * [**`exit`**](#exit-real-time--exit) : Exits the app. - * **`clear`** : Deletes all contacts. +* We recommend you to read the [Command Format](#command-format) section before using REal-Time. - * **`exit`** : Exits the app. +7. Refer to the [Features](#features) below for more details of each command. -1. Refer to the [Features](#features) below for details of each command. +Back to [Table of Contents](#table-of-contents) + +___ + +# Layout + +![Layout](images/layout.png) + +`Command Box` - What you type will be shown here.
+ +`Feedback Box` - REal-Time feedbacks to your commands will appear here.
+ +`Client Box` - All [Clients](#glossary) in REal-Time will appear here.
+ +`Offer Box` - All [Offers](#glossary) in REal-Time will appear here.
+ +`Listing Box` - All [Listings](#glossary) in REal-Time will appear here.
+ +`Meeting Box` - All [Meetings](#glossary) in REal-Time will appear here. + +Back to [Table of Contents](#table-of-contents) -------------------------------------------------------------------------------------------------------------------- -## Features +# Command format +#### We highly recommend you to read this section before using REal-Time +REal-Time functions are based on commands that you enter. REal-Time has 4 main types +of commands. let's learn how to write them! -
+## 1. `add` -**:information_source: Notes about the command format:**
+This type of command includes `addC` `addO` `addL` `addM` and is used to add a new +entry into REal-Time. -* Words in `UPPER_CASE` are the parameters to be supplied by the user.
- e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`. +Back to [Table of Contents](#table-of-contents) -* Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. +**Format:** `addC n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…` -* Items with `…`​ after them can be used multiple times including zero times.
- e.g. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc. +This command adds a client into REal-Time.
+1. `addC` is the **Command Word** +2. `n/` `p/` `e/` `a/` are **Prefixes**. Prefixes help REal-Time distinguish between different inputs. +3. `NAME` `PHONE_NUMBER` `EMAIL` `ADDRESS` are the **Data Field** that you can input. For example, `NAME` in `n/NAME` can be replaced with `n/John Doe`. -* Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. +Let's say you want to add a client named `John Doe`, his phone number +is `12345678`, his email is `john@gmail.com` and his address is `123 John St`.
-* If a parameter is expected only once in the command but you specified it multiple times, only the last occurrence of the parameter will be taken.
- e.g. if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken. +You can enter the following command:
+```text +addC n/John Doe p/12345678 e/john@gmail.com a/123 John St +``` +**Note:** +1. You may notice square brackets [] around some parameters. This indicates that the field is **optional**. For the +example above, the `t/TAG` parameter can be left empty if you do not wish to tag the client. -* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
- e.g. if the command specifies `help 123`, it will be interpreted as `help`. +2. You may also notice `…` after the `t/TAG` field. This indicates that you may enter this field as many times as you +want to (0 or more times). For example, if you want to tag `John Doe` as `Friend` and `Neighbor` you can add +`t/Friend t/Neighbor` to the command above. -
+3. The order of every field does not matter. If the command specifies +`n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. -### Viewing help : `help` +4. If a field is expected only once in the command, but you specified it multiple times, only the last occurrence +will be taken. If you specify `n/John Dough n/John Doe`, only `n/John Doe` will be taken. -Shows a message explaning how to access the help page. -![help message](images/helpMessage.png) +## 2. `del` +This type of command includes `delC` `delO` `delL` `delM` and is used to delete a +current entry from REal-Time. -Format: `help` +Back to [Table of Contents](#table-of-contents) +**Format:** `delL INDEX` -### Adding a person: `add` +Let's say you want to delete the second listing from the `Listing Box`.
-Adds a person to the address book. +You can enter the following command: +```text +delL 2 +``` +**Note:** +1. `INDEX` refers to the index number shown in each of the box. Below is an example:
+![Index](images/OfferIndex.png)
+2. `INDEX` **must be a positive integer** 1, 2, 3, …​ -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +## 3. `edit` +This type of command includes `editC` `editO` `editL` `editM` and is used to edit the details +of a current entry in REal-Time. -
:bulb: **Tip:** -A person can have any number of tags (including 0) -
+Back to [Table of Contents](#table-of-contents) + +**Format:** `editO INDEX [n/NAME] [o/OFFER_PRICE] [l/LISTING_ID]` + +Let's say there is an existing offer at `INDEX` 2 that `Betsy Crowler` made for `john_house` with offer price `100000` +but Betsy Crower just increased her offer price to `200000`, you also realised that you spelt her +name wrongly the first time you add this offer, and it is actualy `Besty Crower` and you want to update this +change in REal-Time.
+ +You can enter the following command:
+ +```text +editO 2 n/Betsy Crower o/200000 +``` + +## 4. `list` +This type of command includes `listC` `listL` and is used to list all the entries +of the specific type in REal-Time. + +Back to [Table of Contents](#table-of-contents) + +**Format:** `listC` + +Let's say you want to see all the clients in REal-Time.
+ +You can enter the following command: +```text +listC +``` + +-------------------------------------------------------------------------------------------------------------------- + +# Features + +## Managing Clients + +In this section, we provide you the basic steps needed to [add](#adding-a-client-addc), [delete](#deleting-a-client--delc), +[edit](#editing-a-client--editc), [find](#finding-clients-by-name-findc) and [list](#listing-clients-listc) clients. + +**If this is not the section you are looking for**, click [here](#table-of-contents) to go back to the **Table of Contents**. + +### Adding a client: `addC` + +> Adds a client to REal-Time. + +**Format:** `addC n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` + +* `NAME` refers to the name of the client.
+* `PHONE_NUMBER` refers to the phone number of the client
+* `EMAIL` refers to the email address of the client.
+* `ADDRESS` refers to the address of the client.
+* `TAG` refers to the tags that you want to associate with the client. + +**Example input:**
+ +**_Success_** +```text +addC n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01 +``` +![addClientSuccess](images/addclientjohndoe.png)
+ +```text +addC n/Betsy Crowe t/friend e/betsy@example.com a/Newgate Prison p/1234567 t/criminal +``` +![addClientSuccess](images/addclientbetsy.png)
+ +**_Failure_** + +* **Invalid client name:** +```text +addC n/John D@e p/98765432 e/johnd@example.com a/John street, block 123, #01-01 +``` + +![addClientFailure](images/invalidclientname.png)
+ +* **Missing fields:** +```text +addC n/John Doe a/John street, block 123, #01-01 +``` +![addClientFailure](images/addclientmissing.png)
+ +**Expected Success Output:**
+A new client is added to RealTime.
+ +**Feedback Box:** + +```text +New client added: [details of the newly added client] +``` + +1. If you have any confusion of the notation, you can refer to the + [Command Format](#command-format) section here. +2. If you are still unsure of the **prefixes**, click [here](#prefix-summary) to find out more. + +Back to [Table of Contents](#table-of-contents)
+Back to [Managing Clients](#managing-clients) -Examples: -* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` -* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal` +___ -### Listing all persons : `list` +### Deleting a client : `delC` -Shows a list of all persons in the address book. +> Deletes the client at the specified index in REal-Time. -Format: `list` +**Format** - `delC INDEX` -### Editing a person : `edit` +**Example Input:**
-Edits an existing person in the address book. +**_Success_** +```text +delC 1 +``` +![deleteClientSuccess](images/deleteclient.png)
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +**_Failure_** +* **Invalid index:** +```text +delC -1 +``` +![deleteClientFailure](images/invaliddelete.png)
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ -* At least one of the optional fields must be provided. -* Existing values will be updated to the input values. -* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative. -* You can remove all the person’s tags by typing `t/` without - specifying any tags after it. +**Expected Success Output:**
+The client at specified `INDEX` is deleted from REal-Time.
-Examples: -* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively. -* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags. +**Feedback Box:** +```text +Deleted Client: [details of the deleted client] +``` +**Note:** +1. `listC` followed by `delC 2` deletes the 2nd client in the REal-Time. +2. `findC Betsy` followed by `delC 1` deletes the 1st client in the results of the `findC` command. -### Locating persons by name: `find` +1. If you have any confusion of the notation, you can refer to the + [Command Format](#command-format) section here. -Finds persons whose names contain any of the given keywords. +Back to [Table of Contents](#table-of-contents)
+Back to [Managing Clients](#managing-clients) -Format: `find KEYWORD [MORE_KEYWORDS]` +___ -* The search is case-insensitive. e.g `hans` will match `Hans` -* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans` -* Only the name is searched. -* Only full words will be matched e.g. `Han` will not match `Hans` -* Persons matching at least one keyword will be returned (i.e. `OR` search). +### Editing a client : `editC` + +> Edits an existing client in REal-Time. + +**Format:** `editC INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` + +**Example input:**
+ +**_Success_** +```text +editC 1 p/91234567 e/johndoe@example.com +``` +![editClientSuccess](images/editclient.png)
+ +```text +editC 2 n/Betsy Crower t/ +``` +![editClientSuccess](images/editbetsy.png)
+ +**_Failure_** + +* **Invalid index:** +```text +editC 0 p/12345678 +``` +![editClientFailure](images/invaliddeleteindex.png)
+ +* **Missing fields:** +```text +editC 1 +``` +![editClientFailure](images/editmissingfield.png)
+ +**Expected Success Output:**
+The client at the specified `INDEX` is edited according to the fields provided.
+ +**Feedback Box:** +```text +Edited Client: [newly updated details of client] +``` + +**Note:** +1. Edits the client at the specified `INDEX`. The index refers to the index number shown in the displayed client list. + The index **must be a positive integer** 1, 2, 3, …​ +2. At least one of the optional fields must be provided. +3. Existing values will be updated to the input values. +4. When editing tags, the existing tags of the client will be removed i.e adding of tags is not cumulative. +5. You can remove all the client’s tags by typing `t/` without + specifying any tags after it. + + +**Help:** +1. If you have any confusion of the notation, you can refer to the + [Command Format](#command-format) section here. +2. If you are still unsure of the **prefixes**, click [here](#prefix-summary) to find out more. + +Back to [Table of Contents](#table-of-contents)
+Back to [Managing Clients](#managing-clients) + +___ + +### Finding clients by name: `findC` + +Finds clients whose names contain any of the given keywords. + +**Format:** `findC KEYWORD [MORE_KEYWORDS]` + +**Example Input:**
+ +**_Success_** +```text +findC John +``` +![findClientSuccess](images/findclient.png)
+ +**_Failure_** + +* **No keywords provided:** +```text +findC +``` +![findClientFailure](images/findclientinvalid.png)
+* The list of clients in the Client Box is updated to a list of all matches from the findC command. + +**Feedback Box:** +```text +[number of matches] clients listed! +``` + +![result for 'find alex david'](images/findAlexDavidResult.png) + +**Note:** +1. The search is case-insensitive. e.g `hans` will match `Hans` +2. The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans` +3. Only the name is searched. +4. Only full words will be matched e.g. `Han` will not match `Hans` +5. Clients matching at least one keyword will be returned (i.e. `OR` search). e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` -Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +**Help:** +1. If you have any confusion of the notation, you can refer to the + [Command Format](#command-format) section here. + +Back to [Table of Contents](#table-of-contents)
+Back to [Managing Clients](#managing-clients) + +___ + +### Listing Clients: `listC` + +> Shows the full list of clients. + +**Format:** `listC` + +**Expected Success Output:**
+The full list of clients appears in the Client Box.
+ +![listClientSuccess](images/listclient.png)
+ +**Feedback Box:** +```text +Listed all clients +``` + + +Back to [Table of Contents](#table-of-contents)
+Back to [Managing Clients](#managing-clients) + +___ + +## Managing Offers + +In this section, we provide you the basic steps needed to [add](#adding-an-offer-addo), [delete](#deleting-an-offer--delo), +and [edit](#editing-an-offer--edito) offers. + +**If this is not the section you are looking for**, click [here](#table-of-contents) to go back to the **Table of Contents**. + +___ + +### Adding an offer: `addO` + + +> Adds an offer to REal-Time. + +**Format:** `addO l/LISTING_ID n/NAME o/OFFER_PRICE` + +* `LISTING_ID` refers to the ListingId of the Listing that the offer has been made for.
+* `NAME` refers to the name of the client making the offer.
+* `OFFER_PRICE` refers to the offer price that has been made for the listing. + +**Example Input:**
+ +**_Success_** +```text +addO l/BEDOK_NORTH_BLK123_12 n/John Doe o/2000000 +``` +![addOfferSuccess](images/AddOfferSuccess.png)
+ +**_Failure_** + +* **Missing fields:** +```text +addO l/BEDOK_NORTH_BLK123_12 n/John Doe +``` +![addOfferFailure1](images/AddOfferFailure1.png)
+ +* **Invalid offer price:** +```text +addO l/BEDOK_NORTH_BLK123_12 n/John Doe o/-1 +``` +![addOfferFailure2](images/AddOfferFailure2.png)
+ +**Expected Success Output:**
+A new offer is added to REal-Time
+ +**Feedback Box:** +```text +New offer added: [offer details] +``` + +**Help:** +1. If you have any confusion of the notation, you can refer to the + [Command Format](#command-format) section here. +2. If you are still unsure of the **prefixes**, click [here](#prefix-summary) to find out more. + +Back to [Table of Contents](#table-of-contents)
+Back to [Managing Offers](#managing-offers) + +___ + +### Deleting an offer : `delO` + +> Deletes the specified offer in REal-Time. + +**Format** - `delO INDEX` + +**Example Input:**
+ +**_Success_** +```text +delO 2 +``` +![DeleteOfferSuccess](images/DeleteOfferSuccess.png)
+ +**_Failure_** -### Deleting a person : `delete` +* **Invalid index:** +```text +delO 0 +``` +![DeleteOfferInvalid1](images/DeleteOfferInvalid1.png)
-Deletes the specified person from the address book. +* **Missing Index** +```text +delO +``` +![DeleteOfferInvalid2](images/DeleteOfferInvalid2.png)
-Format: `delete INDEX` +**Expected Success Output:**
+The offer at specified `INDEX` is deleted from REal-Time.
-* Deletes the person at the specified `INDEX`. -* The index refers to the index number shown in the displayed person list. -* The index **must be a positive integer** 1, 2, 3, …​ +**Feedback Box:** +```text +Deleted Offer: [details of the deleted offer] +``` +**Help:** +1. If you have any confusion of the notation, you can refer to the +[Command Format](#command-format) section here. -Examples: -* `list` followed by `delete 2` deletes the 2nd person in the address book. -* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command. +Back to [Table of Contents](#table-of-contents)
+Back to [Managing Offers](#managing-offers) + +___ + +### Editing an offer : `editO` + +> Edits an existing offer in REal-Time. + +**Format:** `editO INDEX [n/NAME] [o/OFFER_PRICE] [l/LISTING_ID]` + +**Example Input:** + +**_Success_** +```text +editO 2 n/Betsy Crower o/200000 +``` +![EditOfferSuccess](images/EditOfferSuccess.png)
+ +**_Failure_** + +* **Invalid index:** +```text +editO -1 n/Betsy Crower o/200000 +``` +![EditOfferInvalidIndex](images/EditOfferInvalidIndex.png)
+ +* **Invalid client name:** +```text +editO 2 n/Betsy Cr@wer o/200000 +``` +![EditOfferInvalidName](images/EditOfferInvalidName.png)
+ +**Expected Success Output:**
+The offer at `INDEX` 2 is edited according to the fields provided.
+ +**Feedback Box:** +```text +Edited Offer: [newly updated details of offer] +``` +**Note:** +1. Edits the offer at the specified `INDEX`. The index refers to the index number shown in the displayed client list. + The index **must be a positive integer** 1, 2, 3, …​ +2. At least one of the optional fields must be provided. +3. Existing values will be updated to the input values. + +**Help:** +1. If you have any confusion of the notation, you can refer to the + [Command Format](#command-format) section here. +2. If you are still unsure of the **prefixes**, click [here](#prefix-summary) to find out more. + +Back to [Table of Contents](#table-of-contents)
+Back to [Managing Offers](#managing-offers) + +___ + +## Managing Listings + +In this section, we provide you the basic steps needed to [add](#adding-a-listing-addl), [delete](#deleting-a-listing--dell), +[edit](#editing-a-listing--editl) and [list](#listing-listings-listl) listings. + +**If this is not the section you are looking for**, click [here](#table-of-contents) to go back to the **Table of Contents**. + + +### Adding a listing: `addL` + +> Adds a listing to REal-Time. + +**Format:** `addL l/LISTING_ID a/ADDRESS n/OWNER_NAME ap/ASKING_PRICE [t/TAG]…​` + + +* `l/LISTING_ID` refers to the ListingId you wish to assign to this Listing.
+* `a/ADDRESS` refers to the address of this Listing.
+* `n/NAME` refers to the name of the owner of this Listing.
+* `ap/ASKING_PRICE` refers to the asking price that the owner is asking for this Listing. + +**Example Input:** + +**_Success_** +```text +addL l/007 a/100 Charming Ave n/Joke Peralta ap/10000000 +``` +![addL example](images/addL.png) + +**_Failure_** + +* **Missing asking price field:** +```text +addL l/007 a/100 Charming Ave n/Joke Peralta` +``` +![addL Missing Parameter example](images/addLMissingParam.png) + +**Expected Success Output:**
+A new listing is added to REal-Time
+ +**Feedback Box:** +```text +New listing added: [listing details] +``` + +**Help:** +1. If you have any confusion of the notation, you can refer to the + [Command Format](#command-format) section here. +2. If you are still unsure of the **prefixes**, click [here](#prefix-summary) to find out more. + +Back to [Table of Contents](#table-of-contents)
+Back to [Managing Listings](#managing-listings) + +___ + +### Deleting a listing : `delL` + +> Deletes the listing at the specified Index from REal-Time. + +**Format:** `delL id/INDEX` + +**Example Input:**
+ +**_Success_** +```text +delL 1 +``` +![delL example](images/delL.png) + +**_Failure_** +* **Invalid index:** +```text +delL 2356739457 +``` +![delL Invalid ListingId example](images/delLInvalidId.png) + +**Expected Success Output:**
+The listing at the specified `INDEX` is deleted from REal-Time.
+ +**Feedback Box:** +```text +Deleted Listing: [details of the deleted listing] +``` +**Help:** +1. If you have any confusion of the notation, you can refer to the + [Command Format](#command-format) section here. + +Back to [Table of Contents](#table-of-contents)
+Back to [Managing Listings](#managing-listings) + +___ + +### Editing a listing : `editL` + +> Edits an existing listing in the REal-Time. + +**Format:** `editL INDEX [l/LISTING_ID] [a/ADDRESS] [n/OWNER_NAME] [ap/ASKING_PRICE] [t/TAG]…​` + +**Example input:**
+ +**_Success_** +```text +editL 1 n/Joke Peralta +``` +![editL example](images/editL.png) +![editedL example](images/editedL.png) + +**_Failure_** +* **Invalid index:** +```text +editL 200 n/Joke Peralta +``` +![editL IndexOutOfBounds example](images/editLIndexOutOfBounds.png) + +* **Missing fields:** +```text +editL 1 +``` +![editL No Change example](images/editLNoChange.png) + +**Expected Success Output:**
+The listing at the specified `INDEX` is edited according to the fields provided.
+ +**Feedback Box:** +```text +Edited Listing: [newly updated details of listing] +``` +**Note:** +1. Edits the listing at the specified `INDEX`. The index refers to the index number shown in the displayed listing list. + The index **must be a positive integer** 1, 2, 3, …​ +2. At least one of the optional fields must be provided. +3. Existing values will be updated to the input values. +4. When editing tags, the existing tags of the listing will be removed i.e adding of tags is not cumulative. +5. You can remove all the listing’s tags by typing `t/` without + specifying any tags after it. + +**Help:** +1. If you have any confusion of the notation, you can refer to the + [Command Format](#command-format) section here. +2. If you are still unsure of the **prefixes**, click [here](#prefix-summary) to find out more. + +Back to [Table of Contents](#table-of-contents)
+Back to [Managing Listings](#managing-listings) + +___ + +### Listing listings: `listL` + +> Shows the full list of listings. + +**Format:** `listL` + +**Expected Success Output:**
+ +The full list of listings appears in the Client Box.
+ +![ListLSuccess](images/listL.png) + +**Feedback Box:** +```text +Listed all listings +``` + +Back to [Table of Contents](#table-of-contents)
+Back to [Managing Listings](#managing-listings) + +___ + +## Managing Meetings + +In this section, we provide you the basic steps needed to [add](#adding-a-meeting-addm), [delete](#deleting-a-meeting--delm) +and [edit](#editing-a-meeting--editm) meetings. + +**If this is not the section you are looking for**, click [here](#table-of-contents) to go back to the **Table of Contents**. + +> Currently, Meetings are not displayed in the UI. + +### Adding a meeting: `addM` + +> Adds a meeting to the REal-Time. + +**Format:** `addM l/LISTING_ID n/CLIENT_NAME d/DATE_TIME` + +* `LISTING_ID` refers to the Listing the meeting is about. +* `CLIENT_NAME` refers to the name of the Client you are meeting. +* `DATE_TIME` refers to the date and time of the meeting. + +**Example Input:**
+ +**_Success_** +```text +addM l/007 n/Joke Peralta d/2022-10-20 12:00 +``` +![addM](images/addM_example.png)
+ +**_Failure_** +* **Wrong date format:** +```text +addM l/007 n/Joke Peralta d/tomorrow 12pm +``` +![addM](images/addM_invalid.png)
+ +**Note:** +1. DATE_TIME must be in this format, yyyy-MM-dd HH:mm + +**Help:** +1. If you have any confusion of the notation, you can refer to the + [Command Format](#command-format) section here. +2. If you are still unsure of the **prefixes**, click [here](#prefix-summary) to find out more. + +Back to [Table of Contents](#table-of-contents)
+Back to [Managing Meetings](#managing-meetings) + +___ + +### Deleting a meeting : `delM` + +> Deletes the meeting at the specified index from REal-Time. + +**Format:** `delM id/INDEX` + +**Example Input:**
+ +**_Success_** +```text +delM 1 +``` +![addM](images/deleteM_example.png)
+ +**_Failure_** +* **Invalid index:** +```text +delM -1 +``` +![addM](images/delM_invalid.png)
+ +**Expected Success Output:**
+The meeting at the specified `INDEX` is deleted from REal-Time.
+ +**Feedback Box:** +```text +Deleted Meeting: [details of the deleted listing] +``` +**Help:** +1. If you have any confusion of the notation, you can refer to the + [Command Format](#command-format) section here. + +Back to [Table of Contents](#table-of-contents) +Back to [Managing Meetings](#managing-meetings) + +___ + +### Editing a meeting : `editM` + +> Edits an existing meeting in REal-Time. + +**Format:** `editM INDEX [n/NAME] [d/DATE_TIME]` + +**Example input:**
+ +**_Success_** +```text +`editM 1 n/Joke Peralta d/2022-10-20 23:59` +``` +![editM](images/editM_example.png)
+ +**_Failure_** +* **Invalid index:** +```text +editM -1 n/Joke Peralta +``` +![editM Invalid](images/editM_invalid.png)
+ +* **Missing fields:** +```text +editM 1 +``` +![editM no fields](images/editM_nofields.png)
+ +**Expected Success Output:**
+The meeting at the specified `INDEX` is edited according to the fields provided.
+ +**Feedback Box:** +```text +Edited Meeting: [newly updated details of meeting] +``` +**Note:** +1. Edits the meeting at the specified `INDEX`. The index refers to the index number shown in the displayed client list. + The index **must be a positive integer** 1, 2, 3, …​ +2. At least one of the optional fields must be provided. +3. Existing values will be updated to the input values. + +**Help:** +1. If you have any confusion of the notation, you can refer to the + [Command Format](#command-format) section here. +2. If you are still unsure of the **prefixes**, click [here](#prefix-summary) to find out more. + +Back to [Table of Contents](#table-of-contents)
+Back to [Managing Meetings](#managing-meetings) + +___ + +## General + +In this section, we provide you the basic steps needed to use the [help](#viewing-help--help), [clear](#clearing-all-entries--clear) and +[exit](#exit-real-time--exit) commands. + +**If this is not the section you are looking for**, click [here](#table-of-contents) to go back to the **Table of Contents**. + + +Available commands: `help` `clear` `exit` + +### Viewing help : `help` + +> Show a help window for REal-Time + +**Format:** `help` + +**Expected Success Output:**
+ +A window displaying help similar to below will appear. + +![help message](images/helpMessage.png) + +Back to [Table of Contents](#table-of-contents)
+Back to [General](#general) ### Clearing all entries : `clear` -Clears all entries from the address book. +> Clears all entries in REal-Time. + +**Example Input:**
+```text +clear +``` +**Expected Success Output:**
+All data in REal-Time is cleared -Format: `clear` +Back to [Table of Contents](#table-of-contents)
+Back to [General](#general) -### Exiting the program : `exit` +___ +### Exit REal-Time : `exit` +> Exits REal-Time -Exits the program. +**Example Input:**
+```text +exit +``` +**Expected Success Output:**
+The REal-Time window closes. + +Back to [Table of Contents](#table-of-contents)
+Back to [General](#general) + +___ -Format: `exit` ### Saving the data -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +REal-Time data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. + +Back to [Table of Contents](#table-of-contents)
+Back to [General](#general) + +___ ### Editing the data file -AddressBook data are saved as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file. +REal-Time data are saved as a [JSON file](#glossary) `[JAR file location]/data/realtime.json`. +Advanced users are welcome to update data directly by editing that data file. -
:exclamation: **Caution:** -If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. +
:exclamation: **Caution:** If your changes to the data file makes its format invalid, REal-Time will +discard all data and start with an empty data file at the next run.
-### Archiving data files `[coming in v2.0]` +Back to [Table of Contents](#table-of-contents)
+Back to [General](#general) -_Details coming soon ..._ +# Glossary --------------------------------------------------------------------------------------------------------------------- +| Term | Description | +|:-------------:|:-----------------------------------------------------------------------------------------------------------:| +| **OS** | The operating system is the software that is used to run in your computer. | +| **CLI** | Interface that takes in text commands from the user | +| **GUI** | A form of user interface that allows users to interact through graphics | +| **Client** | A person that is interested in purchasing or selling a property | +| **Offer** | A price that the purchaser is willing to pay for a property | +| **Listing** | A property that is being advertised | +| **Meeting** | A scheduled appointment between the Real-Estate agent and client | +| **JSON file** | JSON stands for JavaScript Object Notation. JSON is a lightweight format for storing and transporting data. | + +Back to [Introduction](#introduction)
+Back to [Table of Contents](#table-of-contents) -## FAQ +-------------------------------------------------------------------------------------------------------------------- -**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder. +# Command Summary + +| Action | Format | Examples | +|:-------------------------------------------------:|:----------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------------------------:| +| [**Add Client**](#adding-a-client-addc) | `addC n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` | `addC n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague` | +| [**Add Listing**](#adding-a-listing-addl) | `addL l/LISTING_ID a/ADDRESS n/OWNER_NAME ap/ASKING_PRICE [t/TAG]…​` | `addL l/007 a/100 Charming Ave n/Joke Peralta ap/10000000` | +| [**Add Meeting**](#adding-a-meeting-addm) | `addM id/Listing_ID n/CLIENT_NAME d/DATE_TIME` | `addM id/1 a/John street, block 123, #01-01 d/2022-10-20 12:00` | +| [**Add Offer**](#adding-an-offer-addo) | `addO l/LISTING_ID n/NAME o/OFFER_PRICE` | `addO l/30_SERGARDENS_LOR23_0718 n/Bob o/600000` | +| [**Clear**](#clearing-all-entries--clear) | `clear` | `clear` | +| [**Delete Client**](#deleting-a-client--delc) | `delC INDEX` | `delC 3` | +| [**Delete Listing**](#deleting-a-listing--dell) | `delL INDEX` | `delL 1` | +| [**Delete Meeting**](#deleting-a-meeting--delm) | `delM INDEX` | `delM 4` | +| [**Delete Offer**](#deleting-an-offer--delo) | `delO INDEX` | `delO 2` | +| [**Edit Client**](#editing-a-client--editc) | `editC INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` | `editC 2 n/James Lee e/jameslee@example.com` | +| [**Edit Listing**](#editing-a-listing--editl) | `editL INDEX [l/LISTING_ID][a/ADDRESS] [n/OWNER_NAME] [ap/ASKING_PRICE] [t/TAG]…​` | `editL 4 ap/1234567` | +| [**Edit Meeting**](#editing-a-meeting--editm) | `editM INDEX [n/OWNER_NAME] [d/DATE_TIME]` | `editM 2 n/Roza Santiago` | +| [**Edit Offer**](#editing-an-offer--edito) | `editO INDEX [n/NAME] [o/OFFER_PRICE] [l/LISTING_ID]` | `editO 2 n/Betsy Crower o/123456` | +| [**Find Client**](#finding-clients-by-name-findc) | `findC KEYWORD [MORE_KEYWORDS]` | `findC James Jake` | +| [**List Client**](#listing-clients-listc) | `listC` | `listC` | +| [**List Listing**](#listing-listings-listl) | `listL` | `listL` | +| [**Help**](#viewing-help--help) | `help` | `help` | + + +Back to [Table of Contents](#table-of-contents) -------------------------------------------------------------------------------------------------------------------- -## Command summary - -Action | Format, Examples ---------|------------------ -**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague` -**Clear** | `clear` -**Delete** | `delete INDEX`
e.g., `delete 3` -**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…​`
e.g.,`edit 2 n/James Lee e/jameslee@example.com` -**Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` -**List** | `list` -**Help** | `help` +# Prefix Summary + +| Prefix | Description | Used in | Example | +|--------|-------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------| +| `a/` | **Address** of the Client or the Listing. | [Add Client](#adding-a-client-addc)
[Add Listing](#adding-a-listing-addl)
[Edit Client](#editing-a-client--editc)
[Edit Listing](#editing-a-listing--editl) | `a/123, Clementi Rd, 1234665` | +| `ap/` | **Asking Price** of the Owner in a Listing. | [Add Listing](#adding-a-listing-addl)
[Edit Listing](#editing-a-listing--editl) | `ap/500000` | +| `d/` | **Date and time** of a Meeting with a Client. | [Add Meeting](#adding-a-meeting-addm)
[Edit Meeting](#editing-a-meeting--editm) | `d/2022-10-20 12:00` | +| `e/` | **Email** of the Client. | [Add Client](#adding-a-client-addc)
[Edit Client](#editing-a-client--editc) | `e/johndoe@example.com` | +| `l/` | **Listing ID** by the user for a Listing. Can be specified by the user. | [Add Listing](#adding-a-listing-addl)
[Add Offer](#adding-an-offer-addo)
[Edit Listing](#editing-a-listing--editl)
[Edit Offer](#editing-an-offer--edito) | `l/BEDOK_NORTH_RD_BLK123` | +| `n/` | **Name** of Client or Owner of a Listing | [Add Client](#adding-a-client-addc)
[Add Listing](#adding-a-listing-addl)
[Add Offer](#adding-an-offer-addo)
[Edit Client](#editing-a-client--editc)
[Edit Offer](#editing-an-offer--edito)
[Edit Listing](#editing-a-listing--editl) | `n/John Doe` | +| `o/` | **Offer price** by a Client | [Add Offer](#adding-an-offer-addo)
[Edit Offer](#editing-an-offer--edito) | `o/700000` | +| `p/` | **Phone number** of a Client | [Add Client](#adding-a-client-addc)
[Edit Client](#editing-a-client--editc) | `p/12345678` | +| `t/` | **Tag** to specify a unique trait of a Listing or Client | [Add Client](#adding-a-client-addc)
[Add Listing](#adding-a-listing-addl)
[Edit Client](#editing-a-client--editc)
[Edit Listing](#editing-a-listing--editl) | `t/4room` | + +Back to [Table of Contents](#table-of-contents) diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..842882529cb 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "REal-Time" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2223S1-CS2103T-W15-2/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..cd761ee1edf 100644 --- a/docs/_sass/minima/_base.scss +++ b/docs/_sass/minima/_base.scss @@ -288,7 +288,7 @@ table { text-align: center; } .site-header:before { - content: "AB-3"; + content: "REal-Time"; font-size: 32px; } } diff --git a/docs/diagrams/AddClientSequenceDiagram.puml b/docs/diagrams/AddClientSequenceDiagram.puml new file mode 100644 index 00000000000..ecd6b0986f7 --- /dev/null +++ b/docs/diagrams/AddClientSequenceDiagram.puml @@ -0,0 +1,80 @@ +@startuml +!include style.puml + + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":RealTimeParser" as RealTimeParser LOGIC_COLOR +participant ":AddClientCommandParser" as AddClientCommandParser LOGIC_COLOR +participant "c: AddClientCommand" as AddClientCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +-> LogicManager : execute(userInput) +activate LogicManager + +LogicManager -> RealTimeParser : parseCommand(userInput) +activate RealTimeParser + +create AddClientCommandParser +RealTimeParser -> AddClientCommandParser +activate AddClientCommandParser + +AddClientCommandParser --> RealTimeParser +deactivate AddClientCommandParser + +note right of AddClientCommandParser +Arguments in userInput are seperated by their prefixes +using the tokenize method in the ArgumentTokenizer class. +Each attribute is parsed by their respective parsers in the ParserUtil class +and is used to instantiate an Client object. +end note + +RealTimeParser -> AddClientCommandParser : parse(userInput)) +activate AddClientCommandParser + + +create AddClientCommand +AddClientCommandParser -> AddClientCommand +activate AddClientCommand +return c +return c +return c +destroy AddClientCommandParser + + +LogicManager -> AddClientCommand : execute(model) +destroy RealTimeParser +activate AddClientCommand + + +AddClientCommand -> Model : hasClient(o) +activate Model + +Model --> AddClientCommand +deactivate Model + +AddClientCommand -> Model : addClient(o) +activate Model + +Model --> AddClientCommand +deactivate Model + +create CommandResult +AddClientCommand -> CommandResult +activate CommandResult + +CommandResult --> AddClientCommand +deactivate CommandResult + +AddClientCommand --> LogicManager : result +deactivate AddClientCommand + +<--LogicManager +deactivate LogicManager + +@enduml diff --git a/docs/diagrams/AddListingSequenceDiagram.puml b/docs/diagrams/AddListingSequenceDiagram.puml new file mode 100644 index 00000000000..d71136c3d53 --- /dev/null +++ b/docs/diagrams/AddListingSequenceDiagram.puml @@ -0,0 +1,80 @@ +@startuml +!include style.puml + + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":RealTimeParser" as RealTimeParser LOGIC_COLOR +participant ":AddListingCommandParser" as AddListingCommandParser LOGIC_COLOR +participant "l: AddListingCommand" as AddListingCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +-> LogicManager : execute(userInput) +activate LogicManager + +LogicManager -> RealTimeParser : parseCommand(userInput) +activate RealTimeParser + +create AddListingCommandParser +RealTimeParser -> AddListingCommandParser +activate AddListingCommandParser + +AddListingCommandParser --> RealTimeParser +deactivate AddListingCommandParser + +note right of AddListingCommandParser +Arguments in userInput are seperated by their prefixes +using the tokenize method in the ArgumentTokenizer class. +Each attribute is parsed by their respective parsers in the ParserUtil class +and is used to instantiate an Listing object. +end note + +RealTimeParser -> AddListingCommandParser : parse(userInput)) +activate AddListingCommandParser + + +create AddListingCommand +AddListingCommandParser -> AddListingCommand +activate AddListingCommand +return l +return l +return l +destroy AddListingCommandParser + + +LogicManager -> AddListingCommand : execute(model) +destroy RealTimeParser +activate AddListingCommand + + +AddListingCommand -> Model : hasListing(o) +activate Model + +Model --> AddListingCommand +deactivate Model + +AddListingCommand -> Model : addListing(o) +activate Model + +Model --> AddListingCommand +deactivate Model + +create CommandResult +AddListingCommand -> CommandResult +activate CommandResult + +CommandResult --> AddListingCommand +deactivate CommandResult + +AddListingCommand --> LogicManager : result +deactivate AddListingCommand + +<--LogicManager +deactivate LogicManager + +@enduml diff --git a/docs/diagrams/AddMeetingSequenceDiagram.puml b/docs/diagrams/AddMeetingSequenceDiagram.puml new file mode 100644 index 00000000000..0c8b534a7ff --- /dev/null +++ b/docs/diagrams/AddMeetingSequenceDiagram.puml @@ -0,0 +1,80 @@ +@startuml +!include style.puml + + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":RealTimeParser" as RealTimeParser LOGIC_COLOR +participant ":AddMeetingCommandParser" as AddMeetingCommandParser LOGIC_COLOR +participant "m: AddMeetingCommand" as AddMeetingCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +-> LogicManager : execute(userInput) +activate LogicManager + +LogicManager -> RealTimeParser : parseCommand(userInput) +activate RealTimeParser + +create AddMeetingCommandParser +RealTimeParser -> AddMeetingCommandParser +activate AddMeetingCommandParser + +AddMeetingCommandParser --> RealTimeParser +deactivate AddMeetingCommandParser + +note right of AddMeetingCommandParser +Arguments in userInput are seperated by their prefixes +using the tokenize method in the ArgumentTokenizer class. +Each attribute is parsed by their respective parsers in the ParserUtil class +and is used to instantiate an Meeting object. +end note + +RealTimeParser -> AddMeetingCommandParser : parse(userInput)) +activate AddMeetingCommandParser + + +create AddMeetingCommand +AddMeetingCommandParser -> AddMeetingCommand +activate AddMeetingCommand +return m +return m +return m +destroy AddMeetingCommandParser + + +LogicManager -> AddMeetingCommand : execute(model) +destroy RealTimeParser +activate AddMeetingCommand + + +AddMeetingCommand -> Model : hasMeeting(o) +activate Model + +Model --> AddMeetingCommand +deactivate Model + +AddMeetingCommand -> Model : addMeeting(o) +activate Model + +Model --> AddMeetingCommand +deactivate Model + +create CommandResult +AddMeetingCommand -> CommandResult +activate CommandResult + +CommandResult --> AddMeetingCommand +deactivate CommandResult + +AddMeetingCommand --> LogicManager : result +deactivate AddMeetingCommand + +<--LogicManager +deactivate LogicManager + +@enduml diff --git a/docs/diagrams/AddOfferSequenceDiagram.puml b/docs/diagrams/AddOfferSequenceDiagram.puml new file mode 100644 index 00000000000..5e46edcce14 --- /dev/null +++ b/docs/diagrams/AddOfferSequenceDiagram.puml @@ -0,0 +1,80 @@ +@startuml +!include style.puml + + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":RealTimeParser" as RealTimeParser LOGIC_COLOR +participant ":AddOfferCommandParser" as AddOfferCommandParser LOGIC_COLOR +participant "o: AddOfferCommand" as AddOfferCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +-> LogicManager : execute(userInput) +activate LogicManager + +LogicManager -> RealTimeParser : parseCommand(userInput) +activate RealTimeParser + +create AddOfferCommandParser +RealTimeParser -> AddOfferCommandParser +activate AddOfferCommandParser + +AddOfferCommandParser --> RealTimeParser +deactivate AddOfferCommandParser + +note right of AddOfferCommandParser +Arguments in userInput are seperated by their prefixes +using the tokenize method in the ArgumentTokenizer class. +Each attribute is parsed by their respective parsers in the ParserUtil class +and is used to instantiate an Offer object. +end note + +RealTimeParser -> AddOfferCommandParser : parse(userInput)) +activate AddOfferCommandParser + + +create AddOfferCommand +AddOfferCommandParser -> AddOfferCommand +activate AddOfferCommand +return o +return o +return o +destroy AddOfferCommandParser + + +LogicManager -> AddOfferCommand : execute(model) +destroy RealTimeParser +activate AddOfferCommand + + +AddOfferCommand -> Model : hasOffer(o) +activate Model + +Model --> AddOfferCommand +deactivate Model + +AddOfferCommand -> Model : addOffer(o) +activate Model + +Model --> AddOfferCommand +deactivate Model + +create CommandResult +AddOfferCommand -> CommandResult +activate CommandResult + +CommandResult --> AddOfferCommand +deactivate CommandResult + +AddOfferCommand --> LogicManager : result +deactivate AddOfferCommand + +<--LogicManager +deactivate LogicManager + +@enduml diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index ef81d18c337..a5caba5391a 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -7,19 +7,19 @@ Participant ":Logic" as logic LOGIC_COLOR Participant ":Model" as model MODEL_COLOR Participant ":Storage" as storage STORAGE_COLOR -user -[USER_COLOR]> ui : "delete 1" +user -[USER_COLOR]> ui : "delC 1" activate ui UI_COLOR -ui -[UI_COLOR]> logic : execute("delete 1") +ui -[UI_COLOR]> logic : execute("delC 1") activate logic LOGIC_COLOR -logic -[LOGIC_COLOR]> model : deletePerson(p) +logic -[LOGIC_COLOR]> model : deleteClient(p) activate model MODEL_COLOR model -[MODEL_COLOR]-> logic deactivate model -logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook) +logic -[LOGIC_COLOR]> storage : saveRealTime(realTime) activate storage STORAGE_COLOR storage -[STORAGE_COLOR]> storage : Save to file diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml index 5731f9cbaa1..d4d3b86860a 100644 --- a/docs/diagrams/BetterModelClassDiagram.puml +++ b/docs/diagrams/BetterModelClassDiagram.puml @@ -4,18 +4,18 @@ skinparam arrowThickness 1.1 skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR -AddressBook *-right-> "1" UniquePersonList -AddressBook *-right-> "1" UniqueTagList -UniqueTagList -[hidden]down- UniquePersonList -UniqueTagList -[hidden]down- UniquePersonList +RealTime *-right-> "1" UniqueClientList +RealTime *-right-> "1" UniqueTagList +UniqueTagList -[hidden]down- UniqueClientList +UniqueTagList -[hidden]down- UniqueClientList UniqueTagList *-right-> "*" Tag -UniquePersonList -right-> Person +UniqueClientList -right-> Client -Person -up-> "*" Tag +Client -up-> "*" Tag -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address +Client *--> Name +Client *--> Phone +Client *--> Email +Client *--> Address @enduml diff --git a/docs/diagrams/CommitActivityDiagram.puml b/docs/diagrams/CommitActivityDiagram.puml index 6a6b23a006f..92b559a8345 100644 --- a/docs/diagrams/CommitActivityDiagram.puml +++ b/docs/diagrams/CommitActivityDiagram.puml @@ -5,10 +5,10 @@ start 'Since the beta syntax does not support placing the condition outside the 'diamond we place it as the true branch instead. -if () then ([command commits AddressBook]) +if () then ([command commits RealTime]) :Purge redundant states; - :Save AddressBook to - addressBookStateList; + :Save RealTime to + realTimeStateList; else ([else]) endif stop diff --git a/docs/diagrams/DeleteClientSequenceDiagram.puml b/docs/diagrams/DeleteClientSequenceDiagram.puml new file mode 100644 index 00000000000..223ceaa471e --- /dev/null +++ b/docs/diagrams/DeleteClientSequenceDiagram.puml @@ -0,0 +1,71 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":RealTimeParser" as RealTimeParser LOGIC_COLOR +participant ":DeleteClientCommandParser" as DeleteClientCommandParser LOGIC_COLOR +participant "c:DeleteClientCommand" as DeleteClientCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +-> LogicManager : execute("delC 1") +activate LogicManager + +LogicManager -> RealTimeParser : parseCommand("delC 1") +activate RealTimeParser + +create DeleteClientCommandParser +RealTimeParser -> DeleteClientCommandParser +activate DeleteClientCommandParser + +DeleteClientCommandParser --> RealTimeParser +deactivate DeleteClientCommandParser + +RealTimeParser -> DeleteClientCommandParser : parse("1") +activate DeleteClientCommandParser + +create DeleteClientCommand +DeleteClientCommandParser -> DeleteClientCommand +activate DeleteClientCommand + +DeleteClientCommand --> DeleteClientCommandParser : c +deactivate DeleteClientCommand + +DeleteClientCommandParser --> RealTimeParser : c +deactivate DeleteClientCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +DeleteClientCommandParser -[hidden]-> RealTimeParser +destroy DeleteClientCommandParser + +RealTimeParser --> LogicManager : c +deactivate RealTimeParser + +LogicManager -> DeleteClientCommand : execute() +activate DeleteClientCommand + +DeleteClientCommand -> Model : deleteClient(1) +activate Model + +Model --> DeleteClientCommand +deactivate Model + +create CommandResult +DeleteClientCommand -> CommandResult +activate CommandResult + +CommandResult --> DeleteClientCommand +deactivate CommandResult + +DeleteClientCommand --> LogicManager : result +deactivate DeleteClientCommand + + +[<--LogicManager +deactivate LogicManager +destroy DeleteClientCommand +@enduml diff --git a/docs/diagrams/DeleteListingSequenceDiagram.puml b/docs/diagrams/DeleteListingSequenceDiagram.puml new file mode 100644 index 00000000000..6bf2a742b0c --- /dev/null +++ b/docs/diagrams/DeleteListingSequenceDiagram.puml @@ -0,0 +1,71 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":RealTimeParser" as RealTimeParser LOGIC_COLOR +participant ":DeleteListingCommandParser" as DeleteListingCommandParser LOGIC_COLOR +participant "l:DeleteListingCommand" as DeleteListingCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +-> LogicManager : execute("delL 1") +activate LogicManager + +LogicManager -> RealTimeParser : parseCommand("delL 1") +activate RealTimeParser + +create DeleteListingCommandParser +RealTimeParser -> DeleteListingCommandParser +activate DeleteListingCommandParser + +DeleteListingCommandParser --> RealTimeParser +deactivate DeleteListingCommandParser + +RealTimeParser -> DeleteListingCommandParser : parse("1") +activate DeleteListingCommandParser + +create DeleteListingCommand +DeleteListingCommandParser -> DeleteListingCommand +activate DeleteListingCommand + +DeleteListingCommand --> DeleteListingCommandParser : l +deactivate DeleteListingCommand + +DeleteListingCommandParser --> RealTimeParser : l +deactivate DeleteListingCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +DeleteListingCommandParser -[hidden]-> RealTimeParser +destroy DeleteListingCommandParser + +RealTimeParser --> LogicManager : l +deactivate RealTimeParser + +LogicManager -> DeleteListingCommand : execute() +activate DeleteListingCommand + +DeleteListingCommand -> Model : deleteListing(1) +activate Model + +Model --> DeleteListingCommand +deactivate Model + +create CommandResult +DeleteListingCommand -> CommandResult +activate CommandResult + +CommandResult --> DeleteListingCommand +deactivate CommandResult + +DeleteListingCommand --> LogicManager : result +deactivate DeleteListingCommand + + +[<--LogicManager +deactivate LogicManager +destroy DeleteListingCommand +@enduml diff --git a/docs/diagrams/DeleteMeetingSequenceDiagram.puml b/docs/diagrams/DeleteMeetingSequenceDiagram.puml new file mode 100644 index 00000000000..96dd99f3dc4 --- /dev/null +++ b/docs/diagrams/DeleteMeetingSequenceDiagram.puml @@ -0,0 +1,71 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":RealTimeParser" as RealTimeParser LOGIC_COLOR +participant ":DeleteMeetingCommandParser" as DeleteMeetingCommandParser LOGIC_COLOR +participant "m:DeleteMeetingCommand" as DeleteMeetingCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +-> LogicManager : execute("delM 1") +activate LogicManager + +LogicManager -> RealTimeParser : parseCommand("delM 1") +activate RealTimeParser + +create DeleteMeetingCommandParser +RealTimeParser -> DeleteMeetingCommandParser +activate DeleteMeetingCommandParser + +DeleteMeetingCommandParser --> RealTimeParser +deactivate DeleteMeetingCommandParser + +RealTimeParser -> DeleteMeetingCommandParser : parse("1") +activate DeleteMeetingCommandParser + +create DeleteMeetingCommand +DeleteMeetingCommandParser -> DeleteMeetingCommand +activate DeleteMeetingCommand + +DeleteMeetingCommand --> DeleteMeetingCommandParser : m +deactivate DeleteMeetingCommand + +DeleteMeetingCommandParser --> RealTimeParser : m +deactivate DeleteMeetingCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +DeleteMeetingCommandParser -[hidden]-> RealTimeParser +destroy DeleteMeetingCommandParser + +RealTimeParser --> LogicManager : m +deactivate RealTimeParser + +LogicManager -> DeleteMeetingCommand : execute() +activate DeleteMeetingCommand + +DeleteMeetingCommand -> Model : deleteMeeting(1) +activate Model + +Model --> DeleteMeetingCommand +deactivate Model + +create CommandResult +DeleteMeetingCommand -> CommandResult +activate CommandResult + +CommandResult --> DeleteMeetingCommand +deactivate CommandResult + +DeleteMeetingCommand --> LogicManager : result +deactivate DeleteMeetingCommand + + +[<--LogicManager +deactivate LogicManager +destroy DeleteMeetingCommand +@enduml diff --git a/docs/diagrams/DeleteOfferSequenceDiagram.puml b/docs/diagrams/DeleteOfferSequenceDiagram.puml new file mode 100644 index 00000000000..df609532c32 --- /dev/null +++ b/docs/diagrams/DeleteOfferSequenceDiagram.puml @@ -0,0 +1,71 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":RealTimeParser" as RealTimeParser LOGIC_COLOR +participant ":DeleteOfferCommandParser" as DeleteOfferCommandParser LOGIC_COLOR +participant "o:DeleteOfferCommand" as DeleteOfferCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +-> LogicManager : execute("delO 1") +activate LogicManager + +LogicManager -> RealTimeParser : parseCommand("delO 1") +activate RealTimeParser + +create DeleteOfferCommandParser +RealTimeParser -> DeleteOfferCommandParser +activate DeleteOfferCommandParser + +DeleteOfferCommandParser --> RealTimeParser +deactivate DeleteOfferCommandParser + +RealTimeParser -> DeleteOfferCommandParser : parse("1") +activate DeleteOfferCommandParser + +create DeleteOfferCommand +DeleteOfferCommandParser -> DeleteOfferCommand +activate DeleteOfferCommand + +DeleteOfferCommand --> DeleteOfferCommandParser : o +deactivate DeleteOfferCommand + +DeleteOfferCommandParser --> RealTimeParser : o +deactivate DeleteOfferCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +DeleteOfferCommandParser -[hidden]-> RealTimeParser +destroy DeleteOfferCommandParser + +RealTimeParser --> LogicManager : o +deactivate RealTimeParser + +LogicManager -> DeleteOfferCommand : execute() +activate DeleteOfferCommand + +DeleteOfferCommand -> Model : deleteOffer(1) +activate Model + +Model --> DeleteOfferCommand +deactivate Model + +create CommandResult +DeleteOfferCommand -> CommandResult +activate CommandResult + +CommandResult --> DeleteOfferCommand +deactivate CommandResult + +DeleteOfferCommand --> LogicManager : result +deactivate DeleteOfferCommand + + +[<--LogicManager +deactivate LogicManager +destroy DeleteOfferCommand +@enduml diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 1dc2311b245..6dc00bd6204 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -3,9 +3,9 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR -participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR -participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR +participant ":RealTimeParser" as RealTimeParser LOGIC_COLOR +participant ":DeleteClientCommandParser" as DeleteClientCommandParser LOGIC_COLOR +participant "d:DeleteClientCommand" as DeleteClientCommand LOGIC_COLOR participant ":CommandResult" as CommandResult LOGIC_COLOR end box @@ -13,56 +13,56 @@ box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR end box -[-> LogicManager : execute("delete 1") +[-> LogicManager : execute("delC 1") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") -activate AddressBookParser +LogicManager -> RealTimeParser : parseCommand("delC 1") +activate RealTimeParser -create DeleteCommandParser -AddressBookParser -> DeleteCommandParser -activate DeleteCommandParser +create DeleteClientCommandParser +RealTimeParser -> DeleteClientCommandParser +activate DeleteClientCommandParser -DeleteCommandParser --> AddressBookParser -deactivate DeleteCommandParser +DeleteClientCommandParser --> RealTimeParser +deactivate DeleteClientCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") -activate DeleteCommandParser +RealTimeParser -> DeleteClientCommandParser : parse("1") +activate DeleteClientCommandParser -create DeleteCommand -DeleteCommandParser -> DeleteCommand -activate DeleteCommand +create DeleteClientCommand +DeleteClientCommandParser -> DeleteClientCommand +activate DeleteClientCommand -DeleteCommand --> DeleteCommandParser : d -deactivate DeleteCommand +DeleteClientCommand --> DeleteClientCommandParser : d +deactivate DeleteClientCommand -DeleteCommandParser --> AddressBookParser : d -deactivate DeleteCommandParser +DeleteClientCommandParser --> RealTimeParser : d +deactivate DeleteClientCommandParser 'Hidden arrow to position the destroy marker below the end of the activation bar. -DeleteCommandParser -[hidden]-> AddressBookParser -destroy DeleteCommandParser +DeleteClientCommandParser -[hidden]-> RealTimeParser +destroy DeleteClientCommandParser -AddressBookParser --> LogicManager : d -deactivate AddressBookParser +RealTimeParser --> LogicManager : d +deactivate RealTimeParser -LogicManager -> DeleteCommand : execute() -activate DeleteCommand +LogicManager -> DeleteClientCommand : execute() +activate DeleteClientCommand -DeleteCommand -> Model : deletePerson(1) +DeleteClientCommand -> Model : deleteClient(1) activate Model -Model --> DeleteCommand +Model --> DeleteClientCommand deactivate Model create CommandResult -DeleteCommand -> CommandResult +DeleteClientCommand -> CommandResult activate CommandResult -CommandResult --> DeleteCommand +CommandResult --> DeleteClientCommand deactivate CommandResult -DeleteCommand --> LogicManager : result -deactivate DeleteCommand +DeleteClientCommand --> LogicManager : result +deactivate DeleteClientCommand [<--LogicManager deactivate LogicManager diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index d4193173e18..6248604964a 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR package Logic { -Class AddressBookParser +Class RealTimeParser Class XYZCommand Class CommandResult Class "{abstract}\nCommand" as Command @@ -27,8 +27,8 @@ Class HiddenOutside #FFFFFF HiddenOutside ..> Logic LogicManager .right.|> Logic -LogicManager -right->"1" AddressBookParser -AddressBookParser ..> XYZCommand : creates > +LogicManager -right->"1" RealTimeParser +RealTimeParser ..> XYZCommand : creates > XYZCommand -up-|> Command LogicManager .left.> Command : executes > @@ -38,7 +38,7 @@ LogicManager --> Storage Storage --[hidden] Model Command .[hidden]up.> Storage Command .right.> Model -note right of XYZCommand: XYZCommand = AddCommand, \nFindCommand, etc +note right of XYZCommand: XYZCommand = AddClientCommand, \nDeleteOfferCommand, etc Logic ..> CommandResult LogicManager .down.> CommandResult diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 4439108973a..a1261ec6283 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -5,46 +5,53 @@ skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR Package Model <>{ -Class "<>\nReadOnlyAddressBook" as ReadOnlyAddressBook +Class "<>\nReadOnlyRealTime" as ReadOnlyRealTime Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs Class "<>\nModel" as Model -Class AddressBook +Class RealTime Class ModelManager Class UserPrefs -Class UniquePersonList -Class Person -Class Address -Class Email -Class Name -Class Phone -Class Tag +Class UniqueClientList +Class UniqueOfferList +Class UniqueListingList +Class UniqueMeetingList + +Class Client +Class Offer +Class Meeting +Class Listing } Class HiddenOutside #FFFFFF HiddenOutside ..> Model -AddressBook .up.|> ReadOnlyAddressBook +RealTime .up.|> ReadOnlyRealTime -ModelManager .up.|> Model +ModelManager .up....|> Model Model .right.> ReadOnlyUserPrefs -Model .left.> ReadOnlyAddressBook -ModelManager -left-> "1" AddressBook +Model .left.> ReadOnlyRealTime +ModelManager -up-> "1" RealTime ModelManager -right-> "1" UserPrefs -UserPrefs .up.|> ReadOnlyUserPrefs - -AddressBook *--> "1" UniquePersonList -UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag - -Name -[hidden]right-> Phone -Phone -[hidden]right-> Address -Address -[hidden]right-> Email - -ModelManager -->"~* filtered" Person +UserPrefs .up....|> ReadOnlyUserPrefs + +RealTime *--> "1" UniqueClientList +RealTime *--> "1" UniqueOfferList +RealTime *--> "1" UniqueListingList +RealTime *--> "1" UniqueMeetingList + +UniqueClientList -down-> "~* all" Client +UniqueOfferList -down-> "~* all" Offer +UniqueListingList -down-> "~* all" Listing +UniqueMeetingList -down-> "~* all" Meeting + +ModelManager -up->"~* filtered" Client +ModelManager -up->"~* filtered" Offer +ModelManager -up->"~* filtered" Listing +ModelManager -up->"~* filtered" Meeting + +UniqueClientList -right[hidden]- UniqueOfferList +UniqueOfferList -right[hidden]- UniqueListingList +UniqueListingList -right[hidden]- UniqueMeetingList @enduml diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml index 0c7424de6e0..418b0780a1e 100644 --- a/docs/diagrams/ParserClasses.puml +++ b/docs/diagrams/ParserClasses.puml @@ -9,7 +9,7 @@ Class XYZCommand package "Parser classes"{ Class "<>\nParser" as Parser -Class AddressBookParser +Class RealTimeParser Class XYZCommandParser Class CliSyntax Class ParserUtil @@ -19,12 +19,12 @@ Class Prefix } Class HiddenOutside #FFFFFF -HiddenOutside ..> AddressBookParser +HiddenOutside ..> RealTimeParser -AddressBookParser .down.> XYZCommandParser: creates > +RealTimeParser .down.> XYZCommandParser: creates > XYZCommandParser ..> XYZCommand : creates > -AddressBookParser ..> Command : returns > +RealTimeParser ..> Command : returns > XYZCommandParser .up.|> Parser XYZCommandParser ..> ArgumentMultimap XYZCommandParser ..> ArgumentTokenizer diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 760305e0e58..e3282bdf9a2 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -14,12 +14,14 @@ Class JsonUserPrefsStorage Class "<>\nStorage" as Storage Class StorageManager -package "AddressBook Storage" #F4F6F6{ -Class "<>\nAddressBookStorage" as AddressBookStorage -Class JsonAddressBookStorage -Class JsonSerializableAddressBook -Class JsonAdaptedPerson +package "RealTime Storage" #F4F6F6{ +Class "<>\nRealTimeStorage" as RealTimeStorage +Class JsonRealTimeStorage +Class JsonSerializableRealTime +Class JsonAdaptedClient Class JsonAdaptedTag +Class JsonAdaptedOffer +Class JsonAdaptedListing } } @@ -29,15 +31,18 @@ HiddenOutside ..> Storage StorageManager .up.|> Storage StorageManager -up-> "1" UserPrefsStorage -StorageManager -up-> "1" AddressBookStorage +StorageManager -up-> "1" RealTimeStorage Storage -left-|> UserPrefsStorage -Storage -right-|> AddressBookStorage +Storage -right-|> RealTimeStorage JsonUserPrefsStorage .up.|> UserPrefsStorage -JsonAddressBookStorage .up.|> AddressBookStorage -JsonAddressBookStorage ..> JsonSerializableAddressBook -JsonSerializableAddressBook --> "*" JsonAdaptedPerson -JsonAdaptedPerson --> "*" JsonAdaptedTag +JsonRealTimeStorage .up.|> RealTimeStorage +JsonRealTimeStorage ..> JsonSerializableRealTime +JsonSerializableRealTime --> "*" JsonAdaptedClient +JsonSerializableRealTime --> "*" JsonAdaptedOffer +JsonSerializableRealTime --> "*" JsonAdaptedListing +JsonAdaptedClient --> "*" JsonAdaptedTag +JsonAdaptedListing --> "*" JsonAdaptedTag @enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 95473d5aa19..4075e8d2ed0 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -11,8 +11,14 @@ Class UiManager Class MainWindow Class HelpWindow Class ResultDisplay -Class PersonListPanel -Class PersonCard +Class ClientListPanel +Class ClientCard +Class OfferListPanel +Class OfferCard +Class ListingListPanel +Class ListingCard +Class MeetingListPanel +Class MeetingCard Class StatusBarFooter Class CommandBox } @@ -32,26 +38,44 @@ UiManager .left.|> Ui UiManager -down-> "1" MainWindow MainWindow *-down-> "1" CommandBox MainWindow *-down-> "1" ResultDisplay -MainWindow *-down-> "1" PersonListPanel +MainWindow *-down-> "1" ClientListPanel +MainWindow *-down-> "1" OfferListPanel +MainWindow *-down-> "1" ListingListPanel +MainWindow *-down-> "1" MeetingListPanel MainWindow *-down-> "1" StatusBarFooter MainWindow --> "0..1" HelpWindow -PersonListPanel -down-> "*" PersonCard +ClientListPanel -down---> "*" ClientCard +OfferListPanel -down---> "*" OfferCard +ListingListPanel -down--> "*" ListingCard +MeetingListPanel -down--> "*" MeetingCard MainWindow -left-|> UiPart ResultDisplay --|> UiPart CommandBox --|> UiPart -PersonListPanel --|> UiPart -PersonCard --|> UiPart +ClientListPanel --|> UiPart +ClientCard --|> UiPart StatusBarFooter --|> UiPart HelpWindow --|> UiPart +OfferListPanel --|> UiPart +OfferCard --|> UiPart +ListingListPanel --|> UiPart +ListingCard --|> UiPart +MeetingListPanel --|> UiPart +MeetingCard --|> UiPart -PersonCard ..> Model + +ClientCard ...> Model +OfferCard ..> Model +ListingCard ..> Model +MeetingCard ..> Model UiManager -right-> Logic MainWindow -left-> Logic -PersonListPanel -[hidden]left- HelpWindow +ClientListPanel -[hidden]left- HelpWindow +ListingListPanel -[hidden]left- ResultDisplay +MeetingListPanel -[hidden]left- ResultDisplay HelpWindow -[hidden]left- CommandBox CommandBox -[hidden]left- ResultDisplay ResultDisplay -[hidden]left- StatusBarFooter diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml index 96e30744d24..5e5cd8b223e 100644 --- a/docs/diagrams/UndoRedoState0.puml +++ b/docs/diagrams/UndoRedoState0.puml @@ -6,9 +6,9 @@ skinparam ClassBorderColor #000000 title Initial state package States { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ab0:RealTime__" + class State2 as "__ab1:RealTime__" + class State3 as "__ab2:RealTime__" } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 diff --git a/docs/diagrams/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml index 01fcb9b2b96..0bb29a2b6b1 100644 --- a/docs/diagrams/UndoRedoState1.puml +++ b/docs/diagrams/UndoRedoState1.puml @@ -6,9 +6,9 @@ skinparam ClassBorderColor #000000 title After command "delete 5" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ab0:RealTime__" + class State2 as "__ab1:RealTime__" + class State3 as "__ab2:RealTime__" } State1 -[hidden]right-> State2 diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml index bccc230a5d1..f6cd6b68fb8 100644 --- a/docs/diagrams/UndoRedoState2.puml +++ b/docs/diagrams/UndoRedoState2.puml @@ -6,9 +6,9 @@ skinparam ClassBorderColor #000000 title After command "add n/David" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ab0:RealTime__" + class State2 as "__ab1:RealTime__" + class State3 as "__ab2:RealTime__" } State1 -[hidden]right-> State2 diff --git a/docs/diagrams/UndoRedoState3.puml b/docs/diagrams/UndoRedoState3.puml index ea29c9483e4..920a94c9827 100644 --- a/docs/diagrams/UndoRedoState3.puml +++ b/docs/diagrams/UndoRedoState3.puml @@ -6,9 +6,9 @@ skinparam ClassBorderColor #000000 title After command "undo" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ab0:RealTime__" + class State2 as "__ab1:RealTime__" + class State3 as "__ab2:RealTime__" } State1 -[hidden]right-> State2 diff --git a/docs/diagrams/UndoRedoState4.puml b/docs/diagrams/UndoRedoState4.puml index 1b784cece80..97c3b052ef1 100644 --- a/docs/diagrams/UndoRedoState4.puml +++ b/docs/diagrams/UndoRedoState4.puml @@ -6,9 +6,9 @@ skinparam ClassBorderColor #000000 title After command "list" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__ab0:RealTime__" + class State2 as "__ab1:RealTime__" + class State3 as "__ab2:RealTime__" } State1 -[hidden]right-> State2 diff --git a/docs/diagrams/UndoRedoState5.puml b/docs/diagrams/UndoRedoState5.puml index 88927be32bc..c7b2d161c61 100644 --- a/docs/diagrams/UndoRedoState5.puml +++ b/docs/diagrams/UndoRedoState5.puml @@ -6,9 +6,9 @@ skinparam ClassBorderColor #000000 title After command "clear" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab3:AddressBook__" + class State1 as "__ab0:RealTime__" + class State2 as "__ab1:RealTime__" + class State3 as "__ab3:RealTime__" } State1 -[hidden]right-> State2 diff --git a/docs/diagrams/UndoSequenceDiagram.puml b/docs/diagrams/UndoSequenceDiagram.puml index 410aab4e412..e6e2ed03135 100644 --- a/docs/diagrams/UndoSequenceDiagram.puml +++ b/docs/diagrams/UndoSequenceDiagram.puml @@ -3,42 +3,42 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":RealTimeParser" as RealTimeParser LOGIC_COLOR participant "u:UndoCommand" as UndoCommand LOGIC_COLOR end box box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR -participant ":VersionedAddressBook" as VersionedAddressBook MODEL_COLOR +participant ":VersionedRealTime" as VersionedRealTime MODEL_COLOR end box [-> LogicManager : execute(undo) activate LogicManager -LogicManager -> AddressBookParser : parseCommand(undo) -activate AddressBookParser +LogicManager -> RealTimeParser : parseCommand(undo) +activate RealTimeParser create UndoCommand -AddressBookParser -> UndoCommand +RealTimeParser -> UndoCommand activate UndoCommand -UndoCommand --> AddressBookParser +UndoCommand --> RealTimeParser deactivate UndoCommand -AddressBookParser --> LogicManager : u -deactivate AddressBookParser +RealTimeParser --> LogicManager : u +deactivate RealTimeParser LogicManager -> UndoCommand : execute() activate UndoCommand -UndoCommand -> Model : undoAddressBook() +UndoCommand -> Model : undoRealTime() activate Model -Model -> VersionedAddressBook : undo() -activate VersionedAddressBook +Model -> VersionedRealTime : undo() +activate VersionedRealTime -VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook) -VersionedAddressBook --> Model : -deactivate VersionedAddressBook +VersionedRealTime -> VersionedRealTime :resetData(ReadOnlyRealTime) +VersionedRealTime --> Model : +deactivate VersionedRealTime Model --> UndoCommand deactivate Model diff --git a/docs/diagrams/tracing/LogicSequenceDiagram.puml b/docs/diagrams/tracing/LogicSequenceDiagram.puml index fdcbe1c0ccc..8d5243b72cf 100644 --- a/docs/diagrams/tracing/LogicSequenceDiagram.puml +++ b/docs/diagrams/tracing/LogicSequenceDiagram.puml @@ -2,7 +2,7 @@ !include ../style.puml Participant ":LogicManager" as logic LOGIC_COLOR -Participant ":AddressBookParser" as abp LOGIC_COLOR +Participant ":RealTimeParser" as abp LOGIC_COLOR Participant ":EditCommandParser" as ecp LOGIC_COLOR Participant "command:EditCommand" as ec LOGIC_COLOR diff --git a/docs/images/AddClientSequenceDiagram.png b/docs/images/AddClientSequenceDiagram.png new file mode 100644 index 00000000000..9fbfaf53870 Binary files /dev/null and b/docs/images/AddClientSequenceDiagram.png differ diff --git a/docs/images/AddListingSequenceDiagram.png b/docs/images/AddListingSequenceDiagram.png new file mode 100644 index 00000000000..72d754d8916 Binary files /dev/null and b/docs/images/AddListingSequenceDiagram.png differ diff --git a/docs/images/AddMeetingSequenceDiagram.png b/docs/images/AddMeetingSequenceDiagram.png new file mode 100644 index 00000000000..a4c3fe33da8 Binary files /dev/null and b/docs/images/AddMeetingSequenceDiagram.png differ diff --git a/docs/images/AddOfferFailure1.png b/docs/images/AddOfferFailure1.png new file mode 100644 index 00000000000..a246d0f3340 Binary files /dev/null and b/docs/images/AddOfferFailure1.png differ diff --git a/docs/images/AddOfferFailure2.png b/docs/images/AddOfferFailure2.png new file mode 100644 index 00000000000..ea60f4824a2 Binary files /dev/null and b/docs/images/AddOfferFailure2.png differ diff --git a/docs/images/AddOfferSequenceDiagram.png b/docs/images/AddOfferSequenceDiagram.png new file mode 100644 index 00000000000..e0e4f36ecd0 Binary files /dev/null and b/docs/images/AddOfferSequenceDiagram.png differ diff --git a/docs/images/AddOfferSuccess.png b/docs/images/AddOfferSuccess.png new file mode 100644 index 00000000000..06827177392 Binary files /dev/null and b/docs/images/AddOfferSuccess.png differ diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 2f1346869d0..b419b93b7d7 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png index 1ec62caa2a5..5bfd3db53af 100644 Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ diff --git a/docs/images/DeleteClientSequenceDiagram.png b/docs/images/DeleteClientSequenceDiagram.png new file mode 100644 index 00000000000..bc44a45ee1c Binary files /dev/null and b/docs/images/DeleteClientSequenceDiagram.png differ diff --git a/docs/images/DeleteListingSequenceDiagram.png b/docs/images/DeleteListingSequenceDiagram.png new file mode 100644 index 00000000000..844afb9538b Binary files /dev/null and b/docs/images/DeleteListingSequenceDiagram.png differ diff --git a/docs/images/DeleteMeetingSequenceDiagram.png b/docs/images/DeleteMeetingSequenceDiagram.png new file mode 100644 index 00000000000..ca0a53dd0e3 Binary files /dev/null and b/docs/images/DeleteMeetingSequenceDiagram.png differ diff --git a/docs/images/DeleteOfferInvalid1.png b/docs/images/DeleteOfferInvalid1.png new file mode 100644 index 00000000000..feca9ccbc5e Binary files /dev/null and b/docs/images/DeleteOfferInvalid1.png differ diff --git a/docs/images/DeleteOfferInvalid2.png b/docs/images/DeleteOfferInvalid2.png new file mode 100644 index 00000000000..124f5453267 Binary files /dev/null and b/docs/images/DeleteOfferInvalid2.png differ diff --git a/docs/images/DeleteOfferSequenceDiagram.png b/docs/images/DeleteOfferSequenceDiagram.png new file mode 100644 index 00000000000..40c25e87c63 Binary files /dev/null and b/docs/images/DeleteOfferSequenceDiagram.png differ diff --git a/docs/images/DeleteOfferSuccess.png b/docs/images/DeleteOfferSuccess.png new file mode 100644 index 00000000000..3a811a7ebf8 Binary files /dev/null and b/docs/images/DeleteOfferSuccess.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index fa327b39618..25f85837212 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/EditOfferInvalidIndex.png b/docs/images/EditOfferInvalidIndex.png new file mode 100644 index 00000000000..13f2ffab8dd Binary files /dev/null and b/docs/images/EditOfferInvalidIndex.png differ diff --git a/docs/images/EditOfferInvalidName.png b/docs/images/EditOfferInvalidName.png new file mode 100644 index 00000000000..e97cd49fe54 Binary files /dev/null and b/docs/images/EditOfferInvalidName.png differ diff --git a/docs/images/EditOfferSuccess.png b/docs/images/EditOfferSuccess.png new file mode 100644 index 00000000000..9e09b31f01f Binary files /dev/null and b/docs/images/EditOfferSuccess.png differ diff --git a/docs/images/GUI.png b/docs/images/GUI.png new file mode 100644 index 00000000000..06e0f01e563 Binary files /dev/null and b/docs/images/GUI.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index 9e9ba9f79e5..0908f0bbccb 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 04070af60d8..425109de33c 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/OfferIndex.png b/docs/images/OfferIndex.png new file mode 100644 index 00000000000..ec7f9f376b0 Binary files /dev/null and b/docs/images/OfferIndex.png differ diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png index e7b4c8880cd..e82443dd4ec 100644 Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 2533a5c1af0..4e58d538193 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5bd77847aa2..bbbb7ee6086 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png index 785e04dbab4..8681e022fbc 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/addL.png b/docs/images/addL.png new file mode 100644 index 00000000000..368228966cd Binary files /dev/null and b/docs/images/addL.png differ diff --git a/docs/images/addLMissingParam.png b/docs/images/addLMissingParam.png new file mode 100644 index 00000000000..4abe7894eb1 Binary files /dev/null and b/docs/images/addLMissingParam.png differ diff --git a/docs/images/addM_example.png b/docs/images/addM_example.png new file mode 100644 index 00000000000..809f642a9ac Binary files /dev/null and b/docs/images/addM_example.png differ diff --git a/docs/images/addM_invalid.png b/docs/images/addM_invalid.png new file mode 100644 index 00000000000..3dcb6074eec Binary files /dev/null and b/docs/images/addM_invalid.png differ diff --git a/docs/images/addclientbetsy.png b/docs/images/addclientbetsy.png new file mode 100644 index 00000000000..d2016c4805e Binary files /dev/null and b/docs/images/addclientbetsy.png differ diff --git a/docs/images/addclientjohndoe.png b/docs/images/addclientjohndoe.png new file mode 100644 index 00000000000..35d53f89f76 Binary files /dev/null and b/docs/images/addclientjohndoe.png differ diff --git a/docs/images/addclientmissing.png b/docs/images/addclientmissing.png new file mode 100644 index 00000000000..79d96d9ceb4 Binary files /dev/null and b/docs/images/addclientmissing.png differ diff --git a/docs/images/ama-chi.png b/docs/images/ama-chi.png new file mode 100644 index 00000000000..6ec63780d24 Binary files /dev/null and b/docs/images/ama-chi.png differ diff --git a/docs/images/delL.png b/docs/images/delL.png new file mode 100644 index 00000000000..d6c5e677537 Binary files /dev/null and b/docs/images/delL.png differ diff --git a/docs/images/delLInvalidId.png b/docs/images/delLInvalidId.png new file mode 100644 index 00000000000..d65b922f971 Binary files /dev/null and b/docs/images/delLInvalidId.png differ diff --git a/docs/images/delM_invalid.png b/docs/images/delM_invalid.png new file mode 100644 index 00000000000..ddecfe6bab7 Binary files /dev/null and b/docs/images/delM_invalid.png differ diff --git a/docs/images/deleteLIndex.png b/docs/images/deleteLIndex.png new file mode 100644 index 00000000000..0fa1688e129 Binary files /dev/null and b/docs/images/deleteLIndex.png differ diff --git a/docs/images/deleteM_example.png b/docs/images/deleteM_example.png new file mode 100644 index 00000000000..293f0cd6149 Binary files /dev/null and b/docs/images/deleteM_example.png differ diff --git a/docs/images/deleteclient.png b/docs/images/deleteclient.png new file mode 100644 index 00000000000..5b0b4c7020a Binary files /dev/null and b/docs/images/deleteclient.png differ diff --git a/docs/images/downloadRelease.png b/docs/images/downloadRelease.png new file mode 100644 index 00000000000..eee241881bb Binary files /dev/null and b/docs/images/downloadRelease.png differ diff --git a/docs/images/editL.png b/docs/images/editL.png new file mode 100644 index 00000000000..0b150192625 Binary files /dev/null and b/docs/images/editL.png differ diff --git a/docs/images/editLIndexOutOfBounds.png b/docs/images/editLIndexOutOfBounds.png new file mode 100644 index 00000000000..eb5b70be0af Binary files /dev/null and b/docs/images/editLIndexOutOfBounds.png differ diff --git a/docs/images/editLNoChange.png b/docs/images/editLNoChange.png new file mode 100644 index 00000000000..74885a7f5f1 Binary files /dev/null and b/docs/images/editLNoChange.png differ diff --git a/docs/images/editM_example.png b/docs/images/editM_example.png new file mode 100644 index 00000000000..f8b5b214744 Binary files /dev/null and b/docs/images/editM_example.png differ diff --git a/docs/images/editM_invalid.png b/docs/images/editM_invalid.png new file mode 100644 index 00000000000..accb2a4a342 Binary files /dev/null and b/docs/images/editM_invalid.png differ diff --git a/docs/images/editM_nofields.png b/docs/images/editM_nofields.png new file mode 100644 index 00000000000..ce0b4464da0 Binary files /dev/null and b/docs/images/editM_nofields.png differ diff --git a/docs/images/editbetsy.png b/docs/images/editbetsy.png new file mode 100644 index 00000000000..36b9d1027ca Binary files /dev/null and b/docs/images/editbetsy.png differ diff --git a/docs/images/editclient.png b/docs/images/editclient.png new file mode 100644 index 00000000000..25bf8f806ad Binary files /dev/null and b/docs/images/editclient.png differ diff --git a/docs/images/editedL.png b/docs/images/editedL.png new file mode 100644 index 00000000000..8debf238107 Binary files /dev/null and b/docs/images/editedL.png differ diff --git a/docs/images/editmissingfield.png b/docs/images/editmissingfield.png new file mode 100644 index 00000000000..4c588e842c8 Binary files /dev/null and b/docs/images/editmissingfield.png differ diff --git a/docs/images/findclient.png b/docs/images/findclient.png new file mode 100644 index 00000000000..fd6568d7468 Binary files /dev/null and b/docs/images/findclient.png differ diff --git a/docs/images/findclientinvalid.png b/docs/images/findclientinvalid.png new file mode 100644 index 00000000000..837ce23fd74 Binary files /dev/null and b/docs/images/findclientinvalid.png differ diff --git a/docs/images/gavzzz.png b/docs/images/gavzzz.png new file mode 100644 index 00000000000..4be21722c00 Binary files /dev/null and b/docs/images/gavzzz.png differ diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png index b1f70470137..d96c99211a3 100644 Binary files a/docs/images/helpMessage.png and b/docs/images/helpMessage.png differ diff --git a/docs/images/hoang227.png b/docs/images/hoang227.png new file mode 100644 index 00000000000..1776a083e46 Binary files /dev/null and b/docs/images/hoang227.png differ diff --git a/docs/images/invalidclientname.png b/docs/images/invalidclientname.png new file mode 100644 index 00000000000..e095ab72c21 Binary files /dev/null and b/docs/images/invalidclientname.png differ diff --git a/docs/images/invaliddelete.png b/docs/images/invaliddelete.png new file mode 100644 index 00000000000..fcf36ce8798 Binary files /dev/null and b/docs/images/invaliddelete.png differ diff --git a/docs/images/invaliddeleteindex.png b/docs/images/invaliddeleteindex.png new file mode 100644 index 00000000000..0035326d352 Binary files /dev/null and b/docs/images/invaliddeleteindex.png differ diff --git a/docs/images/isaaclhy00.png b/docs/images/isaaclhy00.png new file mode 100644 index 00000000000..1341a7c0633 Binary files /dev/null and b/docs/images/isaaclhy00.png differ diff --git a/docs/images/jeromehjj.png b/docs/images/jeromehjj.png new file mode 100644 index 00000000000..10c3f72a7dd Binary files /dev/null and b/docs/images/jeromehjj.png differ diff --git a/docs/images/layout.png b/docs/images/layout.png new file mode 100644 index 00000000000..710ab0bea07 Binary files /dev/null and b/docs/images/layout.png differ diff --git a/docs/images/listL.png b/docs/images/listL.png new file mode 100644 index 00000000000..e04a52fe255 Binary files /dev/null and b/docs/images/listL.png differ diff --git a/docs/images/listclient.png b/docs/images/listclient.png new file mode 100644 index 00000000000..dca0f6b3c99 Binary files /dev/null and b/docs/images/listclient.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..d82a39f3518 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,19 +1,20 @@ --- layout: page -title: AddressBook Level-3 +title: REal-Time --- -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) -[![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3) +[![CI Status](https://github.com/AY2223S1-CS2103T-W15-2/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2223S1-CS2103T-W15-2/tp/actions) +[![codecov](https://codecov.io/gh/AY2223S1-CS2103T-W15-2/tp/branch/master/graph/badge.svg?token=H2G32SVMDR)](https://codecov.io/gh/AY2223S1-CS2103T-W15-2/tp) + ![Ui](images/Ui.png) -**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). +**REal-Time is a desktop application for managing your client details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). -* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). -* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. +* If you are interested in using REal-Time, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). +* If you are interested about developing REal-Time, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** - +* This project is based on the addressbook-Level3 project created by the [SE-EDU initiative](https://se-education.org). * Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5) diff --git a/docs/team/ama-chi.md b/docs/team/ama-chi.md new file mode 100644 index 00000000000..c165378dfac --- /dev/null +++ b/docs/team/ama-chi.md @@ -0,0 +1,34 @@ +## Overview + +### Summary of Contributions + +**Code contributed:** [RepoSense Link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=ama-chi&breakdown=true&sort=groupTitle&sortWithin=title&since=2022-09-16&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + +**Enhancements implemented:** +* Implemented a Meeting class to encapsulate a Meeting with a Client, Listing and Date/Time. + * Implemented `AddMeetingCommand`,`AddMeetingCommandParser`, + * `EditMeetingCommand`, `EditMeetingCommandParser` and + * `DeleteMeetingCommand`, `DeleteMeetingCommandParser` functions for the Meeting class. + + * What it does: + * Allows the user to create, edit and remove meetings. + +**Contributions to the UG:** +* Edited formatting to ensure consistency across the User Guide. +* Styling and visual touch ups. + +**Contributions to the DG:** +* Cleaning and formatting. + +**Contributions to team based tasks:** +* Created Milestones `v1.1`, `v1.2`, `v1.2b`, `v1.3` and `v1.4` and their respective deadlines. +* Created Issues and assigned them to respective milestones. +* Wrote Test cases for Meeting class. + +**Review/mentoring contributions:** +* Reviewed and Merged Pull Request. +* Handled merge conflicts. +* Discussions on weekly distribution of work. + +**Contributions beyond the project team:** +* Bug reports for other teams. diff --git a/docs/team/gavzzz.md b/docs/team/gavzzz.md new file mode 100644 index 00000000000..adf831d1f39 --- /dev/null +++ b/docs/team/gavzzz.md @@ -0,0 +1,43 @@ +--- +layout: page +title: Gavzzz Project Portfolio Page +--- + +### Project: REal-Time + +REal-Time is a desktop application for _Real-Estate agents_ to manage client information, schedule meetings, +and track client offers and listings. + +Given below are my contributions to the project. + +* **New Feature**: Added ability to add, delete, edit and find clients. (Pull request [#80](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/80)) + * What it does: allows the user to add new clients to the address book, edit their information, find the client and +delete the client if they want to. + * Justification: This feature improves the product significantly because as a real estate agent, they need to manage +their clients and allows them to freely manipulate their clients information. + * Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. As the Person class was too generic, having a Client class would be more specific and +future commands would make use of the Client class instead. + + +**Code contributed:** [RepoSense Link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=gavzzz&breakdown=true&sort=groupTitle&sortWithin=title&since=2022-09-16&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + + +* **Contributions to team based tasks:**: + * Managed release `v1.3` (1 release) on GitHub + * Updated the read.md Ui picture + * facilitated meetings + +**Enhancements to existing features:** +* Implemented GUI panels for Client, Listing and Meeting (Pull requests [\#93](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/93)) +* Wrote additional tests for existing features for the client class. (Pull requests [\#178](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/178)) +* Fixed bugs related to other members pull requests (Pull request [#80](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/80)) + +**Documentation**: + * User Guide: + * Added documentation for the features `addC`, `editC`, `deleteC` and `findC` (Pull request [#178](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/178)) + + * Developer Guide: + * Added implementation details of the `delete` feature. + +**Review/mentoring contributions:** + * PRs reviewed (with non-trivial review comments): [\#55](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/55), [\#60](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/60), [\#86](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/86) diff --git a/docs/team/hoang227.md b/docs/team/hoang227.md new file mode 100644 index 00000000000..1e72ac21a73 --- /dev/null +++ b/docs/team/hoang227.md @@ -0,0 +1,98 @@ +## Overview +### Summary of Contributions + +**Code contributed:** [RepoSense Link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=ama-chi&breakdown=true&sort=groupTitle&sortWithin=title&since=2022-09-16&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + +**Enhancements implemented:** + +* New Feature: `AddListingCommand` and `AddListingCommandParser` ([#39](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/39), + [#44](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/44), [#60](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/60)) + * What it does: + * Allows users to add listings to Real-Time. + * Highlight: + * Implemented `Listing` Class ([#39](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/39)) + with a new field `ListingId`([#60](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/60)) so that the user can add id to their listings. + * Added `parseListingId`([#60](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/60)) in `ParserUtil` class to parse ListingId. + +* New Feature: Auto-sorting mechanism for all lists ([#60](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/60), [#84](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/84)) + * What it does: + * Automatically sort the lists when a new entry is added. + * Highlight: + * Implemented the sorting mechanism in `add` method in `UniqueClientList`, `UniqueListingList`, + `UniqueMeetingList` and `UniqueOfferList` ([#60](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/60), [#84](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/84)) + * Added `compareTo` to `Client`, `Listing`, `Meeting` and `Offer` classes so + `UniqueClientList`, `UniqueListingList`, `UniqueMeetingList` and `UniqueOfferList` can + sort the list everytime a new entry is added. ([#60](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/60), [#84](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/84)) + +* New Feature: Cascading-delete mechanism for lists. ([#89](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/89)) + * What it does: + * Deleting entries that have related entries from other lists will have a cascading effect. + * Highlight: + * `DeleteClientCommand` will also delete the listings, meetings and offers related to the `Client` that is deleted. + * `DeleteListingCommand` will also delete the offers and meetings related to the `Listing` that is deleted. + +**Contributions to the UG:** +* Added documentation for features: + * Client-related + * Adding, Editing, Finding and Listing + * Listing-related + * Adding, Editing and Listing + * Meeting-related + * Adding +* Enchance the visual aspect and readability for entire UG. +* Added Command Format section to help user learn the commands. +* Relevant pull request(s): [#94](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/94), + [#165](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/165), [#169](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/169), [#173](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/173) + +**Contributions to the DG:** +* Added Implementation details for features with UML sequence diagrams: + * Client-related + * Add, Edit, Delete + * Listing-related + * Add, Edit, Delete + * Meeting-related + * Add, Edit, Delete +* Added Use cases for Offer related features: + * Client-related + * Add, Edit, Delete + * Listing-related + * Add, Edit, Delete + * Meeting-related + * Add, Edit, Delete +* Relevant pull request(s): [#176](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/176), [#180](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/180) + + +**Contributions to team based tasks:** +* Write DG implementation for Client, Listing and Meeting related command. +* Write use cases and user stories for DG. +* Create app's Logo. +* Facilitate meetings. +* Assigning work among the group. +* Refactoring the project to match the product name ([#161](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/161)) +* Format the UG to increase readability. ([#169](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/169), +[#173](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/173)) +* Added Test Cases ([#150](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/150), [#161](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/161)): + * Client-Related + * DeleteClientCommandTest + * EditClientCommandTest + * AddClientCommandTest + * FindClientCommandTest + * ClientTest + * ViewClientListCommandTest + * JsonAdaptedClientTest + * Listing Related + * DeleteListingCommandTest + * EditListingCommandTest + * ViewListingsCommandTest + * EditListingDescriptorTest + * JsonAdaptedListingTest + * AddListingCommandTest + * Offer-Related + * JsonAdaptedOfferTest + +**Review/mentoring contributions:** +* Merging and Review PRs +* Discussion providing feedback to teammates on implementation of app architecture. + +**Contributions beyond the project team:** +* Bugs reported in other team's product: [Link to PED](https://github.com/hoang227/ped) diff --git a/docs/team/isaaclhy00.md b/docs/team/isaaclhy00.md new file mode 100644 index 00000000000..760ec9bc22f --- /dev/null +++ b/docs/team/isaaclhy00.md @@ -0,0 +1,37 @@ +### Overview +REal-Time is a one-stop desktop app for real-estate agents to manage their work. +It aims to help real-estate agents better organise their listings, clients and offers. +REal-Time make suse of a simple Command Line Interface (CLI) +and displays data via a Graphical User Interface (GUI) created with JavaFX. +It is written in Java, and has about 30 kLoC. + +### Summary of Contributions + +**Code contributed:** Up until 7 November 2022, +I have contributed 1786 LoC (roughly 4,425 additions and 2,434 deletions), +and pushed 56 commits. For most recent update, +refer to this [RepoSense Link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=isaaclhy00&breakdown=true&sort=groupTitle&sortWithin=title&since=2022-09-16&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + +**Enhancements implemented:** +* Added `Add`, `Edit`, `Delete`, and `Find` features for listings. +* Added `Add` and `View` offers and clients tagged to the listings. + +**Contributions to the UG:** +* Added `addL` feature description. +* Added `editL` feature description. +* Added `delL` feature description. +* Added links for navigation. + +**Contributions to the DG:** +* Added rationale for features. +* Added user stories for `Listing` related features. + +**Contributions to team based tasks:** +* Added `Listing` class and its features +* Added product demonstration + +**Review/mentoring contributions:** +* weekly review and merging of peer PRs (with simple review comments) + +**Contributions beyond the project team:** +* Reported 9 bugs and suggestions for other teams. diff --git a/docs/team/jeromehjj.md b/docs/team/jeromehjj.md new file mode 100644 index 00000000000..f9edb0aa24e --- /dev/null +++ b/docs/team/jeromehjj.md @@ -0,0 +1,80 @@ +--- +layout: page +title: Jerome Hoo Jun Jie's Project Portfolio Page +--- + +## Overview + +**REal-Time** is a desktop application made for **Real-Estate agents** to **manage client information, +schedule meetings, and track client offers and listings.** + +It is written in Java and uses JavaFX to build the GUI for the application. + +Given below are my contributions to the project. + +## Summary of Contributions + +* **Code contributed:** [RepoSense Link](https://nus-cs2103-ay2223s1.github.io/tp-dashboard/?search=jeromehjj&breakdown=true&sort=groupTitle&sortWithin=title&since=2022-09-16&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + + +* **Enhancements implemented:** + * **New Feature:** `AddOfferCommand` and `AddOfferCommandParser`(Pull request [#46](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/46)) + * What it does: + * Allows users to add their offers made for a listing in the application. + * Highlights: + * Implemented the `Offer` class with new `Price` field so that prices can be added into an offer by + the user. Added a `AddOfferCommandParser` to parse the input by the user when adding offers. + * Added `parsePrice` in `ParserUtil` class to parse prices that are valid + * Implemented an auto-sorting feature when adding offers so that it is sorted by the `listingId` field. + * Added necessary tests in `AddOfferCommandTest`, `AddOfferCommandParserTest`, `ParserUtilTest` and `PriceTest`(Pull request [#163](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/163)) + + * **New Feature:** `EditOfferCommand` and `EditOfferCommandParser`(Pull request [#81](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/81)) + * What it does: + * Allows users to modify their offers when needed + * Highlights: + * Implemented the `EditOfferDescriptor` class to create the `Offer` that is being edited to replace the targeted `Offer` + * Added necessary tests in `EditOfferCommandTest`, `EditOfferCommandParserTest`, and `EditOfferDescriptorTest`(Pull request [#163](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/163)) + + * **New Feature:** `DeleteOfferCommand` and `DeleteOfferCommandParser`(Pull request [#81](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/81)) + * What it does: + * Allows users to delete their offers in the application + * Highlights: + * Added necessary tests in `DeleteOfferCommandTest` and `DeleteOfferCommandParserTest`(Pull request [#163](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/163)) + + +* **Contributions to the User Guide:** + * Added documentation for features: Adding an Offer, Editing an Offer, Deleting an Offer + * Implemented the Glossary table + * Implemented the Prefix Summary table with hyperlinks and details + * Updated the Command Summary table with hyperlinks and details + * Relevant pull request(s): [#163](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/163), + [#155](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/155), + [#87](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/87), + [#74](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/74) + + + +* **Contributions to the Developer Guide:** + * Added **Implementation** details for features with UML sequence diagrams: **Add Offer, Edit Offer, Delete Offer** + * Added **Use cases** for Offer related features: **Add an Offer, Edit an Offer, Delete an Offer** + * Relevant pull request(s): [#163](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/163), + [#74](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/74) + + +* **Contributions to team based tasks:** + * Maintaining issue tracker on Github + * Release management for `v1.3` + * Updated UML Class and Sequence Diagrams in **Design** section to match current architecture of product([#163](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/163)) + * Enabled assertions + * Renamed User Guide to match product([#42](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/42)) + + +* **Review/mentoring contributions:** + * Pull requests reviewed with comments: [#161](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/161), + [#69](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/69), + [#60](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/60), + [#45](https://github.com/AY2223S1-CS2103T-W15-2/tp/pull/45) + + +* **Contributions beyond the project team:** + * Bugs reported in other team's product: [Link to PED](https://github.com/jeromehjj/ped/issues) diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md index 880c701042f..d367cab4ccd 100644 --- a/docs/tutorials/AddRemark.md +++ b/docs/tutorials/AddRemark.md @@ -23,9 +23,9 @@ For now, let’s keep `RemarkCommand` as simple as possible and print some outpu **`RemarkCommand.java`:** ``` java -package seedu.address.logic.commands; +package seedu.addressbook.logic.commands; -import seedu.address.model.Model; +import seedu.addressbook.model.Model; /** * Changes the remark of an existing person in the address book. @@ -91,7 +91,7 @@ Let’s change `RemarkCommand` to parse input from the user. We start by modifying the constructor of `RemarkCommand` to accept an `Index` and a `String`. While we are at it, let’s change the error message to echo the values. While this is not a replacement for tests, it is an obvious way to tell if our code is functioning as intended. ``` java -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.addressbook.commons.util.CollectionUtil.requireAllNonNull; //... public class RemarkCommand extends Command { //... @@ -142,7 +142,7 @@ Your code should look something like [this](https://github.com/se-edu/addressboo Now let’s move on to writing a parser that will extract the index and remark from the input provided by the user. -Create a `RemarkCommandParser` class in the `seedu.address.logic.parser` package. The class must extend the `Parser` interface. +Create a `RemarkCommandParser` class in the `seedu.addressbook.logic.parser` package. The class must extend the `Parser` interface. ![The relationship between Parser and RemarkCommandParser](../images/add-remark/ParserInterface.png) @@ -229,7 +229,7 @@ Now that we have all the information that we need, let’s lay the groundwork fo ### Add a new `Remark` class -Create a new `Remark` in `seedu.address.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. +Create a new `Remark` in `seedu.addressbook.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-41bb13c581e280c686198251ad6cc337cd5e27032772f06ed9bf7f1440995ece). Note how `Remark` has no constrains and thus does not require input validation. @@ -242,7 +242,7 @@ Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark` Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person. -Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688). +Simply add the following to [`seedu.addressbook.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688). **`PersonCard.java`:** diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md index f29169bc924..fd7d1f710ef 100644 --- a/docs/tutorials/RemovingFields.md +++ b/docs/tutorials/RemovingFields.md @@ -28,7 +28,7 @@ IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a re ### Assisted refactoring -The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. +The `address` field in `Person` is actually an instance of the `seedu.addressbook.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. * :bulb: To make things simpler, you can unselect the options `Search in comments and strings` and `Search for text occurrences` ![Usages detected](../images/remove/UnsafeDelete.png) diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md index 4fb62a83ef6..0dc84bfcc3b 100644 --- a/docs/tutorials/TracingCode.md +++ b/docs/tutorials/TracingCode.md @@ -39,7 +39,7 @@ In our case, we would want to begin the tracing at the very point where the App -According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `seedu.address.logic.Logic`. +According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `seedu.addressbook.logic.Logic`. @@ -48,7 +48,7 @@ According to the sequence diagram you saw earlier (and repeated above for refere :bulb: **Intellij Tip:** The ['**Search Everywhere**' feature](https://www.jetbrains.com/help/idea/searching-everywhere.html) can be used here. In particular, the '**Find Symbol**' ('Symbol' here refers to methods, variables, classes etc.) variant of that feature is quite useful here as we are looking for a _method_ named `execute`, not simply the text `execute`.
-A quick look at the `seedu.address.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for. +A quick look at the `seedu.addressbook.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for. ```java public interface Logic { @@ -120,7 +120,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [ CommandResult commandResult; //Parse user input from String to a Command - Command command = addressBookParser.parseCommand(commandText); + Command command = AddressBookParser.parseCommand(commandText); //Executes the Command and stores the result commandResult = command.execute(model); diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java deleted file mode 100644 index 1deb3a1e469..00000000000 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ /dev/null @@ -1,13 +0,0 @@ -package seedu.address.commons.core; - -/** - * Container for user visible messages. - */ -public class Messages { - - public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; - public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; - -} diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java deleted file mode 100644 index 92cd8fa605a..00000000000 --- a/src/main/java/seedu/address/logic/Logic.java +++ /dev/null @@ -1,50 +0,0 @@ -package seedu.address.logic; - -import java.nio.file.Path; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; - -/** - * API of the Logic component - */ -public interface Logic { - /** - * Executes the command and returns the result. - * @param commandText The command as entered by the user. - * @return the result of the command execution. - * @throws CommandException If an error occurs during command execution. - * @throws ParseException If an error occurs during parsing. - */ - CommandResult execute(String commandText) throws CommandException, ParseException; - - /** - * Returns the AddressBook. - * - * @see seedu.address.model.Model#getAddressBook() - */ - ReadOnlyAddressBook getAddressBook(); - - /** Returns an unmodifiable view of the filtered list of persons */ - ObservableList getFilteredPersonList(); - - /** - * Returns the user prefs' address book file path. - */ - Path getAddressBookFilePath(); - - /** - * Returns the user prefs' GUI settings. - */ - GuiSettings getGuiSettings(); - - /** - * Set the user prefs' GUI settings. - */ - void setGuiSettings(GuiSettings guiSettings); -} diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java deleted file mode 100644 index 9d9c6d15bdc..00000000000 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ /dev/null @@ -1,81 +0,0 @@ -package seedu.address.logic; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.AddressBookParser; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; -import seedu.address.storage.Storage; - -/** - * The main LogicManager of the app. - */ -public class LogicManager implements Logic { - public static final String FILE_OPS_ERROR_MESSAGE = "Could not save data to file: "; - private final Logger logger = LogsCenter.getLogger(LogicManager.class); - - private final Model model; - private final Storage storage; - private final AddressBookParser addressBookParser; - - /** - * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. - */ - public LogicManager(Model model, Storage storage) { - this.model = model; - this.storage = storage; - addressBookParser = new AddressBookParser(); - } - - @Override - public CommandResult execute(String commandText) throws CommandException, ParseException { - logger.info("----------------[USER COMMAND][" + commandText + "]"); - - CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); - commandResult = command.execute(model); - - try { - storage.saveAddressBook(model.getAddressBook()); - } catch (IOException ioe) { - throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); - } - - return commandResult; - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return model.getAddressBook(); - } - - @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); - } - - @Override - public Path getAddressBookFilePath() { - return model.getAddressBookFilePath(); - } - - @Override - public GuiSettings getGuiSettings() { - return model.getGuiSettings(); - } - - @Override - public void setGuiSettings(GuiSettings guiSettings) { - model.setGuiSettings(guiSettings); - } -} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java deleted file mode 100644 index 1e466792b46..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.ClearCommand; -import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.ExitCommand; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.parser.exceptions.ParseException; - -/** - * Parses user input. - */ -public class AddressBookParser { - - /** - * Used for initial separation of command word and args. - */ - private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); - - /** - * Parses user input into command for execution. - * - * @param userInput full user input string - * @return the command based on the user input - * @throws ParseException if the user input does not conform the expected format - */ - public Command parseCommand(String userInput) throws ParseException { - final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); - if (!matcher.matches()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); - } - - final String commandWord = matcher.group("commandWord"); - final String arguments = matcher.group("arguments"); - switch (commandWord) { - - case AddCommand.COMMAND_WORD: - return new AddCommandParser().parse(arguments); - - case EditCommand.COMMAND_WORD: - return new EditCommandParser().parse(arguments); - - case DeleteCommand.COMMAND_WORD: - return new DeleteCommandParser().parse(arguments); - - case ClearCommand.COMMAND_WORD: - return new ClearCommand(); - - case FindCommand.COMMAND_WORD: - return new FindCommandParser().parse(arguments); - - case ListCommand.COMMAND_WORD: - return new ListCommand(); - - case ExitCommand.COMMAND_WORD: - return new ExitCommand(); - - case HelpCommand.COMMAND_WORD: - return new HelpCommand(); - - default: - throw new ParseException(MESSAGE_UNKNOWN_COMMAND); - } - } - -} diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java deleted file mode 100644 index 75b1a9bf119..00000000000 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ /dev/null @@ -1,15 +0,0 @@ -package seedu.address.logic.parser; - -/** - * Contains Command Line Interface (CLI) syntax definitions common to multiple commands - */ -public class CliSyntax { - - /* Prefix definitions */ - public static final Prefix PREFIX_NAME = new Prefix("n/"); - public static final Prefix PREFIX_PHONE = new Prefix("p/"); - public static final Prefix PREFIX_EMAIL = new Prefix("e/"); - public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); - public static final Prefix PREFIX_TAG = new Prefix("t/"); - -} diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java deleted file mode 100644 index 522b93081cc..00000000000 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ /dev/null @@ -1,29 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.parser.exceptions.ParseException; - -/** - * Parses input arguments and creates a new DeleteCommand object - */ -public class DeleteCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the DeleteCommand - * and returns a DeleteCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public DeleteCommand parse(String args) throws ParseException { - try { - Index index = ParserUtil.parseIndex(args); - return new DeleteCommand(index); - } catch (ParseException pe) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe); - } - } - -} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java deleted file mode 100644 index 4fb71f23103..00000000000 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ /dev/null @@ -1,33 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; - -import java.util.Arrays; - -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -/** - * Parses input arguments and creates a new FindCommand object - */ -public class FindCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the FindCommand - * and returns a FindCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public FindCommand parse(String args) throws ParseException { - String trimmedArgs = args.trim(); - if (trimmedArgs.isEmpty()) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); - } - - String[] nameKeywords = trimmedArgs.split("\\s+"); - - return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); - } - -} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java deleted file mode 100644 index 1a943a0781a..00000000000 --- a/src/main/java/seedu/address/model/AddressBook.java +++ /dev/null @@ -1,120 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; - -/** - * Wraps all data at the address-book level - * Duplicates are not allowed (by .isSamePerson comparison) - */ -public class AddressBook implements ReadOnlyAddressBook { - - private final UniquePersonList persons; - - /* - * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication - * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html - * - * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication - * among constructors. - */ - { - persons = new UniquePersonList(); - } - - public AddressBook() {} - - /** - * Creates an AddressBook using the Persons in the {@code toBeCopied} - */ - public AddressBook(ReadOnlyAddressBook toBeCopied) { - this(); - resetData(toBeCopied); - } - - //// list overwrite operations - - /** - * Replaces the contents of the person list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - this.persons.setPersons(persons); - } - - /** - * Resets the existing data of this {@code AddressBook} with {@code newData}. - */ - public void resetData(ReadOnlyAddressBook newData) { - requireNonNull(newData); - - setPersons(newData.getPersonList()); - } - - //// person-level operations - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - public boolean hasPerson(Person person) { - requireNonNull(person); - return persons.contains(person); - } - - /** - * Adds a person to the address book. - * The person must not already exist in the address book. - */ - public void addPerson(Person p) { - persons.add(p); - } - - /** - * Replaces the given person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. - */ - public void setPerson(Person target, Person editedPerson) { - requireNonNull(editedPerson); - - persons.setPerson(target, editedPerson); - } - - /** - * Removes {@code key} from this {@code AddressBook}. - * {@code key} must exist in the address book. - */ - public void removePerson(Person key) { - persons.remove(key); - } - - //// util methods - - @Override - public String toString() { - return persons.asUnmodifiableObservableList().size() + " persons"; - // TODO: refine later - } - - @Override - public ObservableList getPersonList() { - return persons.asUnmodifiableObservableList(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddressBook // instanceof handles nulls - && persons.equals(((AddressBook) other).persons)); - } - - @Override - public int hashCode() { - return persons.hashCode(); - } -} diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java deleted file mode 100644 index d54df471c1f..00000000000 --- a/src/main/java/seedu/address/model/Model.java +++ /dev/null @@ -1,87 +0,0 @@ -package seedu.address.model; - -import java.nio.file.Path; -import java.util.function.Predicate; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; - -/** - * The API of the Model component. - */ -public interface Model { - /** {@code Predicate} that always evaluate to true */ - Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; - - /** - * Replaces user prefs data with the data in {@code userPrefs}. - */ - void setUserPrefs(ReadOnlyUserPrefs userPrefs); - - /** - * Returns the user prefs. - */ - ReadOnlyUserPrefs getUserPrefs(); - - /** - * Returns the user prefs' GUI settings. - */ - GuiSettings getGuiSettings(); - - /** - * Sets the user prefs' GUI settings. - */ - void setGuiSettings(GuiSettings guiSettings); - - /** - * Returns the user prefs' address book file path. - */ - Path getAddressBookFilePath(); - - /** - * Sets the user prefs' address book file path. - */ - void setAddressBookFilePath(Path addressBookFilePath); - - /** - * Replaces address book data with the data in {@code addressBook}. - */ - void setAddressBook(ReadOnlyAddressBook addressBook); - - /** Returns the AddressBook */ - ReadOnlyAddressBook getAddressBook(); - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - boolean hasPerson(Person person); - - /** - * Deletes the given person. - * The person must exist in the address book. - */ - void deletePerson(Person target); - - /** - * Adds the given person. - * {@code person} must not already exist in the address book. - */ - void addPerson(Person person); - - /** - * Replaces the given person {@code target} with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. - */ - void setPerson(Person target, Person editedPerson); - - /** Returns an unmodifiable view of the filtered person list */ - ObservableList getFilteredPersonList(); - - /** - * Updates the filter of the filtered person list to filter by the given {@code predicate}. - * @throws NullPointerException if {@code predicate} is null. - */ - void updateFilteredPersonList(Predicate predicate); -} diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java deleted file mode 100644 index 86c1df298d7..00000000000 --- a/src/main/java/seedu/address/model/ModelManager.java +++ /dev/null @@ -1,150 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.nio.file.Path; -import java.util.function.Predicate; -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import javafx.collections.transformation.FilteredList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; - -/** - * Represents the in-memory model of the address book data. - */ -public class ModelManager implements Model { - private static final Logger logger = LogsCenter.getLogger(ModelManager.class); - - private final AddressBook addressBook; - private final UserPrefs userPrefs; - private final FilteredList filteredPersons; - - /** - * Initializes a ModelManager with the given addressBook and userPrefs. - */ - public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { - requireAllNonNull(addressBook, userPrefs); - - logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); - - this.addressBook = new AddressBook(addressBook); - this.userPrefs = new UserPrefs(userPrefs); - filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); - } - - public ModelManager() { - this(new AddressBook(), new UserPrefs()); - } - - //=========== UserPrefs ================================================================================== - - @Override - public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { - requireNonNull(userPrefs); - this.userPrefs.resetData(userPrefs); - } - - @Override - public ReadOnlyUserPrefs getUserPrefs() { - return userPrefs; - } - - @Override - public GuiSettings getGuiSettings() { - return userPrefs.getGuiSettings(); - } - - @Override - public void setGuiSettings(GuiSettings guiSettings) { - requireNonNull(guiSettings); - userPrefs.setGuiSettings(guiSettings); - } - - @Override - public Path getAddressBookFilePath() { - return userPrefs.getAddressBookFilePath(); - } - - @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - userPrefs.setAddressBookFilePath(addressBookFilePath); - } - - //=========== AddressBook ================================================================================ - - @Override - public void setAddressBook(ReadOnlyAddressBook addressBook) { - this.addressBook.resetData(addressBook); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return addressBook; - } - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return addressBook.hasPerson(person); - } - - @Override - public void deletePerson(Person target) { - addressBook.removePerson(target); - } - - @Override - public void addPerson(Person person) { - addressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - } - - @Override - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - addressBook.setPerson(target, editedPerson); - } - - //=========== Filtered Person List Accessors ============================================================= - - /** - * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of - * {@code versionedAddressBook} - */ - @Override - public ObservableList getFilteredPersonList() { - return filteredPersons; - } - - @Override - public void updateFilteredPersonList(Predicate predicate) { - requireNonNull(predicate); - filteredPersons.setPredicate(predicate); - } - - @Override - public boolean equals(Object obj) { - // short circuit if same object - if (obj == this) { - return true; - } - - // instanceof handles nulls - if (!(obj instanceof ModelManager)) { - return false; - } - - // state check - ModelManager other = (ModelManager) obj; - return addressBook.equals(other.addressBook) - && userPrefs.equals(other.userPrefs) - && filteredPersons.equals(other.filteredPersons); - } - -} diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java deleted file mode 100644 index 6ddc2cd9a29..00000000000 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ /dev/null @@ -1,17 +0,0 @@ -package seedu.address.model; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; - -/** - * Unmodifiable view of an address book - */ -public interface ReadOnlyAddressBook { - - /** - * Returns an unmodifiable view of the persons list. - * This list will not contain any duplicate persons. - */ - ObservableList getPersonList(); - -} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java deleted file mode 100644 index 1806da4facf..00000000000 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.model.util; - -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; - -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Contains utility methods for populating {@code AddressBook} with sample data. - */ -public class SampleDataUtil { - public static Person[] getSamplePersons() { - return new Person[] { - new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), - new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), - new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), - new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), - new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), - new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) - }; - } - - public static ReadOnlyAddressBook getSampleAddressBook() { - AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); - } - return sampleAb; - } - - /** - * Returns a tag set containing the list of strings given. - */ - public static Set getTagSet(String... strings) { - return Arrays.stream(strings) - .map(Tag::new) - .collect(Collectors.toSet()); - } - -} diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/AddressBookStorage.java deleted file mode 100644 index 4599182b3f9..00000000000 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ /dev/null @@ -1,45 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; - -/** - * Represents a storage for {@link seedu.address.model.AddressBook}. - */ -public interface AddressBookStorage { - - /** - * Returns the file path of the data file. - */ - Path getAddressBookFilePath(); - - /** - * Returns AddressBook data as a {@link ReadOnlyAddressBook}. - * Returns {@code Optional.empty()} if storage file is not found. - * @throws DataConversionException if the data in storage is not in the expected format. - * @throws IOException if there was any problem when reading from the storage. - */ - Optional readAddressBook() throws DataConversionException, IOException; - - /** - * @see #getAddressBookFilePath() - */ - Optional readAddressBook(Path filePath) throws DataConversionException, IOException; - - /** - * Saves the given {@link ReadOnlyAddressBook} to the storage. - * @param addressBook cannot be null. - * @throws IOException if there was any problem writing to the file. - */ - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - - /** - * @see #saveAddressBook(ReadOnlyAddressBook) - */ - void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java deleted file mode 100644 index dfab9daaa0d..00000000000 --- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java +++ /dev/null @@ -1,80 +0,0 @@ -package seedu.address.storage; - -import static java.util.Objects.requireNonNull; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; -import java.util.logging.Logger; - -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.FileUtil; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyAddressBook; - -/** - * A class to access AddressBook data stored as a json file on the hard disk. - */ -public class JsonAddressBookStorage implements AddressBookStorage { - - private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class); - - private Path filePath; - - public JsonAddressBookStorage(Path filePath) { - this.filePath = filePath; - } - - public Path getAddressBookFilePath() { - return filePath; - } - - @Override - public Optional readAddressBook() throws DataConversionException { - return readAddressBook(filePath); - } - - /** - * Similar to {@link #readAddressBook()}. - * - * @param filePath location of the data. Cannot be null. - * @throws DataConversionException if the file is not in the correct format. - */ - public Optional readAddressBook(Path filePath) throws DataConversionException { - requireNonNull(filePath); - - Optional jsonAddressBook = JsonUtil.readJsonFile( - filePath, JsonSerializableAddressBook.class); - if (!jsonAddressBook.isPresent()) { - return Optional.empty(); - } - - try { - return Optional.of(jsonAddressBook.get().toModelType()); - } catch (IllegalValueException ive) { - logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); - throw new DataConversionException(ive); - } - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, filePath); - } - - /** - * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}. - * - * @param filePath location of the data. Cannot be null. - */ - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - requireNonNull(addressBook); - requireNonNull(filePath); - - FileUtil.createIfMissing(filePath); - JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java deleted file mode 100644 index 5efd834091d..00000000000 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonRootName; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; - -/** - * An Immutable AddressBook that is serializable to JSON format. - */ -@JsonRootName(value = "addressbook") -class JsonSerializableAddressBook { - - public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; - - private final List persons = new ArrayList<>(); - - /** - * Constructs a {@code JsonSerializableAddressBook} with the given persons. - */ - @JsonCreator - public JsonSerializableAddressBook(@JsonProperty("persons") List persons) { - this.persons.addAll(persons); - } - - /** - * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use. - * - * @param source future changes to this will not affect the created {@code JsonSerializableAddressBook}. - */ - public JsonSerializableAddressBook(ReadOnlyAddressBook source) { - persons.addAll(source.getPersonList().stream().map(JsonAdaptedPerson::new).collect(Collectors.toList())); - } - - /** - * Converts this address book into the model's {@code AddressBook} object. - * - * @throws IllegalValueException if there were any data constraints violated. - */ - public AddressBook toModelType() throws IllegalValueException { - AddressBook addressBook = new AddressBook(); - for (JsonAdaptedPerson jsonAdaptedPerson : persons) { - Person person = jsonAdaptedPerson.toModelType(); - if (addressBook.hasPerson(person)) { - throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON); - } - addressBook.addPerson(person); - } - return addressBook; - } - -} diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java deleted file mode 100644 index beda8bd9f11..00000000000 --- a/src/main/java/seedu/address/storage/Storage.java +++ /dev/null @@ -1,32 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; - -/** - * API of the Storage component - */ -public interface Storage extends AddressBookStorage, UserPrefsStorage { - - @Override - Optional readUserPrefs() throws DataConversionException, IOException; - - @Override - void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; - - @Override - Path getAddressBookFilePath(); - - @Override - Optional readAddressBook() throws DataConversionException, IOException; - - @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java deleted file mode 100644 index 6cfa0162164..00000000000 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ /dev/null @@ -1,78 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; -import java.util.logging.Logger; - -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; - -/** - * Manages storage of AddressBook data in local storage. - */ -public class StorageManager implements Storage { - - private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; - private UserPrefsStorage userPrefsStorage; - - /** - * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}. - */ - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { - this.addressBookStorage = addressBookStorage; - this.userPrefsStorage = userPrefsStorage; - } - - // ================ UserPrefs methods ============================== - - @Override - public Path getUserPrefsFilePath() { - return userPrefsStorage.getUserPrefsFilePath(); - } - - @Override - public Optional readUserPrefs() throws DataConversionException, IOException { - return userPrefsStorage.readUserPrefs(); - } - - @Override - public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { - userPrefsStorage.saveUserPrefs(userPrefs); - } - - - // ================ AddressBook methods ============================== - - @Override - public Path getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); - } - - @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); - } - - @Override - public Optional readAddressBook(Path filePath) throws DataConversionException, IOException { - logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); - } - -} diff --git a/src/main/java/seedu/address/AppParameters.java b/src/main/java/seedu/realtime/AppParameters.java similarity index 93% rename from src/main/java/seedu/address/AppParameters.java rename to src/main/java/seedu/realtime/AppParameters.java index ab552c398f3..e69c8d22378 100644 --- a/src/main/java/seedu/address/AppParameters.java +++ b/src/main/java/seedu/realtime/AppParameters.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.realtime; import java.nio.file.Path; import java.nio.file.Paths; @@ -7,8 +7,8 @@ import java.util.logging.Logger; import javafx.application.Application; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.FileUtil; +import seedu.realtime.commons.core.LogsCenter; +import seedu.realtime.commons.util.FileUtil; /** * Represents the parsed command-line parameters given to the application. diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/realtime/Main.java similarity index 97% rename from src/main/java/seedu/address/Main.java rename to src/main/java/seedu/realtime/Main.java index 052a5068631..96075436e88 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/seedu/realtime/Main.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.realtime; import javafx.application.Application; diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/realtime/MainApp.java similarity index 72% rename from src/main/java/seedu/address/MainApp.java rename to src/main/java/seedu/realtime/MainApp.java index 4133aaa0151..145c52f82df 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/realtime/MainApp.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.realtime; import java.io.IOException; import java.nio.file.Path; @@ -7,36 +7,37 @@ import javafx.application.Application; import javafx.stage.Stage; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.core.Version; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.ConfigUtil; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; -import seedu.address.logic.LogicManager; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; -import seedu.address.model.util.SampleDataUtil; -import seedu.address.storage.AddressBookStorage; -import seedu.address.storage.JsonAddressBookStorage; -import seedu.address.storage.JsonUserPrefsStorage; -import seedu.address.storage.Storage; -import seedu.address.storage.StorageManager; -import seedu.address.storage.UserPrefsStorage; -import seedu.address.ui.Ui; -import seedu.address.ui.UiManager; +import seedu.realtime.commons.core.Config; +import seedu.realtime.commons.core.LogsCenter; +import seedu.realtime.commons.core.Version; +import seedu.realtime.commons.exceptions.DataConversionException; +import seedu.realtime.commons.util.ConfigUtil; +import seedu.realtime.commons.util.StringUtil; +import seedu.realtime.logic.Logic; +import seedu.realtime.logic.LogicManager; +import seedu.realtime.model.Model; +import seedu.realtime.model.ModelManager; +import seedu.realtime.model.ReadOnlyRealTime; +import seedu.realtime.model.ReadOnlyUserPrefs; +import seedu.realtime.model.RealTime; +import seedu.realtime.model.UserPrefs; +import seedu.realtime.model.util.SampleDataUtil; +import seedu.realtime.storage.JsonRealTimeStorage; +import seedu.realtime.storage.JsonUserPrefsStorage; +import seedu.realtime.storage.RealTimeStorage; +import seedu.realtime.storage.Storage; +import seedu.realtime.storage.StorageManager; +import seedu.realtime.storage.UserPrefsStorage; +import seedu.realtime.ui.Ui; +import seedu.realtime.ui.UiManager; + /** * Runs the application. */ public class MainApp extends Application { - public static final Version VERSION = new Version(0, 2, 0, true); + public static final Version VERSION = new Version(1, 4, 0, true); private static final Logger logger = LogsCenter.getLogger(MainApp.class); @@ -48,7 +49,7 @@ public class MainApp extends Application { @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing RealTime ]==========================="); super.init(); AppParameters appParameters = AppParameters.parse(getParameters()); @@ -56,8 +57,8 @@ public void init() throws Exception { UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); UserPrefs userPrefs = initPrefs(userPrefsStorage); - AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath()); - storage = new StorageManager(addressBookStorage, userPrefsStorage); + RealTimeStorage realTimeStorage = new JsonRealTimeStorage(userPrefs.getRealTimeFilePath()); + storage = new StorageManager(realTimeStorage, userPrefsStorage); initLogging(config); @@ -74,20 +75,20 @@ public void init() throws Exception { * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book. */ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional realTimeOptional; + ReadOnlyRealTime initialData; try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Data file not found. Will be starting with a sample AddressBook"); + realTimeOptional = storage.readRealTime(); + if (!realTimeOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample RealTime"); } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + initialData = realTimeOptional.orElseGet(SampleDataUtil::getSampleRealTime); } catch (DataConversionException e) { - logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Data file not in the correct format. Will be starting with an empty RealTime"); + initialData = new RealTime(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Problem while reading from the file. Will be starting with an empty RealTime"); + initialData = new RealTime(); } return new ModelManager(initialData, userPrefs); @@ -151,7 +152,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { + "Using default user prefs"); initializedPrefs = new UserPrefs(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); + logger.warning("Problem while reading from the file. Will be starting with an empty RealTime"); initializedPrefs = new UserPrefs(); } @@ -167,7 +168,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting RealTime " + MainApp.VERSION); ui.start(primaryStage); } diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/realtime/commons/core/Config.java similarity index 97% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/seedu/realtime/commons/core/Config.java index 91145745521..a8afc63e7f4 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/seedu/realtime/commons/core/Config.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.realtime.commons.core; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/realtime/commons/core/GuiSettings.java similarity index 98% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/seedu/realtime/commons/core/GuiSettings.java index ba33653be67..17d7516b8cc 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/realtime/commons/core/GuiSettings.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.realtime.commons.core; import java.awt.Point; import java.io.Serializable; diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/realtime/commons/core/LogsCenter.java similarity index 97% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/seedu/realtime/commons/core/LogsCenter.java index 431e7185e76..59b0e2bc20c 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/seedu/realtime/commons/core/LogsCenter.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.realtime.commons.core; import java.io.IOException; import java.util.Arrays; @@ -18,7 +18,7 @@ public class LogsCenter { private static final int MAX_FILE_COUNT = 5; private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB - private static final String LOG_FILE = "addressbook.log"; + private static final String LOG_FILE = "realTime.log"; private static Level currentLogLevel = Level.INFO; private static final Logger logger = LogsCenter.getLogger(LogsCenter.class); private static FileHandler fileHandler; diff --git a/src/main/java/seedu/realtime/commons/core/Messages.java b/src/main/java/seedu/realtime/commons/core/Messages.java new file mode 100644 index 00000000000..3b7a5e1df09 --- /dev/null +++ b/src/main/java/seedu/realtime/commons/core/Messages.java @@ -0,0 +1,20 @@ +package seedu.realtime.commons.core; + +/** + * Container for user visible messages. + */ +public class Messages { + + public static final String MESSAGE_CLIENTS_LISTED_OVERVIEW = "%1$d clients listed!"; + public static final String MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX = "The client index provided is invalid"; + public static final String MESSAGE_INVALID_LISTING_DISPLAYED_INDEX = "The listing index provided is invalid"; + public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; + public static final String MESSAGE_INVALID_LISTING_ID = "The listing ID provided is invalid."; + public static final String MESSAGE_LISTING_DOES_NOT_EXIST = "This listing does not exist in REal-Time."; + public static final String MESSAGE_INVALID_OFFER_DISPLAYED_INDEX = "The offer index provided is invalid"; + public static final String MESSAGE_INVALID_MEETING_DISPLAYED_INDEX = "The meeting index provided is invalid"; + public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; + public static final String MESSAGE_OFFERS_LISTED_OVERVIEW = "%1$d offers listed!"; + public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; + public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; +} diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/realtime/commons/core/Version.java similarity index 98% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/seedu/realtime/commons/core/Version.java index 12142ec1e32..efa562572e2 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/seedu/realtime/commons/core/Version.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.realtime.commons.core; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/seedu/realtime/commons/core/index/Index.java similarity index 97% rename from src/main/java/seedu/address/commons/core/index/Index.java rename to src/main/java/seedu/realtime/commons/core/index/Index.java index 19536439c09..282f1e2cf0f 100644 --- a/src/main/java/seedu/address/commons/core/index/Index.java +++ b/src/main/java/seedu/realtime/commons/core/index/Index.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core.index; +package seedu.realtime.commons.core.index; /** * Represents a zero-based or one-based index. diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/seedu/realtime/commons/exceptions/DataConversionException.java similarity index 84% rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java rename to src/main/java/seedu/realtime/commons/exceptions/DataConversionException.java index 1f689bd8e3f..9c241e0a395 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/seedu/realtime/commons/exceptions/DataConversionException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.realtime.commons.exceptions; /** * Represents an error during conversion of data from one format to another diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/realtime/commons/exceptions/IllegalValueException.java similarity index 92% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/seedu/realtime/commons/exceptions/IllegalValueException.java index 19124db485c..0e5225d3b36 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/seedu/realtime/commons/exceptions/IllegalValueException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.realtime.commons.exceptions; /** * Signals that some given data does not fulfill some constraints. diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/seedu/realtime/commons/util/AppUtil.java similarity index 94% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/seedu/realtime/commons/util/AppUtil.java index 87aa89c0326..9a4a5298536 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/seedu/realtime/commons/util/AppUtil.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package seedu.realtime.commons.util; import static java.util.Objects.requireNonNull; import javafx.scene.image.Image; -import seedu.address.MainApp; +import seedu.realtime.MainApp; /** * A container for App specific utility functions diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/seedu/realtime/commons/util/CollectionUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/seedu/realtime/commons/util/CollectionUtil.java index eafe4dfd681..2a525facbad 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/seedu/realtime/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.realtime.commons.util; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/seedu/realtime/commons/util/ConfigUtil.java similarity index 77% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/seedu/realtime/commons/util/ConfigUtil.java index f7f8a2bd44c..a14f056ca43 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/seedu/realtime/commons/util/ConfigUtil.java @@ -1,11 +1,11 @@ -package seedu.address.commons.util; +package seedu.realtime.commons.util; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.realtime.commons.core.Config; +import seedu.realtime.commons.exceptions.DataConversionException; /** * A class for accessing the Config File. diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/realtime/commons/util/FileUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/seedu/realtime/commons/util/FileUtil.java index b1e2767cdd9..156b1dd279a 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/seedu/realtime/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.realtime.commons.util; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/seedu/realtime/commons/util/JsonUtil.java similarity index 97% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/seedu/realtime/commons/util/JsonUtil.java index 8ef609f055d..66745585dcb 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/seedu/realtime/commons/util/JsonUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.realtime.commons.util; import static java.util.Objects.requireNonNull; @@ -20,8 +20,8 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.realtime.commons.core.LogsCenter; +import seedu.realtime.commons.exceptions.DataConversionException; /** * Converts a Java object instance to JSON and vice versa diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/realtime/commons/util/StringUtil.java similarity index 95% rename from src/main/java/seedu/address/commons/util/StringUtil.java rename to src/main/java/seedu/realtime/commons/util/StringUtil.java index 61cc8c9a1cb..1eeeeef401a 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/realtime/commons/util/StringUtil.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package seedu.realtime.commons.util; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.realtime.commons.util.AppUtil.checkArgument; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/src/main/java/seedu/realtime/logic/Logic.java b/src/main/java/seedu/realtime/logic/Logic.java new file mode 100644 index 00000000000..78dcc11a5fc --- /dev/null +++ b/src/main/java/seedu/realtime/logic/Logic.java @@ -0,0 +1,67 @@ +package seedu.realtime.logic; + +import java.nio.file.Path; + +import javafx.collections.ObservableList; +import seedu.realtime.commons.core.GuiSettings; +import seedu.realtime.logic.commands.CommandResult; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.ReadOnlyRealTime; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.meeting.Meeting; +import seedu.realtime.model.offer.Offer; +import seedu.realtime.model.person.Client; +import seedu.realtime.model.person.Person; + +/** + * API of the Logic component + */ +public interface Logic { + /** + * Executes the command and returns the result. + * @param commandText The command as entered by the user. + * @return the result of the command execution. + * @throws CommandException If an error occurs during command execution. + * @throws ParseException If an error occurs during parsing. + */ + CommandResult execute(String commandText) throws CommandException, ParseException; + + /** + * Returns the RealTime. + * + * @see seedu.realtime.model.Model#getRealTime() + */ + ReadOnlyRealTime getRealTime(); + + /** Returns an unmodifiable view of the filtered list of persons */ + ObservableList getFilteredPersonList(); + + /** Returns an unmodifiable view of the filtered list of clients */ + ObservableList getFilteredClientList(); + + /** Returns an unmodifiable view of the filtered list of offers*/ + ObservableList getFilteredOfferList(); + + /** Returns an unmodifiable view of the filtered list of listings*/ + ObservableList getFilteredListingList(); + + /** Returns an unmodifiable view of the filtered list of listings*/ + ObservableList getFilteredMeetingList(); + + /** + * Returns the user prefs' address book file path. + */ + Path getRealTimeFilePath(); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Set the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); + +} diff --git a/src/main/java/seedu/realtime/logic/LogicManager.java b/src/main/java/seedu/realtime/logic/LogicManager.java new file mode 100644 index 00000000000..4f5fa397cca --- /dev/null +++ b/src/main/java/seedu/realtime/logic/LogicManager.java @@ -0,0 +1,105 @@ +package seedu.realtime.logic; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import seedu.realtime.commons.core.GuiSettings; +import seedu.realtime.commons.core.LogsCenter; +import seedu.realtime.logic.commands.Command; +import seedu.realtime.logic.commands.CommandResult; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.logic.parser.RealTimeParser; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.Model; +import seedu.realtime.model.ReadOnlyRealTime; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.meeting.Meeting; +import seedu.realtime.model.offer.Offer; +import seedu.realtime.model.person.Client; +import seedu.realtime.model.person.Person; +import seedu.realtime.storage.Storage; + +/** + * The main LogicManager of the app. + */ +public class LogicManager implements Logic { + public static final String FILE_OPS_ERROR_MESSAGE = "Could not save data to file: "; + private final Logger logger = LogsCenter.getLogger(LogicManager.class); + + private final Model model; + private final Storage storage; + private final RealTimeParser realTimeParser; + + /** + * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. + */ + public LogicManager(Model model, Storage storage) { + this.model = model; + this.storage = storage; + realTimeParser = new RealTimeParser(); + } + + @Override + public CommandResult execute(String commandText) throws CommandException, ParseException { + logger.info("----------------[USER COMMAND][" + commandText + "]"); + + CommandResult commandResult; + Command command = realTimeParser.parseCommand(commandText); + commandResult = command.execute(model); + + try { + storage.saveRealTime(model.getRealTime()); + } catch (IOException ioe) { + throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); + } + + return commandResult; + } + + @Override + public ReadOnlyRealTime getRealTime() { + return model.getRealTime(); + } + + @Override + public ObservableList getFilteredPersonList() { + return model.getFilteredPersonList(); + } + + @Override + public ObservableList getFilteredClientList() { + return model.getFilteredClientList(); + } + + @Override + public ObservableList getFilteredOfferList() { + return model.getFilteredOfferList(); + } + + @Override + public ObservableList getFilteredListingList() { + return model.getFilteredListingList(); + } + + @Override + public ObservableList getFilteredMeetingList() { + return model.getFilteredMeetingList(); + } + + @Override + public Path getRealTimeFilePath() { + return model.getRealTimeFilePath(); + } + + @Override + public GuiSettings getGuiSettings() { + return model.getGuiSettings(); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + model.setGuiSettings(guiSettings); + } +} diff --git a/src/main/java/seedu/realtime/logic/commands/AddClientCommand.java b/src/main/java/seedu/realtime/logic/commands/AddClientCommand.java new file mode 100644 index 00000000000..07797869f2a --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/AddClientCommand.java @@ -0,0 +1,68 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_TAG; + +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.person.Client; + +/** + * Adds a client to the address book. + */ +public class AddClientCommand extends Command { + + public static final String COMMAND_WORD = "addC"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a client to the address book. " + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_PHONE + "PHONE " + + PREFIX_EMAIL + "EMAIL " + + PREFIX_ADDRESS + "ADDRESS " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe " + + PREFIX_PHONE + "98765432 " + + PREFIX_EMAIL + "johnd@example.com " + + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " + + PREFIX_TAG + "friends " + + PREFIX_TAG + "owesMoney"; + + public static final String MESSAGE_SUCCESS = "New client added: %1$s"; + public static final String MESSAGE_DUPLICATE_CLIENT = "This client already exists in the address book"; + + private final Client toAdd; + + /** + * Creates an AddCommand to add the specified {@code Client} + */ + public AddClientCommand(Client client) { + requireNonNull(client); + toAdd = client; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasClient(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_CLIENT); + } + + model.addClient(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddClientCommand // instanceof handles nulls + && toAdd.equals(((AddClientCommand) other).toAdd)); + } +} + diff --git a/src/main/java/seedu/realtime/logic/commands/AddInterestedClientCommand.java b/src/main/java/seedu/realtime/logic/commands/AddInterestedClientCommand.java new file mode 100644 index 00000000000..fa62f40b843 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/AddInterestedClientCommand.java @@ -0,0 +1,62 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; + +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.person.Name; + +/** + * Adds an interested client to the listing. + */ +public class AddInterestedClientCommand extends Command { + + public static final String COMMAND_WORD = "addIC"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an interested client to the listing. " + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_LISTING_ID + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe " + + PREFIX_LISTING_ID + "001"; + + public static final String MESSAGE_SUCCESS = "New client added: %1$s"; + public static final String MESSAGE_DUPLICATE_CLIENT = "This client already exists in the listing"; + + private final Name toAdd; + private final ListingId listingId; + + /** + * Creates an AddCommand to add the specified {@code Client} + */ + public AddInterestedClientCommand(Name client, ListingId listingId) { + requireNonNull(client); + toAdd = client; + this.listingId = listingId; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + Listing listing = model.getListing(listingId); + if (listing.hasInterestedClient(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_CLIENT); + } + + listing.addInterestedClient(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddInterestedClientCommand // instanceof handles nulls + && toAdd.equals(((AddInterestedClientCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/realtime/logic/commands/AddListingCommand.java b/src/main/java/seedu/realtime/logic/commands/AddListingCommand.java new file mode 100644 index 00000000000..f88f7d6bb53 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/AddListingCommand.java @@ -0,0 +1,79 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ASKING_PRICE; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; + +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.offer.Price; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Name; + +/** + * Adds a person to the address book. + */ +public class AddListingCommand extends Command { + + public static final String COMMAND_WORD = "addL"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a listing to the address book. " + + "Parameters: " + + PREFIX_LISTING_ID + "ID " + + PREFIX_ADDRESS + " ADDRESS " + + PREFIX_NAME + "NAME " + + PREFIX_ASKING_PRICE + "ASKING PRICE \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_LISTING_ID + "3412 " + + PREFIX_ADDRESS + " 100 Charming Avenue " + + PREFIX_NAME + "Jake Holt " + + PREFIX_ASKING_PRICE + "1 "; + + public static final String MESSAGE_SUCCESS = "New listing added: %1$s"; + public static final String MESSAGE_DUPLICATE_LISTING = "This listing already exists in the address book"; + + private final ListingId id; + private final Address address; + private final Name name; + private final Price askingPrice; + private Listing toAdd; + + /** + * Creates an AddCommand to add the specified {@code Person} + */ + public AddListingCommand(ListingId id, Address address, Name name, Price askingPrice) { + requireAllNonNull(address, name); + this.id = id; + this.address = address; + this.name = name; + this.askingPrice = askingPrice; + this.toAdd = new Listing(id, address, name, askingPrice); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + this.toAdd = new Listing(id, address, name, askingPrice); + + if (model.hasListing(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_LISTING); + } + + model.addListing(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddListingCommand // instanceof handles nulls + && toAdd.getId().equals(((AddListingCommand) other).toAdd.getId())); + } + +} diff --git a/src/main/java/seedu/realtime/logic/commands/AddMeetingCommand.java b/src/main/java/seedu/realtime/logic/commands/AddMeetingCommand.java new file mode 100644 index 00000000000..fc9f070a5ea --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/AddMeetingCommand.java @@ -0,0 +1,60 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_DATETIME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; + +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.meeting.Meeting; + + +/** + * Adds a Meeting to the address book. + */ +public class AddMeetingCommand extends Command { + + public static final String COMMAND_WORD = "addM"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a meeting to the address book. " + + "Parameters: " + + PREFIX_LISTING_ID + "LISTING ADDRESS " + + PREFIX_NAME + "NAME " + + PREFIX_DATETIME + "Date Time (yyyy-MM-dd HH:mm) \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_LISTING_ID + "30_SERGARDENS_LOR23_0718 " + + PREFIX_NAME + "Bob " + + PREFIX_DATETIME + "2022-12-14 12:00"; + + public static final String MESSAGE_SUCCESS = "New Meeting added: %1$s"; + public static final String MESSAGE_DUPLICATE_MEETING = "This meeting already exists!"; + + private final Meeting toAdd; + + /** + * Creates an AddMeetingCommand to add the specified {@code Meeting} + */ + public AddMeetingCommand(Meeting meeting) { + requireNonNull(meeting); + toAdd = meeting; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasMeeting(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_MEETING); + } + model.addMeeting(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof AddMeetingCommand + && toAdd.equals(((AddMeetingCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/realtime/logic/commands/AddOfferCommand.java b/src/main/java/seedu/realtime/logic/commands/AddOfferCommand.java new file mode 100644 index 00000000000..2a9139a21cb --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/AddOfferCommand.java @@ -0,0 +1,59 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_OFFER; + +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.offer.Offer; + +/** + * Adds an offer to the address book. + */ +public class AddOfferCommand extends Command { + public static final String COMMAND_WORD = "addO"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an offer to the address book. " + + "Parameters: " + + PREFIX_LISTING_ID + "LISTING ADDRESS " + + PREFIX_NAME + "NAME " + + PREFIX_OFFER + "OFFER \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_LISTING_ID + "30_SERGARDENS_LOR23_0718 " + + PREFIX_NAME + "Bob " + + PREFIX_OFFER + "600000"; + + public static final String MESSAGE_SUCCESS = "New offer added: %1$s"; + public static final String MESSAGE_DUPLICATE_OFFER = "This offer already exists in the list of offers"; + + private final Offer toAdd; + + /** + * Creates an AddOfferCommand to add the specified {@code Person} + */ + public AddOfferCommand(Offer offer) { + requireNonNull(offer); + toAdd = offer; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasOffer(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_OFFER); + } + + model.addOffer(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddOfferCommand // instanceof handles nulls + && toAdd.equals(((AddOfferCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/realtime/logic/commands/AddPersonCommand.java similarity index 66% rename from src/main/java/seedu/address/logic/commands/AddCommand.java rename to src/main/java/seedu/realtime/logic/commands/AddPersonCommand.java index 71656d7c5c8..865d8d570d3 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/realtime/logic/commands/AddPersonCommand.java @@ -1,22 +1,22 @@ -package seedu.address.logic.commands; +package seedu.realtime.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_TAG; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.person.Person; /** * Adds a person to the address book. */ -public class AddCommand extends Command { +public class AddPersonCommand extends Command { - public static final String COMMAND_WORD = "add"; + public static final String COMMAND_WORD = "addP"; public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " + "Parameters: " @@ -41,7 +41,7 @@ public class AddCommand extends Command { /** * Creates an AddCommand to add the specified {@code Person} */ - public AddCommand(Person person) { + public AddPersonCommand(Person person) { requireNonNull(person); toAdd = person; } @@ -61,7 +61,8 @@ public CommandResult execute(Model model) throws CommandException { @Override public boolean equals(Object other) { return other == this // short circuit if same object - || (other instanceof AddCommand // instanceof handles nulls - && toAdd.equals(((AddCommand) other).toAdd)); + || (other instanceof AddPersonCommand // instanceof handles nulls + && toAdd.equals(((AddPersonCommand) other).toAdd)); } + } diff --git a/src/main/java/seedu/realtime/logic/commands/AddTagsToListingCommand.java b/src/main/java/seedu/realtime/logic/commands/AddTagsToListingCommand.java new file mode 100644 index 00000000000..01f64232fa9 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/AddTagsToListingCommand.java @@ -0,0 +1,63 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Set; + +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.tag.Tag; + +/** + * Adds a set of Tags to the Listing. + */ +public class AddTagsToListingCommand extends Command { + + public static final String COMMAND_WORD = "addT"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds tags to the listing in the address book. " + + "Parameters: " + + PREFIX_TAG + " tags \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_TAG + " 3 bedroom \n"; + + public static final String MESSAGE_SUCCESS = "New tags added to listing: %1$s"; + public static final String MESSAGE_DUPLICATE_TAGS = "These tags already exists in the listing"; + + private final Set tags; + private final ListingId listingId; + + /** + * Creates an AddCommand to add the specified {@code Person} + */ + public AddTagsToListingCommand(Set tags, ListingId listingId) { + requireNonNull(tags); + this.tags = tags; + this.listingId = listingId; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + Listing listing = model.getListing(listingId); + + if (listing.hasTag(tags)) { + throw new CommandException(MESSAGE_DUPLICATE_TAGS); + } + + listing.addTags(tags); + model.setListing(model.getListing(listingId), listing); + return new CommandResult(String.format(MESSAGE_SUCCESS, tags)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddTagsToListingCommand // instanceof handles nulls + && tags.equals(((AddTagsToListingCommand) other).tags)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/realtime/logic/commands/ClearCommand.java similarity index 72% rename from src/main/java/seedu/address/logic/commands/ClearCommand.java rename to src/main/java/seedu/realtime/logic/commands/ClearCommand.java index 9c86b1fa6e4..8c29854c7e3 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/realtime/logic/commands/ClearCommand.java @@ -1,9 +1,10 @@ -package seedu.address.logic.commands; +package seedu.realtime.logic.commands; import static java.util.Objects.requireNonNull; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; +import seedu.realtime.model.Model; +import seedu.realtime.model.RealTime; + /** * Clears the address book. @@ -17,7 +18,7 @@ public class ClearCommand extends Command { @Override public CommandResult execute(Model model) { requireNonNull(model); - model.setAddressBook(new AddressBook()); + model.setRealTime(new RealTime()); return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/realtime/logic/commands/Command.java similarity index 78% rename from src/main/java/seedu/address/logic/commands/Command.java rename to src/main/java/seedu/realtime/logic/commands/Command.java index 64f18992160..a0c2387f752 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/realtime/logic/commands/Command.java @@ -1,7 +1,7 @@ -package seedu.address.logic.commands; +package seedu.realtime.logic.commands; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; /** * Represents a command with hidden internal logic and the ability to be executed. diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/realtime/logic/commands/CommandResult.java similarity index 97% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/seedu/realtime/logic/commands/CommandResult.java index 92f900b7916..ea28aa279d2 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/realtime/logic/commands/CommandResult.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.realtime.logic.commands; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/realtime/logic/commands/DeleteClientCommand.java b/src/main/java/seedu/realtime/logic/commands/DeleteClientCommand.java new file mode 100644 index 00000000000..f352cb11f3b --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/DeleteClientCommand.java @@ -0,0 +1,56 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.realtime.commons.core.Messages; +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.person.Client; + +/** + * Deletes a client identified using it's displayed index from the address book. + */ +public class DeleteClientCommand extends Command { + + public static final String COMMAND_WORD = "delC"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the client identified by the index number used in the displayed client list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_CLIENT_SUCCESS = "Deleted Client: %1$s"; + + private final Index targetIndex; + + public DeleteClientCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredClientList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX); + } + + Client clientToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deleteClient(clientToDelete); + model.deleteListingsOwnedBy(clientToDelete); + model.deleteOffersMadeBy(clientToDelete); + model.deleteMeetingsWith(clientToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_CLIENT_SUCCESS, clientToDelete)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteClientCommand // instanceof handles nulls + && targetIndex.equals(((DeleteClientCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/realtime/logic/commands/DeleteListingCommand.java b/src/main/java/seedu/realtime/logic/commands/DeleteListingCommand.java new file mode 100644 index 00000000000..3d9ff5966a9 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/DeleteListingCommand.java @@ -0,0 +1,53 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.realtime.commons.core.Messages; +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.listing.Listing; + +/** + * Deletes a person identified using it's displayed index from the address book. + */ +public class DeleteListingCommand extends Command { + + public static final String COMMAND_WORD = "delL"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the listing identified by the id used in the displayed listings list.\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_LISTING_SUCCESS = "Deleted Listing: %1$s"; + + private final Index index; + + public DeleteListingCommand(Index index) { + this.index = index; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredListingList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_LISTING_DISPLAYED_INDEX); + } + + Listing listingToDelete = lastShownList.get(index.getZeroBased()); + model.deleteListing(listingToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_LISTING_SUCCESS, listingToDelete)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteListingCommand // instanceof handles nulls + && index.equals(((DeleteListingCommand) other).index)); // state check + } +} + diff --git a/src/main/java/seedu/realtime/logic/commands/DeleteMeetingCommand.java b/src/main/java/seedu/realtime/logic/commands/DeleteMeetingCommand.java new file mode 100644 index 00000000000..128e73e7e00 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/DeleteMeetingCommand.java @@ -0,0 +1,54 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.realtime.commons.core.Messages; +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.meeting.Meeting; + + +/** + * Deletes a meeting identified using it's displayed index from the address book. + */ +public class DeleteMeetingCommand extends Command { + + public static final String COMMAND_WORD = "delM"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the meeting identified by the index number used in the displayed meeting list.\n" + + "Parameters INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_MEETING_SUCCESS = "Deleted Meeting: %1$s"; + + private final Index targetIndex; + + public DeleteMeetingCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastshownlist = model.getFilteredMeetingList(); + + if (targetIndex.getZeroBased() >= lastshownlist.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_MEETING_DISPLAYED_INDEX); + } + + Meeting toDelete = lastshownlist.get(targetIndex.getZeroBased()); + model.deleteMeeting(toDelete); + return new CommandResult(String.format(MESSAGE_DELETE_MEETING_SUCCESS, toDelete)); + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof DeleteMeetingCommand + && targetIndex.equals(((DeleteMeetingCommand) other).targetIndex)); + } +} diff --git a/src/main/java/seedu/realtime/logic/commands/DeleteOfferCommand.java b/src/main/java/seedu/realtime/logic/commands/DeleteOfferCommand.java new file mode 100644 index 00000000000..dd45d52a8c0 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/DeleteOfferCommand.java @@ -0,0 +1,53 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.realtime.commons.core.Messages; +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.offer.Offer; + +/** + * Deletes an offer identified using it's displayed index from the address book. + */ +public class DeleteOfferCommand extends Command { + + public static final String COMMAND_WORD = "delO"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the offer identified by the index number used in the displayed offer list.\n" + + "Parameters INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_OFFER_SUCCESS = "Deleted Offer: %1$s"; + + private final Index targetIndex; + + public DeleteOfferCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredOfferList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_OFFER_DISPLAYED_INDEX); + } + + Offer offerToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deleteOffer(offerToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_OFFER_SUCCESS, offerToDelete)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteOfferCommand // instanceof handles nulls + && targetIndex.equals(((DeleteOfferCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/realtime/logic/commands/DeletePersonCommand.java similarity index 68% rename from src/main/java/seedu/address/logic/commands/DeleteCommand.java rename to src/main/java/seedu/realtime/logic/commands/DeletePersonCommand.java index 02fd256acba..2be12dc3f1b 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/realtime/logic/commands/DeletePersonCommand.java @@ -1,21 +1,21 @@ -package seedu.address.logic.commands; +package seedu.realtime.logic.commands; import static java.util.Objects.requireNonNull; import java.util.List; -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.realtime.commons.core.Messages; +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.person.Person; /** * Deletes a person identified using it's displayed index from the address book. */ -public class DeleteCommand extends Command { +public class DeletePersonCommand extends Command { - public static final String COMMAND_WORD = "delete"; + public static final String COMMAND_WORD = "delP"; public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes the person identified by the index number used in the displayed person list.\n" @@ -26,7 +26,7 @@ public class DeleteCommand extends Command { private final Index targetIndex; - public DeleteCommand(Index targetIndex) { + public DeletePersonCommand(Index targetIndex) { this.targetIndex = targetIndex; } @@ -47,7 +47,7 @@ public CommandResult execute(Model model) throws CommandException { @Override public boolean equals(Object other) { return other == this // short circuit if same object - || (other instanceof DeleteCommand // instanceof handles nulls - && targetIndex.equals(((DeleteCommand) other).targetIndex)); // state check + || (other instanceof DeletePersonCommand // instanceof handles nulls + && targetIndex.equals(((DeletePersonCommand) other).targetIndex)); // state check } } diff --git a/src/main/java/seedu/realtime/logic/commands/EditClientCommand.java b/src/main/java/seedu/realtime/logic/commands/EditClientCommand.java new file mode 100644 index 00000000000..04639f89fd8 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/EditClientCommand.java @@ -0,0 +1,227 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.realtime.model.Model.PREDICATE_SHOW_ALL_CLIENTS; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import seedu.realtime.commons.core.Messages; +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.commons.util.CollectionUtil; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Client; +import seedu.realtime.model.person.Email; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.person.Phone; +import seedu.realtime.model.tag.Tag; + +/** + * Edits the details of an existing client in the address book. + */ +public class EditClientCommand extends Command { + + public static final String COMMAND_WORD = "editC"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the client identified " + + "by the index number used in the displayed client list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_PHONE + "PHONE] " + + "[" + PREFIX_EMAIL + "EMAIL] " + + "[" + PREFIX_ADDRESS + "ADDRESS] " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_PHONE + "91234567 " + + PREFIX_EMAIL + "johndoe@example.com"; + + public static final String MESSAGE_EDIT_CLIENT_SUCCESS = "Edited Clients: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_CLIENT = "This client already exists in the address book."; + + private final Index index; + private final EditClientDescriptor editClientDescriptor; + + /** + * @param index of the client in the filtered client list to edit + * @param editClientDescriptor details to edit the client with + */ + public EditClientCommand(Index index, EditClientDescriptor editClientDescriptor) { + requireNonNull(index); + requireNonNull(editClientDescriptor); + + this.index = index; + this.editClientDescriptor = new EditClientDescriptor(editClientDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredClientList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX); + } + + Client clientToEdit = lastShownList.get(index.getZeroBased()); + Client editedClient = createEditedClient(clientToEdit, editClientDescriptor); + + if (!clientToEdit.isSameClient(editedClient) && model.hasClient(editedClient)) { + throw new CommandException(MESSAGE_DUPLICATE_CLIENT); + } + + model.setClient(clientToEdit, editedClient); + model.updateFilteredClientList(PREDICATE_SHOW_ALL_CLIENTS); + return new CommandResult(String.format(MESSAGE_EDIT_CLIENT_SUCCESS, editedClient)); + } + + /** + * Creates and returns a {@code Client} with the details of {@code clientToEdit} + * edited with {@code editClientDescriptor}. + */ + private static Client createEditedClient(Client clientToEdit, EditClientDescriptor editClientDescriptor) { + assert clientToEdit != null; + + Name updatedName = editClientDescriptor.getName().orElse(clientToEdit.getName()); + Phone updatedPhone = editClientDescriptor.getPhone().orElse(clientToEdit.getPhone()); + Email updatedEmail = editClientDescriptor.getEmail().orElse(clientToEdit.getEmail()); + Address updatedAddress = editClientDescriptor.getAddress().orElse(clientToEdit.getAddress()); + Set updatedTags = editClientDescriptor.getTags().orElse(clientToEdit.getTags()); + + return new Client(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditClientCommand)) { + return false; + } + + // state check + EditClientCommand e = (EditClientCommand) other; + return index.equals(e.index) + && editClientDescriptor.equals(e.editClientDescriptor); + } + + /** + * Stores the details to edit the client with. Each non-empty field value will replace the + * corresponding field value of the client. + */ + public static class EditClientDescriptor { + private Name name; + private Phone phone; + private Email email; + private Address address; + private Set tags; + + public EditClientDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditClientDescriptor(EditClientDescriptor toCopy) { + setName(toCopy.name); + setPhone(toCopy.phone); + setEmail(toCopy.email); + setAddress(toCopy.address); + setTags(toCopy.tags); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); + } + + public void setName(Name name) { + this.name = name; + } + + public Optional getName() { + return Optional.ofNullable(name); + } + + public void setPhone(Phone phone) { + this.phone = phone; + } + + public Optional getPhone() { + return Optional.ofNullable(phone); + } + + public void setEmail(Email email) { + this.email = email; + } + + public Optional getEmail() { + return Optional.ofNullable(email); + } + + public void setAddress(Address address) { + this.address = address; + } + + public Optional
getAddress() { + return Optional.ofNullable(address); + } + + /** + * Sets {@code tags} to this object's {@code tags}. + * A defensive copy of {@code tags} is used internally. + */ + public void setTags(Set tags) { + this.tags = (tags != null) ? new HashSet<>(tags) : null; + } + + /** + * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * Returns {@code Optional#empty()} if {@code tags} is null. + */ + public Optional> getTags() { + return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditClientDescriptor)) { + return false; + } + + // state check + EditClientDescriptor e = (EditClientDescriptor) other; + + return getName().equals(e.getName()) + && getPhone().equals(e.getPhone()) + && getEmail().equals(e.getEmail()) + && getAddress().equals(e.getAddress()) + && getTags().equals(e.getTags()); + } + } +} + diff --git a/src/main/java/seedu/realtime/logic/commands/EditListingCommand.java b/src/main/java/seedu/realtime/logic/commands/EditListingCommand.java new file mode 100644 index 00000000000..73eda7d05cf --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/EditListingCommand.java @@ -0,0 +1,227 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ASKING_PRICE; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.realtime.model.Model.PREDICATE_SHOW_ALL_LISTINGS; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import seedu.realtime.commons.core.Messages; +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.commons.util.CollectionUtil; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.offer.Price; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.tag.Tag; + +/** + * Edits the details of an existing person in the address book. + */ +public class EditListingCommand extends Command { + + public static final String COMMAND_WORD = "editL"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the listing identified " + + "by the id used in the displayed listing list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: " + + "[" + PREFIX_LISTING_ID + "ID]" + + "[" + PREFIX_ADDRESS + "ADDRESS]" + + "[" + PREFIX_NAME + "NAME]" + + "[" + PREFIX_ASKING_PRICE + "ASKING PRICE] \n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_LISTING_ID + "3412 " + + PREFIX_ADDRESS + " 100 Charming Avenue " + + PREFIX_NAME + "Jake Holt " + + PREFIX_ASKING_PRICE + "1 "; + + public static final String MESSAGE_EDIT_LISTING_SUCCESS = "Edited Listing: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_LISTING = "This Listing already exists in the address book."; + + private final Index index; + private final EditListingDescriptor editListingDescriptor; + + /** + * @param index of the person in the filtered person list to edit + * @param editListingDescriptor details to edit the person with + */ + public EditListingCommand(Index index, EditListingDescriptor editListingDescriptor) { + requireNonNull(index); + requireNonNull(editListingDescriptor); + + this.index = index; + this.editListingDescriptor = new EditListingDescriptor(editListingDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredListingList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_LISTING_DISPLAYED_INDEX); + } + + Listing listingToEdit = lastShownList.get(index.getZeroBased()); + Listing editedListing = createEditedListing(listingToEdit, editListingDescriptor); + + if (!listingToEdit.isSameListing(editedListing) && model.hasListing(editedListing)) { + throw new CommandException(MESSAGE_DUPLICATE_LISTING); + } + + model.setListing(listingToEdit, editedListing); + model.updateFilteredListingList(PREDICATE_SHOW_ALL_LISTINGS); + return new CommandResult(String.format(MESSAGE_EDIT_LISTING_SUCCESS, editedListing)); + } + + /** + * Creates and returns a {@code Listing} with the details of {@code listingToEdit} + * edited with {@code editListingDescriptor}. + */ + private static Listing createEditedListing(Listing listingToEdit, EditListingDescriptor editListingDescriptor) { + assert listingToEdit != null; + + ListingId updatedId = editListingDescriptor.getId().orElse(listingToEdit.getId()); + Address updatedAddress = editListingDescriptor.getAddress().orElse(listingToEdit.getAddress()); + Name updatedName = editListingDescriptor.getName().orElse(listingToEdit.getName()); + Price updatedAskingPrice = editListingDescriptor.getAskingPrice().orElse(listingToEdit.getAskingPrice()); + Set updatedTags = editListingDescriptor.getTags().orElse(listingToEdit.getTags()); + + Listing listing = new Listing(updatedId, updatedAddress, updatedName, updatedAskingPrice); + listing.addTags(updatedTags); + return listing; + } + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditListingCommand)) { + return false; + } + + // state check + EditListingCommand e = (EditListingCommand) other; + return index.equals(e.index) + && editListingDescriptor.equals(e.editListingDescriptor); + } + + /** + * Stores the details to edit the listing with. Each non-empty field value will replace the + * corresponding field value of the person. + */ + public static class EditListingDescriptor { + private ListingId id; + private Name name; + private Address address; + private Price askingPrice; + private Set tags; + + public EditListingDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditListingDescriptor(EditListingDescriptor toCopy) { + setId(toCopy.id); + setName(toCopy.name); + setAddress(toCopy.address); + setAskingPrice(toCopy.askingPrice); + setTags(toCopy.tags); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(id, name, address, askingPrice, tags); + } + + public void setName(Name name) { + this.name = name; + } + + public Optional getName() { + return Optional.ofNullable(name); + } + + public void setId(ListingId id) { + this.id = id; + } + + public Optional getId() { + return Optional.ofNullable(id); + } + + public void setAddress(Address address) { + this.address = address; + } + + public Optional
getAddress() { + return Optional.ofNullable(address); + } + + public void setAskingPrice(Price askingPrice) { + this.askingPrice = askingPrice; + } + + public Optional getAskingPrice() { + return Optional.ofNullable(askingPrice); + } + + /** + * Sets {@code tags} to this object's {@code tags}. + * A defensive copy of {@code tags} is used internally. + */ + public void setTags(Set tags) { + this.tags = (tags != null) ? new HashSet<>(tags) : null; + } + + /** + * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * Returns {@code Optional#empty()} if {@code tags} is null. + */ + public Optional> getTags() { + return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditListingDescriptor)) { + return false; + } + + // state check + EditListingDescriptor e = (EditListingDescriptor) other; + + return getId().equals(e.getId()) + && getName().equals(e.getName()) + && getAddress().equals(e.getAddress()) + && getAskingPrice().equals(e.getAskingPrice()) + && getTags().equals(e.getTags()); + } + } +} diff --git a/src/main/java/seedu/realtime/logic/commands/EditMeetingCommand.java b/src/main/java/seedu/realtime/logic/commands/EditMeetingCommand.java new file mode 100644 index 00000000000..ee5ffa87a24 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/EditMeetingCommand.java @@ -0,0 +1,177 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_DATETIME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +import seedu.realtime.commons.core.Messages; +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.commons.util.CollectionUtil; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.meeting.Meeting; +import seedu.realtime.model.person.Name; + + +/** + * Edits the details of an existing meeting in the address book. + */ +public class EditMeetingCommand extends Command { + public static final String COMMAND_WORD = "editM"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the meeting identified " + + "by the index number used in the displayed meeting list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_LISTING_ID + "ADDRESS LISTING ID] " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_DATETIME + "Date and Time]...\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_DATETIME + "2022-09-20 12:59"; + + public static final String MESSAGE_EDIT_MEETING_SUCCESS = "Edited Meeting: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_MEETING = "This meeting already exists in the address book"; + + private final Index index; + private final EditMeetingCommand.EditMeetingDescriptor editMeetingDescriptor; + + + /** + * Constructor + * @param index index of Meeting to edit + * @param editMeetingDescriptor descriptor + */ + public EditMeetingCommand(Index index, EditMeetingCommand.EditMeetingDescriptor editMeetingDescriptor) { + requireNonNull(index); + requireNonNull(editMeetingDescriptor); + this.index = index; + this.editMeetingDescriptor = new EditMeetingCommand.EditMeetingDescriptor(editMeetingDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastshownlist = model.getFilteredMeetingList(); + + if (index.getZeroBased() >= lastshownlist.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_MEETING_DISPLAYED_INDEX); + } + + Meeting toEdit = lastshownlist.get(index.getZeroBased()); + Meeting edited = createEditedMeeting(toEdit, editMeetingDescriptor); + + if (!toEdit.isSameMeeting(edited) && model.hasMeeting(edited)) { + throw new CommandException(MESSAGE_DUPLICATE_MEETING); + } + + model.setMeeting(toEdit, edited); + model.updateFilteredMeetingList(Model.PREDICATE_SHOW_ALL_MEETINGS); + return new CommandResult(String.format(MESSAGE_EDIT_MEETING_SUCCESS, edited)); + } + + private static Meeting createEditedMeeting(Meeting toedit, + EditMeetingCommand.EditMeetingDescriptor editMeetingDescriptor) { + assert toedit != null; + + Name updatedName = editMeetingDescriptor.getName().orElse(toedit.getClient()); + ListingId updatedListingId = editMeetingDescriptor.getListing().orElse(toedit.getListing()); + LocalDateTime updatedDateTime = editMeetingDescriptor.getDateTime().orElse(toedit.getdateTime()); + + return new Meeting(updatedName, updatedListingId, updatedDateTime); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditMeetingCommand)) { + return false; + } + + // state check + EditMeetingCommand e = (EditMeetingCommand) other; + return index.equals(e.index) + && editMeetingDescriptor.equals(e.editMeetingDescriptor); + } + + /** + * Stores the details to edit the meeting with. Each non-empty field value will replace the + * corresponding field value of the meeting. + */ + public static class EditMeetingDescriptor { + private Name name; + private ListingId listing; + private LocalDateTime dateTime; + + public EditMeetingDescriptor() {} + + /** + * Constructor. + */ + public EditMeetingDescriptor(EditMeetingCommand.EditMeetingDescriptor toCopy) { + setName(toCopy.name); + setListing(toCopy.listing); + setDateTime(toCopy.dateTime); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(name, listing, dateTime); + } + + public void setName(Name name) { + this.name = name; + } + + public Optional getName() { + return Optional.ofNullable(name); + } + + public void setListing(ListingId listing) { + this.listing = listing; + } + + public Optional getListing() { + return Optional.ofNullable(listing); + } + + public void setDateTime(LocalDateTime newDate) { + this.dateTime = newDate; + } + + public Optional getDateTime() { + return Optional.ofNullable(dateTime); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + // instanceof handles nulls + if (!(other instanceof EditMeetingCommand.EditMeetingDescriptor)) { + return false; + } + + // state check + EditMeetingCommand.EditMeetingDescriptor e = (EditMeetingCommand.EditMeetingDescriptor) other; + + return getName().equals(e.getName()) + && getListing().equals(e.getListing()) + && getDateTime().equals(e.getDateTime()); + } + } +} diff --git a/src/main/java/seedu/realtime/logic/commands/EditOfferCommand.java b/src/main/java/seedu/realtime/logic/commands/EditOfferCommand.java new file mode 100644 index 00000000000..bb0fc72a187 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/EditOfferCommand.java @@ -0,0 +1,183 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_OFFER; +import static seedu.realtime.model.Model.PREDICATE_SHOW_ALL_OFFERS; + +import java.util.List; +import java.util.Optional; + +import seedu.realtime.commons.core.Messages; +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.commons.util.CollectionUtil; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.offer.Offer; +import seedu.realtime.model.offer.Price; +import seedu.realtime.model.person.Name; + +/** + * Edits the details of an existing offer in the address book. + */ +public class EditOfferCommand extends Command { + public static final String COMMAND_WORD = "editO"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the offer identified " + + "by the index number used in the displayed offer list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_LISTING_ID + "ADDRESS LISTING ID] " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_OFFER + "OFFER PRICE]...\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_OFFER + "650000"; + + public static final String MESSAGE_EDIT_OFFER_SUCCESS = "Edited Offer: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_OFFER = "This offer already exists in the address book."; + + private final Index index; + private final EditOfferCommand.EditOfferDescriptor editOfferDescriptor; + + /** + * @param index of the offer in the filtered offer list to edit + * @param editOfferDescriptor details to edit the offer with + */ + public EditOfferCommand(Index index, EditOfferCommand.EditOfferDescriptor editOfferDescriptor) { + requireNonNull(index); + requireNonNull(editOfferDescriptor); + + this.index = index; + this.editOfferDescriptor = new EditOfferCommand.EditOfferDescriptor(editOfferDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredOfferList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_OFFER_DISPLAYED_INDEX); + } + + Offer offerToEdit = lastShownList.get(index.getZeroBased()); + Offer editedOffer = createEditedOffer(offerToEdit, editOfferDescriptor); + + if (!offerToEdit.isSameOffer(editedOffer) && model.hasOffer(editedOffer)) { + throw new CommandException(MESSAGE_DUPLICATE_OFFER); + } + + model.setOffer(offerToEdit, editedOffer); + model.updateFilteredOfferList(PREDICATE_SHOW_ALL_OFFERS); + return new CommandResult(String.format(MESSAGE_EDIT_OFFER_SUCCESS, editedOffer)); + } + + /** + * Creates and returns an {@code Offer} with the details of {@code offerToEdit} + * edited with {@code editOfferDescriptor}. + */ + private static Offer createEditedOffer(Offer offerToEdit, + EditOfferCommand.EditOfferDescriptor editOfferDescriptor) { + assert offerToEdit != null; + + Name updatedName = editOfferDescriptor.getName().orElse(offerToEdit.getClient()); + ListingId updatedListingId = editOfferDescriptor.getListing().orElse(offerToEdit.getListing()); + Price offerPrice = editOfferDescriptor.getOfferPrice().orElse(offerToEdit.getOfferPrice()); + + return new Offer(updatedName, updatedListingId, offerPrice); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditOfferCommand)) { + return false; + } + + // state check + EditOfferCommand e = (EditOfferCommand) other; + return index.equals(e.index) + && editOfferDescriptor.equals(e.editOfferDescriptor); + } + + /** + * Stores the details to edit the offer with. Each non-empty field value will replace the + * corresponding field value of the offer. + */ + public static class EditOfferDescriptor { + private Name name; + private ListingId listing; + private Price offerPrice; + + public EditOfferDescriptor() {} + + /** + * Copy constructor. + */ + public EditOfferDescriptor(EditOfferCommand.EditOfferDescriptor toCopy) { + setName(toCopy.name); + setListing(toCopy.listing); + setOfferPrice(toCopy.offerPrice); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(name, listing, offerPrice); + } + + public void setName(Name name) { + this.name = name; + } + + public Optional getName() { + return Optional.ofNullable(name); + } + + public void setListing(ListingId listing) { + this.listing = listing; + } + + public Optional getListing() { + return Optional.ofNullable(listing); + } + + public void setOfferPrice(Price offerPrice) { + this.offerPrice = offerPrice; + } + + public Optional getOfferPrice() { + return Optional.ofNullable(offerPrice); + } + + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditOfferCommand.EditOfferDescriptor)) { + return false; + } + + // state check + EditOfferCommand.EditOfferDescriptor e = (EditOfferCommand.EditOfferDescriptor) other; + + return getName().equals(e.getName()) + && getListing().equals(e.getListing()) + && getOfferPrice().equals(e.getOfferPrice()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/realtime/logic/commands/EditPersonCommand.java similarity index 74% rename from src/main/java/seedu/address/logic/commands/EditCommand.java rename to src/main/java/seedu/realtime/logic/commands/EditPersonCommand.java index 7e36114902f..1df59c591e6 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/realtime/logic/commands/EditPersonCommand.java @@ -1,12 +1,12 @@ -package seedu.address.logic.commands; +package seedu.realtime.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.realtime.model.Model.PREDICATE_SHOW_ALL_PERSONS; import java.util.Collections; import java.util.HashSet; @@ -14,37 +14,37 @@ import java.util.Optional; import java.util.Set; -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.CollectionUtil; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import seedu.realtime.commons.core.Messages; +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.commons.util.CollectionUtil; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.model.Model; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Email; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.person.Person; +import seedu.realtime.model.person.Phone; +import seedu.realtime.model.tag.Tag; /** * Edits the details of an existing person in the address book. */ -public class EditCommand extends Command { +public class EditPersonCommand extends Command { - public static final String COMMAND_WORD = "edit"; + public static final String COMMAND_WORD = "editP"; public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified " - + "by the index number used in the displayed person list. " - + "Existing values will be overwritten by the input values.\n" - + "Parameters: INDEX (must be a positive integer) " - + "[" + PREFIX_NAME + "NAME] " - + "[" + PREFIX_PHONE + "PHONE] " - + "[" + PREFIX_EMAIL + "EMAIL] " - + "[" + PREFIX_ADDRESS + "ADDRESS] " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " 1 " - + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; + + "by the index number used in the displayed person list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_PHONE + "PHONE] " + + "[" + PREFIX_EMAIL + "EMAIL] " + + "[" + PREFIX_ADDRESS + "ADDRESS] " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_PHONE + "91234567 " + + PREFIX_EMAIL + "johndoe@example.com"; public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; @@ -57,7 +57,7 @@ public class EditCommand extends Command { * @param index of the person in the filtered person list to edit * @param editPersonDescriptor details to edit the person with */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { + public EditPersonCommand(Index index, EditPersonDescriptor editPersonDescriptor) { requireNonNull(index); requireNonNull(editPersonDescriptor); @@ -110,14 +110,14 @@ public boolean equals(Object other) { } // instanceof handles nulls - if (!(other instanceof EditCommand)) { + if (!(other instanceof EditPersonCommand)) { return false; } // state check - EditCommand e = (EditCommand) other; + EditPersonCommand e = (EditPersonCommand) other; return index.equals(e.index) - && editPersonDescriptor.equals(e.editPersonDescriptor); + && editPersonDescriptor.equals(e.editPersonDescriptor); } /** @@ -217,10 +217,10 @@ public boolean equals(Object other) { EditPersonDescriptor e = (EditPersonDescriptor) other; return getName().equals(e.getName()) - && getPhone().equals(e.getPhone()) - && getEmail().equals(e.getEmail()) - && getAddress().equals(e.getAddress()) - && getTags().equals(e.getTags()); + && getPhone().equals(e.getPhone()) + && getEmail().equals(e.getEmail()) + && getAddress().equals(e.getAddress()) + && getTags().equals(e.getTags()); } } } diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/realtime/logic/commands/ExitCommand.java similarity index 83% rename from src/main/java/seedu/address/logic/commands/ExitCommand.java rename to src/main/java/seedu/realtime/logic/commands/ExitCommand.java index 3dd85a8ba90..2b2ce066f8e 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/realtime/logic/commands/ExitCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package seedu.realtime.logic.commands; -import seedu.address.model.Model; +import seedu.realtime.model.Model; /** * Terminates the program. diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/realtime/logic/commands/FindClientCommand.java similarity index 53% rename from src/main/java/seedu/address/logic/commands/FindCommand.java rename to src/main/java/seedu/realtime/logic/commands/FindClientCommand.java index d6b19b0a0de..08b080b779f 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/seedu/realtime/logic/commands/FindClientCommand.java @@ -1,42 +1,42 @@ -package seedu.address.logic.commands; +package seedu.realtime.logic.commands; import static java.util.Objects.requireNonNull; -import seedu.address.commons.core.Messages; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.realtime.commons.core.Messages; +import seedu.realtime.model.Model; +import seedu.realtime.model.person.NameContainsKeywordsPredicate; /** - * Finds and lists all persons in address book whose name contains any of the argument keywords. + * Finds and lists all clients in address book whose name contains any of the argument keywords. * Keyword matching is case insensitive. */ -public class FindCommand extends Command { +public class FindClientCommand extends Command { - public static final String COMMAND_WORD = "find"; + public static final String COMMAND_WORD = "findC"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all clients whose names contain any of " + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + "Example: " + COMMAND_WORD + " alice bob charlie"; private final NameContainsKeywordsPredicate predicate; - public FindCommand(NameContainsKeywordsPredicate predicate) { + public FindClientCommand(NameContainsKeywordsPredicate predicate) { this.predicate = predicate; } @Override public CommandResult execute(Model model) { requireNonNull(model); - model.updateFilteredPersonList(predicate); + model.updateFilteredClientList(predicate); return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); + String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredClientList().size())); } @Override public boolean equals(Object other) { return other == this // short circuit if same object - || (other instanceof FindCommand // instanceof handles nulls - && predicate.equals(((FindCommand) other).predicate)); // state check + || (other instanceof FindClientCommand // instanceof handles nulls + && predicate.equals(((FindClientCommand) other).predicate)); // state check } } diff --git a/src/main/java/seedu/realtime/logic/commands/FindOffersForListingCommand.java b/src/main/java/seedu/realtime/logic/commands/FindOffersForListingCommand.java new file mode 100644 index 00000000000..1207cfc1663 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/FindOffersForListingCommand.java @@ -0,0 +1,42 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.realtime.commons.core.Messages; +import seedu.realtime.model.Model; +import seedu.realtime.model.offer.OfferContainsListingIdPredicate; + +/** + * Finds and lists all offers in address book for the specified Listing ID. + * Keyword matching is case-insensitive. + */ +public class FindOffersForListingCommand extends Command { + + public static final String COMMAND_WORD = "findO"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all offers in address book for " + + "the specified Listing ID (case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: LISTING ID \n" + + "Example: " + COMMAND_WORD + " 007"; + + private final OfferContainsListingIdPredicate predicate; + + public FindOffersForListingCommand(OfferContainsListingIdPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredOfferList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_OFFERS_LISTED_OVERVIEW, model.getFilteredOfferList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindOffersForListingCommand // instanceof handles nulls + && predicate.equals(((FindOffersForListingCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/realtime/logic/commands/HelpCommand.java similarity index 87% rename from src/main/java/seedu/address/logic/commands/HelpCommand.java rename to src/main/java/seedu/realtime/logic/commands/HelpCommand.java index bf824f91bd0..49e4bfd11a6 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/realtime/logic/commands/HelpCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package seedu.realtime.logic.commands; -import seedu.address.model.Model; +import seedu.realtime.model.Model; /** * Format full help instructions for every command for display. diff --git a/src/main/java/seedu/realtime/logic/commands/ViewClientListCommand.java b/src/main/java/seedu/realtime/logic/commands/ViewClientListCommand.java new file mode 100644 index 00000000000..77f0ccca6e4 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/ViewClientListCommand.java @@ -0,0 +1,24 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.model.Model.PREDICATE_SHOW_ALL_CLIENTS; + +import seedu.realtime.model.Model; + +/** + * Lists all clients in the address book to the user. + */ +public class ViewClientListCommand extends Command { + + public static final String COMMAND_WORD = "listC"; + + public static final String MESSAGE_SUCCESS = "Listed all clients"; + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredClientList(PREDICATE_SHOW_ALL_CLIENTS); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/realtime/logic/commands/ViewListingClientsCommand.java b/src/main/java/seedu/realtime/logic/commands/ViewListingClientsCommand.java new file mode 100644 index 00000000000..dd388399e78 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/ViewListingClientsCommand.java @@ -0,0 +1,44 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; + +import seedu.realtime.model.Model; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.person.Name; + +/** + * Lists all persons in the address book to the user. + */ +public class ViewListingClientsCommand extends Command { + + public static final String COMMAND_WORD = "listLC"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Views interested clients of this listing. " + + "Parameters: " + + PREFIX_LISTING_ID + "ID \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_LISTING_ID + "3412 "; + + public static final String MESSAGE_SUCCESS = "Listed all listing clients"; + + private final ListingId id; + /** + * Creates an ViewListingClientsCommand to view interested clients of the specified {@code Listing} + */ + public ViewListingClientsCommand(ListingId id) { + this.id = id; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + final StringBuilder builder = new StringBuilder(); + for (Name client : model.getListing(id).getInterestedClients()) { + builder.append(client).append("\n"); + } + return new CommandResult(MESSAGE_SUCCESS + "\n" + builder); + } +} + + diff --git a/src/main/java/seedu/realtime/logic/commands/ViewListingsCommand.java b/src/main/java/seedu/realtime/logic/commands/ViewListingsCommand.java new file mode 100644 index 00000000000..90399608042 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/commands/ViewListingsCommand.java @@ -0,0 +1,25 @@ +package seedu.realtime.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.model.Model.PREDICATE_SHOW_ALL_LISTINGS; + +import seedu.realtime.model.Model; + +/** + * Lists all persons in the address book to the user. + */ +public class ViewListingsCommand extends Command { + + public static final String COMMAND_WORD = "listL"; + + public static final String MESSAGE_SUCCESS = "Listed all listings"; + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredListingList(PREDICATE_SHOW_ALL_LISTINGS); + return new CommandResult(MESSAGE_SUCCESS); + } +} + diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/realtime/logic/commands/ViewPersonListCommand.java similarity index 61% rename from src/main/java/seedu/address/logic/commands/ListCommand.java rename to src/main/java/seedu/realtime/logic/commands/ViewPersonListCommand.java index 84be6ad2596..ccbdaa5dbec 100644 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ b/src/main/java/seedu/realtime/logic/commands/ViewPersonListCommand.java @@ -1,16 +1,16 @@ -package seedu.address.logic.commands; +package seedu.realtime.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static seedu.realtime.model.Model.PREDICATE_SHOW_ALL_PERSONS; -import seedu.address.model.Model; +import seedu.realtime.model.Model; /** * Lists all persons in the address book to the user. */ -public class ListCommand extends Command { +public class ViewPersonListCommand extends Command { - public static final String COMMAND_WORD = "list"; + public static final String COMMAND_WORD = "listP"; public static final String MESSAGE_SUCCESS = "Listed all persons"; diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/realtime/logic/commands/exceptions/CommandException.java similarity index 89% rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java rename to src/main/java/seedu/realtime/logic/commands/exceptions/CommandException.java index a16bd14f2cd..ede7550dab9 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/seedu/realtime/logic/commands/exceptions/CommandException.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands.exceptions; +package seedu.realtime.logic.commands.exceptions; /** * Represents an error which occurs during execution of a {@link Command}. diff --git a/src/main/java/seedu/realtime/logic/parser/AddClientCommandParser.java b/src/main/java/seedu/realtime/logic/parser/AddClientCommandParser.java new file mode 100644 index 00000000000..6fb0b1bde32 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/AddClientCommandParser.java @@ -0,0 +1,60 @@ +package seedu.realtime.logic.parser; + +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Set; +import java.util.stream.Stream; + +import seedu.realtime.logic.commands.AddClientCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Client; +import seedu.realtime.model.person.Email; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.person.Phone; +import seedu.realtime.model.tag.Tag; + +/** + * Parses input arguments and creates a new AddClientCommand object + */ +public class AddClientCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddClientCommand + * and returns an AddClientCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddClientCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddClientCommand.MESSAGE_USAGE)); + } + + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); + Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); + Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); + Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); + + Client client = new Client(name, phone, email, address, tagList); + + return new AddClientCommand(client); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/realtime/logic/parser/AddInterestedClientCommandParser.java b/src/main/java/seedu/realtime/logic/parser/AddInterestedClientCommandParser.java new file mode 100644 index 00000000000..38d3d780f28 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/AddInterestedClientCommandParser.java @@ -0,0 +1,48 @@ +package seedu.realtime.logic.parser; + +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; + +import java.util.stream.Stream; + +import seedu.realtime.logic.commands.AddClientCommand; +import seedu.realtime.logic.commands.AddInterestedClientCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.person.Name; + + +/** + * Parses input arguments and creates a new AddInterestedClientCommand object + */ +public class AddInterestedClientCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddInterestedClientCommand + * and returns an AddInterestedClientCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddInterestedClientCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_LISTING_ID); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_LISTING_ID) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddClientCommand.MESSAGE_USAGE)); + } + + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + ListingId listingId = ParserUtil.parseListingId(argMultimap.getValue(PREFIX_LISTING_ID).get()); + + return new AddInterestedClientCommand(name, listingId); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/realtime/logic/parser/AddListingCommandParser.java b/src/main/java/seedu/realtime/logic/parser/AddListingCommandParser.java new file mode 100644 index 00000000000..913e5749590 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/AddListingCommandParser.java @@ -0,0 +1,53 @@ +package seedu.realtime.logic.parser; + +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ASKING_PRICE; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; + +import java.util.stream.Stream; + +import seedu.realtime.logic.commands.AddListingCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.offer.Price; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Name; + +/** + * Parses input arguments and creates a new AddCommand object + */ +public class AddListingCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddListingCommand + * and returns an AddListingCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddListingCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_LISTING_ID, PREFIX_ADDRESS, PREFIX_NAME, PREFIX_ASKING_PRICE); + + if (!arePrefixesPresent(argMultimap, PREFIX_LISTING_ID, PREFIX_ADDRESS, PREFIX_NAME, PREFIX_ASKING_PRICE) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddListingCommand.MESSAGE_USAGE)); + } + + ListingId id = ParserUtil.parseListingId(argMultimap.getValue(PREFIX_LISTING_ID).get()); + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); + Price askingPrice = ParserUtil.parsePrice(argMultimap.getValue(PREFIX_ASKING_PRICE).get()); + + return new AddListingCommand(id, address, name, askingPrice); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/realtime/logic/parser/AddMeetingCommandParser.java b/src/main/java/seedu/realtime/logic/parser/AddMeetingCommandParser.java new file mode 100644 index 00000000000..72f35df2da3 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/AddMeetingCommandParser.java @@ -0,0 +1,52 @@ +package seedu.realtime.logic.parser; + +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_DATETIME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; + +import java.time.LocalDateTime; +import java.util.stream.Stream; + +import seedu.realtime.logic.commands.AddMeetingCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.meeting.Meeting; +import seedu.realtime.model.person.Name; + +/** + * Parses input arguments and create a new AddMeetingCommand object. + */ +public class AddMeetingCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of AddMeetingCommand + * and returns an AddMeetingCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format + */ + public AddMeetingCommand parse(String args) throws ParseException { + ArgumentMultimap argMultiMap = + ArgumentTokenizer.tokenize(args, PREFIX_LISTING_ID, PREFIX_NAME, PREFIX_DATETIME); + + if (!arePrefixesPresent(argMultiMap, PREFIX_LISTING_ID, PREFIX_NAME, PREFIX_DATETIME) + || !argMultiMap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddMeetingCommand.MESSAGE_USAGE)); + } + + ListingId listing = ParserUtil.parseListingId(argMultiMap.getValue(PREFIX_LISTING_ID).get()); + Name name = ParserUtil.parseName(argMultiMap.getValue(PREFIX_NAME).get()); + LocalDateTime dateTime = ParserUtil.parseDateTime(argMultiMap.getValue(PREFIX_DATETIME).get()); + + Meeting meeting = new Meeting(name, listing, dateTime); + return new AddMeetingCommand(meeting); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/realtime/logic/parser/AddOfferCommandParser.java b/src/main/java/seedu/realtime/logic/parser/AddOfferCommandParser.java new file mode 100644 index 00000000000..572d8eb9be3 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/AddOfferCommandParser.java @@ -0,0 +1,54 @@ +package seedu.realtime.logic.parser; + +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_OFFER; + +import java.util.stream.Stream; + +import seedu.realtime.logic.commands.AddOfferCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.offer.Offer; +import seedu.realtime.model.offer.Price; +import seedu.realtime.model.person.Name; + +/** + * Parses input arguments and creates a new AddOfferCommand object + */ +public class AddOfferCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddOfferCommand + * and returns an AddOfferCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public AddOfferCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_LISTING_ID, PREFIX_NAME, PREFIX_OFFER); + + if (!arePrefixesPresent(argMultimap, PREFIX_LISTING_ID, PREFIX_NAME, PREFIX_OFFER) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddOfferCommand.MESSAGE_USAGE)); + } + + ListingId listing = ParserUtil.parseListingId(argMultimap.getValue(PREFIX_LISTING_ID).get()); + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + Price offerPrice = ParserUtil.parsePrice(argMultimap.getValue(PREFIX_OFFER).get()); + + Offer offer = new Offer(name, listing, offerPrice); + + return new AddOfferCommand(offer); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/realtime/logic/parser/AddPersonCommandParser.java similarity index 55% rename from src/main/java/seedu/address/logic/parser/AddCommandParser.java rename to src/main/java/seedu/realtime/logic/parser/AddPersonCommandParser.java index 3b8bfa035e8..4a8ed301879 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/seedu/realtime/logic/parser/AddPersonCommandParser.java @@ -1,41 +1,41 @@ -package seedu.address.logic.parser; +package seedu.realtime.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_TAG; import java.util.Set; import java.util.stream.Stream; -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import seedu.realtime.logic.commands.AddPersonCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Email; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.person.Person; +import seedu.realtime.model.person.Phone; +import seedu.realtime.model.tag.Tag; /** - * Parses input arguments and creates a new AddCommand object + * Parses input arguments and creates a new AddPersonCommand object */ -public class AddCommandParser implements Parser { +public class AddPersonCommandParser implements Parser { /** - * Parses the given {@code String} of arguments in the context of the AddCommand - * and returns an AddCommand object for execution. + * Parses the given {@code String} of arguments in the context of the AddPersonCommand + * and returns an AddPersonCommand object for execution. * @throws ParseException if the user input does not conform the expected format */ - public AddCommand parse(String args) throws ParseException { + public AddPersonCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) || !argMultimap.getPreamble().isEmpty()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddPersonCommand.MESSAGE_USAGE)); } Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); @@ -46,7 +46,7 @@ public AddCommand parse(String args) throws ParseException { Person person = new Person(name, phone, email, address, tagList); - return new AddCommand(person); + return new AddPersonCommand(person); } /** diff --git a/src/main/java/seedu/realtime/logic/parser/AddTagsToListingCommandParser.java b/src/main/java/seedu/realtime/logic/parser/AddTagsToListingCommandParser.java new file mode 100644 index 00000000000..240f8105459 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/AddTagsToListingCommandParser.java @@ -0,0 +1,48 @@ +package seedu.realtime.logic.parser; + +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Set; +import java.util.stream.Stream; + +import seedu.realtime.logic.commands.AddTagsToListingCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.tag.Tag; + +/** + * Parses input arguments and creates a new AddTagsToListingCommand object. + */ +public class AddTagsToListingCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddTagsToListingCommand + * and returns an AddTagsToListingCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddTagsToListingCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_TAG, PREFIX_LISTING_ID); + + if (!arePrefixesPresent(argMultimap, PREFIX_TAG, PREFIX_LISTING_ID) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddTagsToListingCommand.MESSAGE_USAGE)); + } + + Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); + ListingId listingId = ParserUtil.parseListingId(argMultimap.getValue(PREFIX_LISTING_ID).get()); + + return new AddTagsToListingCommand(tagList, listingId); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/seedu/realtime/logic/parser/ArgumentMultimap.java similarity index 93% rename from src/main/java/seedu/address/logic/parser/ArgumentMultimap.java rename to src/main/java/seedu/realtime/logic/parser/ArgumentMultimap.java index 954c8e18f8e..3412fed3495 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/seedu/realtime/logic/parser/ArgumentMultimap.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.realtime.logic.parser; import java.util.ArrayList; import java.util.HashMap; @@ -36,9 +36,11 @@ public void put(Prefix prefix, String argValue) { */ public Optional getValue(Prefix prefix) { List values = getAllValues(prefix); - return values.isEmpty() ? Optional.empty() : Optional.of(values.get(values.size() - 1)); + return values.isEmpty() ? Optional.empty() + : Optional.of(values.get(values.size() - 1)); } + /** * Returns all values of {@code prefix}. * If the prefix does not exist or has no values, this will return an empty list. diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/realtime/logic/parser/ArgumentTokenizer.java similarity index 99% rename from src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java rename to src/main/java/seedu/realtime/logic/parser/ArgumentTokenizer.java index 5c9aebfa488..8a30973440b 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/seedu/realtime/logic/parser/ArgumentTokenizer.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.realtime.logic.parser; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/seedu/realtime/logic/parser/CliSyntax.java b/src/main/java/seedu/realtime/logic/parser/CliSyntax.java new file mode 100644 index 00000000000..b08fcc92fb8 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/CliSyntax.java @@ -0,0 +1,22 @@ +package seedu.realtime.logic.parser; + +/** + * Contains Command Line Interface (CLI) syntax definitions common to multiple commands + */ +public class CliSyntax { + + /* Prefix definitions */ + public static final Prefix PREFIX_NAME = new Prefix("n/"); + public static final Prefix PREFIX_PHONE = new Prefix("p/"); + public static final Prefix PREFIX_EMAIL = new Prefix("e/"); + public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); + public static final Prefix PREFIX_TAG = new Prefix("t/"); + public static final Prefix PREFIX_ID = new Prefix("id/"); + public static final Prefix PREFIX_CLIENT = new Prefix("cli/"); + public static final Prefix PREFIX_PERSON = new Prefix("per/"); + public static final Prefix PREFIX_ASKING_PRICE = new Prefix("ap/"); + public static final Prefix PREFIX_OFFER = new Prefix("o/"); + public static final Prefix PREFIX_LISTING_ID = new Prefix("l/"); + public static final Prefix PREFIX_DATETIME = new Prefix("d/"); + +} diff --git a/src/main/java/seedu/realtime/logic/parser/DeleteClientCommandParser.java b/src/main/java/seedu/realtime/logic/parser/DeleteClientCommandParser.java new file mode 100644 index 00000000000..e7a92c3fcfa --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/DeleteClientCommandParser.java @@ -0,0 +1,29 @@ +package seedu.realtime.logic.parser; + +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.DeleteClientCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteCommand object + */ +public class DeleteClientCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteClientCommand + * and returns a DeleteClientCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteClientCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteClientCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteClientCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/realtime/logic/parser/DeleteListingCommandParser.java b/src/main/java/seedu/realtime/logic/parser/DeleteListingCommandParser.java new file mode 100644 index 00000000000..ba67d93a6dd --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/DeleteListingCommandParser.java @@ -0,0 +1,22 @@ +package seedu.realtime.logic.parser; + +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.DeleteListingCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteCommand object + */ +public class DeleteListingCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteListingCommand + * and returns a DeleteListingCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteListingCommand parse(String args) throws ParseException { + Index index = ParserUtil.parseIndex(args.trim()); + return new DeleteListingCommand(index); + } + +} diff --git a/src/main/java/seedu/realtime/logic/parser/DeleteMeetingCommandParser.java b/src/main/java/seedu/realtime/logic/parser/DeleteMeetingCommandParser.java new file mode 100644 index 00000000000..f5ccf6f0397 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/DeleteMeetingCommandParser.java @@ -0,0 +1,28 @@ +package seedu.realtime.logic.parser; + +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.DeleteMeetingCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteMeetingCommand object + */ +public class DeleteMeetingCommandParser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteMeetingCommand + * and returns a DeleteMeetingCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteMeetingCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteMeetingCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteMeetingCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/realtime/logic/parser/DeleteOfferCommandParser.java b/src/main/java/seedu/realtime/logic/parser/DeleteOfferCommandParser.java new file mode 100644 index 00000000000..f29a19142df --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/DeleteOfferCommandParser.java @@ -0,0 +1,28 @@ +package seedu.realtime.logic.parser; + +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.DeleteOfferCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteOfferCommand object + */ +public class DeleteOfferCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteOfferCommand + * and returns a DeleteOfferCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteOfferCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteOfferCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteOfferCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/realtime/logic/parser/DeletePersonCommandParser.java b/src/main/java/seedu/realtime/logic/parser/DeletePersonCommandParser.java new file mode 100644 index 00000000000..727b9c88792 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/DeletePersonCommandParser.java @@ -0,0 +1,29 @@ +package seedu.realtime.logic.parser; + +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.DeletePersonCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteCommand object + */ +public class DeletePersonCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeletePersonCommand + * and returns a DeletePersonCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeletePersonCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeletePersonCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeletePersonCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/realtime/logic/parser/EditClientCommandParser.java b/src/main/java/seedu/realtime/logic/parser/EditClientCommandParser.java new file mode 100644 index 00000000000..0682775e62c --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/EditClientCommandParser.java @@ -0,0 +1,84 @@ +package seedu.realtime.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.EditClientCommand; +import seedu.realtime.logic.commands.EditClientCommand.EditClientDescriptor; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.tag.Tag; + +/** + * Parses input arguments and creates a new EditCommand object + */ +public class EditClientCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditClientCommand + * and returns an EditClientCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditClientCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditClientCommand.MESSAGE_USAGE), pe); + } + + EditClientDescriptor editClientDescriptor = new EditClientDescriptor(); + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + editClientDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); + } + if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { + editClientDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); + } + if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { + editClientDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); + } + if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { + editClientDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); + } + parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editClientDescriptor::setTags); + + if (!editClientDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditClientCommand.MESSAGE_NOT_EDITED); + } + + return new EditClientCommand(index, editClientDescriptor); + } + + /** + * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. + * If {@code tags} contain only one element which is an empty string, it will be parsed into a + * {@code Set} containing zero tags. + */ + private Optional> parseTagsForEdit(Collection tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + return Optional.empty(); + } + Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; + return Optional.of(ParserUtil.parseTags(tagSet)); + } + +} diff --git a/src/main/java/seedu/realtime/logic/parser/EditListingCommandParser.java b/src/main/java/seedu/realtime/logic/parser/EditListingCommandParser.java new file mode 100644 index 00000000000..4660ed1dd07 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/EditListingCommandParser.java @@ -0,0 +1,86 @@ +package seedu.realtime.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ASKING_PRICE; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.EditListingCommand; +import seedu.realtime.logic.commands.EditListingCommand.EditListingDescriptor; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.tag.Tag; + +/** + * Parses input arguments and creates a new EditCommand object + */ +public class EditListingCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditListingCommand + * and returns an EditListingCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditListingCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_LISTING_ID, PREFIX_ADDRESS, PREFIX_NAME, PREFIX_ASKING_PRICE); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditListingCommand.MESSAGE_USAGE), pe); + } + + EditListingDescriptor editListingDescriptor = new EditListingDescriptor(); + if (argMultimap.getValue(PREFIX_LISTING_ID).isPresent()) { + editListingDescriptor.setId(ParserUtil.parseListingId(argMultimap.getValue(PREFIX_LISTING_ID).get())); + } + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + editListingDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); + } + if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { + editListingDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); + } + if (argMultimap.getValue(PREFIX_ASKING_PRICE).isPresent()) { + editListingDescriptor.setAskingPrice( + ParserUtil.parsePrice(argMultimap.getValue(PREFIX_ASKING_PRICE).get())); + } + parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editListingDescriptor::setTags); + + if (!editListingDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditListingCommand.MESSAGE_NOT_EDITED); + } + + return new EditListingCommand(index, editListingDescriptor); + } + + /** + * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. + * If {@code tags} contain only one element which is an empty string, it will be parsed into a + * {@code Set} containing zero tags. + */ + private Optional> parseTagsForEdit(Collection tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + return Optional.empty(); + } + Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; + return Optional.of(ParserUtil.parseTags(tagSet)); + } + +} + diff --git a/src/main/java/seedu/realtime/logic/parser/EditMeetingCommandParser.java b/src/main/java/seedu/realtime/logic/parser/EditMeetingCommandParser.java new file mode 100644 index 00000000000..e828e89a52f --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/EditMeetingCommandParser.java @@ -0,0 +1,55 @@ +package seedu.realtime.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_DATETIME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; + +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.EditMeetingCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EditMeetingCommand object + */ +public class EditMeetingCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the EditMeetingCommand + * and returns an EditMeetingCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditMeetingCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_LISTING_ID, PREFIX_NAME, PREFIX_DATETIME); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditMeetingCommand.MESSAGE_USAGE), pe); + } + + EditMeetingCommand.EditMeetingDescriptor editMeetingDescriptor = new EditMeetingCommand.EditMeetingDescriptor(); + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + editMeetingDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); + } + if (argMultimap.getValue(PREFIX_LISTING_ID).isPresent()) { + editMeetingDescriptor.setListing(ParserUtil.parseListingId(argMultimap.getValue(PREFIX_LISTING_ID).get())); + } + if (argMultimap.getValue(PREFIX_DATETIME).isPresent()) { + editMeetingDescriptor.setDateTime(ParserUtil.parseDateTime(argMultimap.getValue(PREFIX_DATETIME).get())); + } + + if (!editMeetingDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditMeetingCommand.MESSAGE_NOT_EDITED); + } + + return new EditMeetingCommand(index, editMeetingDescriptor); + } + +} diff --git a/src/main/java/seedu/realtime/logic/parser/EditOfferCommandParser.java b/src/main/java/seedu/realtime/logic/parser/EditOfferCommandParser.java new file mode 100644 index 00000000000..5c6ccb7e173 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/EditOfferCommandParser.java @@ -0,0 +1,55 @@ +package seedu.realtime.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_OFFER; + +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.EditOfferCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EditOfferCommand object + */ +public class EditOfferCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the EditOfferCommand + * and returns an EditOfferCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditOfferCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_LISTING_ID, PREFIX_NAME, PREFIX_OFFER); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditOfferCommand.MESSAGE_USAGE), pe); + } + + EditOfferCommand.EditOfferDescriptor editOfferDescriptor = new EditOfferCommand.EditOfferDescriptor(); + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + editOfferDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); + } + if (argMultimap.getValue(PREFIX_LISTING_ID).isPresent()) { + editOfferDescriptor.setListing(ParserUtil.parseListingId(argMultimap.getValue(PREFIX_LISTING_ID).get())); + } + if (argMultimap.getValue(PREFIX_OFFER).isPresent()) { + editOfferDescriptor.setOfferPrice(ParserUtil.parsePrice(argMultimap.getValue(PREFIX_OFFER).get())); + } + + if (!editOfferDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditOfferCommand.MESSAGE_NOT_EDITED); + } + + return new EditOfferCommand(index, editOfferDescriptor); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/realtime/logic/parser/EditPersonCommandParser.java similarity index 64% rename from src/main/java/seedu/address/logic/parser/EditCommandParser.java rename to src/main/java/seedu/realtime/logic/parser/EditPersonCommandParser.java index 845644b7dea..84bef8e6419 100644 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ b/src/main/java/seedu/realtime/logic/parser/EditPersonCommandParser.java @@ -1,35 +1,35 @@ -package seedu.address.logic.parser; +package seedu.realtime.logic.parser; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_TAG; import java.util.Collection; import java.util.Collections; import java.util.Optional; import java.util.Set; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.tag.Tag; +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.logic.commands.EditPersonCommand; +import seedu.realtime.logic.commands.EditPersonCommand.EditPersonDescriptor; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.tag.Tag; /** * Parses input arguments and creates a new EditCommand object */ -public class EditCommandParser implements Parser { +public class EditPersonCommandParser implements Parser { /** - * Parses the given {@code String} of arguments in the context of the EditCommand - * and returns an EditCommand object for execution. + * Parses the given {@code String} of arguments in the context of the EditPersonCommand + * and returns an EditPersonCommand object for execution. * @throws ParseException if the user input does not conform the expected format */ - public EditCommand parse(String args) throws ParseException { + public EditPersonCommand parse(String args) throws ParseException { requireNonNull(args); ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); @@ -39,7 +39,9 @@ public EditCommand parse(String args) throws ParseException { try { index = ParserUtil.parseIndex(argMultimap.getPreamble()); } catch (ParseException pe) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditPersonCommand.MESSAGE_USAGE), pe); } EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); @@ -58,10 +60,10 @@ public EditCommand parse(String args) throws ParseException { parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); if (!editPersonDescriptor.isAnyFieldEdited()) { - throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); + throw new ParseException(EditPersonCommand.MESSAGE_NOT_EDITED); } - return new EditCommand(index, editPersonDescriptor); + return new EditPersonCommand(index, editPersonDescriptor); } /** @@ -80,3 +82,4 @@ private Optional> parseTagsForEdit(Collection tags) throws Pars } } + diff --git a/src/main/java/seedu/realtime/logic/parser/FindClientCommandParser.java b/src/main/java/seedu/realtime/logic/parser/FindClientCommandParser.java new file mode 100644 index 00000000000..ea4957a55a2 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/FindClientCommandParser.java @@ -0,0 +1,33 @@ +package seedu.realtime.logic.parser; + +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.realtime.logic.commands.FindClientCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.person.NameContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindClientCommand object + */ +public class FindClientCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindClientCommand + * and returns a FindClientCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FindClientCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindClientCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = trimmedArgs.split("\\s+"); + + return new FindClientCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + } + +} diff --git a/src/main/java/seedu/realtime/logic/parser/FindOffersForListingCommandParser.java b/src/main/java/seedu/realtime/logic/parser/FindOffersForListingCommandParser.java new file mode 100644 index 00000000000..f2729094819 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/FindOffersForListingCommandParser.java @@ -0,0 +1,31 @@ +package seedu.realtime.logic.parser; + +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.List; + +import seedu.realtime.logic.commands.FindOffersForListingCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.offer.OfferContainsListingIdPredicate; + +/** + * Parses input arguments and creates a new FindOffersForListingCommand object + */ +public class FindOffersForListingCommandParser { + /** + * Parses the given {@code String} of arguments in the context of the FindOffersForListingCommand + * and returns a FindOffersForListingCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FindOffersForListingCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindOffersForListingCommand.MESSAGE_USAGE)); + } + + String[] listingId = trimmedArgs.split("\\s+"); + + return new FindOffersForListingCommand(new OfferContainsListingIdPredicate(List.of(listingId))); + } +} diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/realtime/logic/parser/Parser.java similarity index 72% rename from src/main/java/seedu/address/logic/parser/Parser.java rename to src/main/java/seedu/realtime/logic/parser/Parser.java index d6551ad8e3f..e56afae74c1 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/seedu/realtime/logic/parser/Parser.java @@ -1,7 +1,7 @@ -package seedu.address.logic.parser; +package seedu.realtime.logic.parser; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.realtime.logic.commands.Command; +import seedu.realtime.logic.parser.exceptions.ParseException; /** * Represents a Parser that is able to parse user input into a {@code Command} of type {@code T}. diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/realtime/logic/parser/ParserUtil.java similarity index 63% rename from src/main/java/seedu/address/logic/parser/ParserUtil.java rename to src/main/java/seedu/realtime/logic/parser/ParserUtil.java index b117acb9c55..7c08c2ab958 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/realtime/logic/parser/ParserUtil.java @@ -1,19 +1,24 @@ -package seedu.address.logic.parser; +package seedu.realtime.logic.parser; import static java.util.Objects.requireNonNull; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.Collection; import java.util.HashSet; import java.util.Set; -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import seedu.realtime.commons.core.index.Index; +import seedu.realtime.commons.util.StringUtil; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.offer.Price; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Email; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.person.Phone; +import seedu.realtime.model.tag.Tag; /** * Contains utility methods used for parsing strings in the various *Parser classes. @@ -22,6 +27,10 @@ public class ParserUtil { public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; + public static final String INVALID_DATETIME_FORMAT = "Date time format incorrect! Use yyyy-MM-dd HH:mm"; + + public static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm"; + /** * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be * trimmed. @@ -121,4 +130,43 @@ public static Set parseTags(Collection tags) throws ParseException } return tagSet; } + + /** + * Parses {@code int askingPrice} into a {@code Integer}. + */ + public static Price parsePrice(String offerPrice) throws ParseException { + requireNonNull(offerPrice); + String trimmedPrice = offerPrice.trim(); + if (!Price.isValidPrice(trimmedPrice)) { + throw new ParseException(Price.MESSAGE_CONSTRAINTS); + } + return new Price(trimmedPrice); + } + + /** + * Parses {@code int id} into a {@code ListingId}. + */ + public static ListingId parseListingId(String id) throws ParseException { + requireNonNull(id); + String trimmedListingID = id.trim(); + if (!ListingId.isValidListingId(trimmedListingID)) { + throw new ParseException(ListingId.MESSAGE_CONSTRAINTS); + } + return new ListingId(trimmedListingID); + } + + /** + * Parses {@code String LocalDateTime} into a {@Code LocalDateTime}. + */ + public static LocalDateTime parseDateTime(String dateTime) throws ParseException { + requireNonNull(dateTime); + String trimmedDateTime = dateTime.trim(); + DateTimeFormatter format = DateTimeFormatter.ofPattern(DATETIME_FORMAT); + try { + LocalDateTime formattedDate = LocalDateTime.parse(trimmedDateTime, format); + return formattedDate; + } catch (DateTimeParseException e) { + throw new ParseException(INVALID_DATETIME_FORMAT); + } + } } diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/seedu/realtime/logic/parser/Prefix.java similarity index 95% rename from src/main/java/seedu/address/logic/parser/Prefix.java rename to src/main/java/seedu/realtime/logic/parser/Prefix.java index c859d5fa5db..fcdbf158c87 100644 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ b/src/main/java/seedu/realtime/logic/parser/Prefix.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.realtime.logic.parser; /** * A prefix that marks the beginning of an argument in an arguments string. diff --git a/src/main/java/seedu/realtime/logic/parser/RealTimeParser.java b/src/main/java/seedu/realtime/logic/parser/RealTimeParser.java new file mode 100644 index 00000000000..be3a4705294 --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/RealTimeParser.java @@ -0,0 +1,133 @@ +package seedu.realtime.logic.parser; + +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.realtime.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.realtime.logic.commands.AddClientCommand; +import seedu.realtime.logic.commands.AddInterestedClientCommand; +import seedu.realtime.logic.commands.AddListingCommand; +import seedu.realtime.logic.commands.AddMeetingCommand; +import seedu.realtime.logic.commands.AddOfferCommand; +import seedu.realtime.logic.commands.AddTagsToListingCommand; +import seedu.realtime.logic.commands.ClearCommand; +import seedu.realtime.logic.commands.Command; +import seedu.realtime.logic.commands.DeleteClientCommand; +import seedu.realtime.logic.commands.DeleteListingCommand; +import seedu.realtime.logic.commands.DeleteMeetingCommand; +import seedu.realtime.logic.commands.DeleteOfferCommand; +import seedu.realtime.logic.commands.EditClientCommand; +import seedu.realtime.logic.commands.EditListingCommand; +import seedu.realtime.logic.commands.EditMeetingCommand; +import seedu.realtime.logic.commands.EditOfferCommand; +import seedu.realtime.logic.commands.ExitCommand; +import seedu.realtime.logic.commands.FindClientCommand; +import seedu.realtime.logic.commands.FindOffersForListingCommand; +import seedu.realtime.logic.commands.HelpCommand; +import seedu.realtime.logic.commands.ViewClientListCommand; +import seedu.realtime.logic.commands.ViewListingClientsCommand; +import seedu.realtime.logic.commands.ViewListingsCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; + + +/** + * Parses user input. + */ +public class RealTimeParser { + + /** + * Used for initial separation of command word and args. + */ + private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + + /** + * Parses user input into command for execution. + * + * @param userInput full user input string + * @return the command based on the user input + * @throws ParseException if the user input does not conform the expected format + */ + public Command parseCommand(String userInput) throws ParseException { + final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); + if (!matcher.matches()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + + final String commandWord = matcher.group("commandWord"); + final String arguments = matcher.group("arguments"); + switch (commandWord) { + + case AddClientCommand.COMMAND_WORD: + return new AddClientCommandParser().parse(arguments); + + case AddInterestedClientCommand.COMMAND_WORD: + return new AddInterestedClientCommandParser().parse(arguments); + + case AddListingCommand.COMMAND_WORD: + return new AddListingCommandParser().parse(arguments); + + case AddTagsToListingCommand.COMMAND_WORD: + return new AddTagsToListingCommandParser().parse(arguments); + + case AddOfferCommand.COMMAND_WORD: + return new AddOfferCommandParser().parse(arguments); + + case AddMeetingCommand.COMMAND_WORD: + return new AddMeetingCommandParser().parse(arguments); + + case EditClientCommand.COMMAND_WORD: + return new EditClientCommandParser().parse(arguments); + + case EditListingCommand.COMMAND_WORD: + return new EditListingCommandParser().parse(arguments); + + case EditOfferCommand.COMMAND_WORD: + return new EditOfferCommandParser().parse(arguments); + + case EditMeetingCommand.COMMAND_WORD: + return new EditMeetingCommandParser().parse(arguments); + + case DeleteClientCommand.COMMAND_WORD: + return new DeleteClientCommandParser().parse(arguments); + + case DeleteListingCommand.COMMAND_WORD: + return new DeleteListingCommandParser().parse(arguments); + + case DeleteOfferCommand.COMMAND_WORD: + return new DeleteOfferCommandParser().parse(arguments); + + case DeleteMeetingCommand.COMMAND_WORD: + return new DeleteMeetingCommandParser().parse(arguments); + + case ClearCommand.COMMAND_WORD: + return new ClearCommand(); + + case FindClientCommand.COMMAND_WORD: + return new FindClientCommandParser().parse(arguments); + + case FindOffersForListingCommand.COMMAND_WORD: + return new FindOffersForListingCommandParser().parse(arguments); + + case ViewClientListCommand.COMMAND_WORD: + return new ViewClientListCommand(); + + case ViewListingsCommand.COMMAND_WORD: + return new ViewListingsCommand(); + + case ViewListingClientsCommand.COMMAND_WORD: + return new ViewListingClientsCommandParser().parse(arguments); + + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } + +} diff --git a/src/main/java/seedu/realtime/logic/parser/ViewListingClientsCommandParser.java b/src/main/java/seedu/realtime/logic/parser/ViewListingClientsCommandParser.java new file mode 100644 index 00000000000..100b8f92a2e --- /dev/null +++ b/src/main/java/seedu/realtime/logic/parser/ViewListingClientsCommandParser.java @@ -0,0 +1,47 @@ +package seedu.realtime.logic.parser; + +import static seedu.realtime.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.realtime.logic.parser.CliSyntax.PREFIX_LISTING_ID; + +import java.util.stream.Stream; + +import seedu.realtime.logic.commands.ViewListingClientsCommand; +import seedu.realtime.logic.parser.exceptions.ParseException; +import seedu.realtime.model.listing.ListingId; + +/** + * Parses input arguments and creates a new AddCommand object + */ +public class ViewListingClientsCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ViewListingClientsCommand + * and returns an ViewListingClientsCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ViewListingClientsCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_LISTING_ID); + + if (!arePrefixesPresent(argMultimap, PREFIX_LISTING_ID) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ViewListingClientsCommand.MESSAGE_USAGE)); + } + + ListingId id = ParserUtil.parseListingId(argMultimap.getValue(PREFIX_LISTING_ID).get()); + + return new ViewListingClientsCommand(id); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} + + diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/seedu/realtime/logic/parser/exceptions/ParseException.java similarity index 72% rename from src/main/java/seedu/address/logic/parser/exceptions/ParseException.java rename to src/main/java/seedu/realtime/logic/parser/exceptions/ParseException.java index 158a1a54c1c..cbd7afc4c94 100644 --- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java +++ b/src/main/java/seedu/realtime/logic/parser/exceptions/ParseException.java @@ -1,6 +1,6 @@ -package seedu.address.logic.parser.exceptions; +package seedu.realtime.logic.parser.exceptions; -import seedu.address.commons.exceptions.IllegalValueException; +import seedu.realtime.commons.exceptions.IllegalValueException; /** * Represents a parse error encountered by a parser. diff --git a/src/main/java/seedu/realtime/model/Model.java b/src/main/java/seedu/realtime/model/Model.java new file mode 100644 index 00000000000..666e1eac7b5 --- /dev/null +++ b/src/main/java/seedu/realtime/model/Model.java @@ -0,0 +1,302 @@ +package seedu.realtime.model; + +import java.nio.file.Path; +import java.util.function.Predicate; + +import javafx.collections.ObservableList; +import seedu.realtime.commons.core.GuiSettings; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.meeting.Meeting; +import seedu.realtime.model.offer.Offer; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Client; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.person.Person; + +/** + * The API of the Model component. + */ +public interface Model { + /** {@code Predicate} that always evaluate to true */ + Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; + + /** {@code Predicate} that always evaluate to true */ + Predicate PREDICATE_SHOW_ALL_CLIENTS = unused -> true; + + /** {@code Predicate} that always evaluate to true */ + Predicate PREDICATE_SHOW_ALL_LISTINGS = unused -> true; + + /** {@code Predicate} that always evaluate to true */ + Predicate PREDICATE_SHOW_ALL_OFFERS = unused -> true; + + /** {@code Predicate} that always evaluate to true */ + Predicate PREDICATE_SHOW_ALL_MEETINGS = unused -> true; + + /** + * Replaces user prefs data with the data in {@code userPrefs}. + */ + void setUserPrefs(ReadOnlyUserPrefs userPrefs); + + /** + * Returns the user prefs. + */ + ReadOnlyUserPrefs getUserPrefs(); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Sets the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); + + /** + * Returns the user prefs' address book file path. + */ + Path getRealTimeFilePath(); + + /** + * Sets the user prefs' address book file path. + */ + void setRealTimeFilePath(Path realTimeFilePath); + + /** + * Replaces address book data with the data in {@code realTime}. + */ + void setRealTime(ReadOnlyRealTime realTime); + + /** Returns the RealTime */ + ReadOnlyRealTime getRealTime(); + + /** + * Returns true if a person with the same identity as {@code person} exists in the address book. + */ + boolean hasPerson(Person person); + + /** + * Deletes the given person. + * The person must exist in the address book. + */ + void deletePerson(Person target); + + /** + * Adds the given person. + * {@code person} must not already exist in the address book. + */ + void addPerson(Person person); + + /** + * Gets the person with the given name {@code name}. + * @param name name of the person + * @return person with given name + */ + Person getPerson(Name name); + + /** + * Replaces the given person {@code target} with {@code editedPerson}. + * {@code target} must exist in the address book. + * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. + */ + void setPerson(Person target, Person editedPerson); + + /** Returns an unmodifiable view of the filtered person list */ + ObservableList getFilteredPersonList(); + + /** + * Updates the filter of the filtered person list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredPersonList(Predicate predicate); + + /** + * Returns true if a listing with the same identity as {@code listing} exists in the address book. + */ + boolean hasListing(Listing listing); + + /** + * Deletes the given listing. + * The listing must exist in the address book. + */ + void deleteListing(Listing target); + + /** + * Adds the given listing. + * {@code listing} must not already exist in the address book. + */ + void addListing(Listing listing); + + /** + * Gets the listing with the given id {@code id}. + * @param id id of the listing + * @return listing with given id + */ + Listing getListing(ListingId id); + + /** + * Replaces the given listing {@code target} with {@code editedListing}. + * {@code target} must exist in the address book. + * The listing identity of {@code editedListing} must not be the same + * as another existing listing in the address book. + */ + void setListing(Listing listing, Listing editedListing); + + /** + * Deletes all offers for the specified listing. + */ + void deleteOffersFor(Listing target); + + /** + * Delete all meetings about the specified listing. + */ + void deleteMeetingsAbout(Listing target); + + /** Returns an unmodifiable view of the filtered listings list */ + ObservableList getFilteredListingList(); + + /** + * Updates the filter of the filtered listings list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredListingList(Predicate predicate); + + /** + * Returns true if an offer with the same identity as {@code offer} exists in the address book. + */ + boolean hasOffer(Offer offer); + + /** + * Deletes the given offer. + * The offer must exist in the address book. + */ + void deleteOffer(Offer target); + + /** + * Adds the given offer. + * {@code offer} must not already exist in the address book. + */ + void addOffer(Offer offer); + + /** + * Gets the offer from the given name {@code name} and listing address {@code address}. + * @param name name of the client in offer + * @param address listing address of offer + * @return offer with given name and listing address + */ + public Offer getOffer(Name name, Address address); + + /** + * Replaces the given offer {@code target} with {@code editedOffer}. + * {@code target} must exist in the address book. + * The offer identity of {@code editedOffer} must not be the same as another existing offer in the address book. + */ + void setOffer(Offer target, Offer editedOffer); + + /** Returns an unmodifiable view of the filtered offer list */ + ObservableList getFilteredOfferList(); + + /** + * Updates the filter of the filtered offer list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredOfferList(Predicate predicate); + + /** + * Returns true if a meeting with the same identity as {@code meeting} exists in the address book. + */ + boolean hasMeeting(Meeting meeting); + + /** + * Adds the given meeting. + * {@code meeting} must not already exist in the address book. + */ + void addMeeting(Meeting meeting); + + /** + * Deletes given meeting. + * {@code meeting} must not exist in the given address book. + */ + void deleteMeeting(Meeting meeting); + + /** + * Gets the meeting from the given name {@code name} and listing address {@code address}. + * @param name name of the client in meeting + * @param address listing address of meeting + * @return meeting with given name and listing address + */ + public Meeting getMeeting(Name name, Address address); + + /** + * Replaces the given meeting {@code target} with {@code editedMeeting}. + * {@code target} must exist in the address book. + * The meeting identity of {@code editedMeeting} must not be the same as another existing meeting in the address + * book. + */ + void setMeeting(Meeting target, Meeting editedMeeting); + + /** Returns an unmodifiable view of the filtered meeting list */ + ObservableList getFilteredMeetingList(); + + /** + * Updates the filter of the filtered meeting list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredMeetingList(Predicate predicate); + + /** + * Returns true if a client with the same identity as {@code client} exists in the address book. + */ + boolean hasClient(Client client); + + /** + * Deletes the given client. + * The client must exist in the address book. + */ + void deleteClient(Client target); + + /** + * Deletes all the listings owned by the given client. + */ + void deleteListingsOwnedBy(Client target); + + /** + * Delete all the offers made by the given client. + */ + void deleteOffersMadeBy(Client target); + + /** + * Delete all the meetings with the given client. + */ + void deleteMeetingsWith(Client target); + /** + * Adds the given client. + * {@code client} must not already exist in the address book. + */ + void addClient(Client client); + + /** + * Gets the client with the given name {@code name}. + * @param name name of the client + * @return client with given name + */ + Client getClient(Name name); + + /** + * Replaces the given client {@code target} with {@code editedClient}. + * {@code target} must exist in the address book. + * The client identity of {@code editedClient} must not be the same as another existing client in the address book. + */ + void setClient(Client target, Client editedClient); + + /** Returns an unmodifiable view of the filtered client list */ + ObservableList getFilteredClientList(); + + /** + * Updates the filter of the filtered client list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredClientList(Predicate predicate); + +} diff --git a/src/main/java/seedu/realtime/model/ModelManager.java b/src/main/java/seedu/realtime/model/ModelManager.java new file mode 100644 index 00000000000..2c43302e1c7 --- /dev/null +++ b/src/main/java/seedu/realtime/model/ModelManager.java @@ -0,0 +1,363 @@ +package seedu.realtime.model; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.commons.util.CollectionUtil.requireAllNonNull; + +import java.nio.file.Path; +import java.util.function.Predicate; +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import seedu.realtime.commons.core.GuiSettings; +import seedu.realtime.commons.core.LogsCenter; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.meeting.Meeting; +import seedu.realtime.model.offer.Offer; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Client; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.person.Person; + +/** + * Represents the in-memory model of the address book data. + */ +public class ModelManager implements Model { + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + + private final RealTime realTime; + private final UserPrefs userPrefs; + private final FilteredList filteredClients; + private final FilteredList filteredPersons; + private final FilteredList filteredListings; + private final FilteredList filteredOffers; + private final FilteredList filteredMeetings; + + /** + * Initializes a ModelManager with the given realTime and userPrefs. + */ + public ModelManager(ReadOnlyRealTime realTime, ReadOnlyUserPrefs userPrefs) { + requireAllNonNull(realTime, userPrefs); + + logger.fine("Initializing with address book: " + realTime + " and user prefs " + userPrefs); + + this.realTime = new RealTime(realTime); + this.userPrefs = new UserPrefs(userPrefs); + filteredClients = new FilteredList<>(this.realTime.getClientList()); + filteredPersons = new FilteredList<>(this.realTime.getPersonList()); + filteredListings = new FilteredList<>(this.realTime.getListingList()); + filteredOffers = new FilteredList<>(this.realTime.getOfferList()); + filteredMeetings = new FilteredList<>(this.realTime.getMeetingList()); + } + + public ModelManager() { + this(new RealTime(), new UserPrefs()); + } + + //=========== UserPrefs ================================================================================== + + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + requireNonNull(userPrefs); + this.userPrefs.resetData(userPrefs); + } + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + return userPrefs; + } + + @Override + public GuiSettings getGuiSettings() { + return userPrefs.getGuiSettings(); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + requireNonNull(guiSettings); + userPrefs.setGuiSettings(guiSettings); + } + + @Override + public Path getRealTimeFilePath() { + return userPrefs.getRealTimeFilePath(); + } + + @Override + public void setRealTimeFilePath(Path realTimeFilePath) { + requireNonNull(realTimeFilePath); + userPrefs.setRealTimeFilePath(realTimeFilePath); + } + + //=========== RealTime ================================================================================ + + @Override + public void setRealTime(ReadOnlyRealTime realTime) { + this.realTime.resetData(realTime); + } + + @Override + public ReadOnlyRealTime getRealTime() { + return realTime; + } + + @Override + public boolean hasPerson(Person person) { + requireNonNull(person); + return realTime.hasPerson(person); + } + + @Override + public void deletePerson(Person target) { + realTime.removePerson(target); + } + + @Override + public void addPerson(Person person) { + realTime.addPerson(person); + updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + } + + @Override + public Person getPerson(Name name) { + return realTime.getPerson(name); + } + + @Override + public void setPerson(Person target, Person editedPerson) { + requireAllNonNull(target, editedPerson); + + realTime.setPerson(target, editedPerson); + } + + + @Override + public boolean hasClient(Client client) { + requireNonNull(client); + return realTime.hasClient(client); + } + + @Override + public void deleteClient(Client target) { + realTime.removeClient(target); + } + + @Override + public void deleteListingsOwnedBy(Client target) { + realTime.removeAllListingOwnedBy(target); + } + + @Override + public void deleteOffersMadeBy(Client target) { + realTime.removeAllOffersMadeBy(target); + } + + @Override + public void deleteMeetingsWith(Client target) { + realTime.removeAllMeetingsWith(target); + } + + @Override + public void addClient(Client client) { + realTime.addClient(client); + updateFilteredClientList(PREDICATE_SHOW_ALL_CLIENTS); + } + + @Override + public Client getClient(Name name) { + return realTime.getClient(name); + } + + @Override + public void setClient(Client target, Client editedClient) { + requireAllNonNull(target, editedClient); + + realTime.setClient(target, editedClient); + } + + + @Override + public boolean hasListing(Listing listing) { + requireNonNull(listing); + return realTime.hasListing(listing); + } + + @Override + public void deleteListing(Listing target) { + realTime.removeListing(target); + } + + @Override + public void addListing(Listing listing) { + realTime.addListing(listing); + updateFilteredListingList(PREDICATE_SHOW_ALL_LISTINGS); + } + + /** + * Gets the listing with the given id {@code id}. + * @param id id of the listing + * @return listing with given id + */ + public Listing getListing(ListingId id) { + return realTime.getListing(id); + } + + @Override + public void setListing(Listing target, Listing editedListing) { + requireAllNonNull(target, editedListing); + realTime.setListing(target, editedListing); + } + + @Override + public void deleteOffersFor(Listing target) { + realTime.removeAllOffersFor(target); + } + + @Override + public void deleteMeetingsAbout(Listing target) { + realTime.removeAllMeetingsAbout(target); + } + + @Override + public boolean hasOffer(Offer offer) { + requireNonNull(offer); + return realTime.hasOffer(offer); + } + + @Override + public void deleteOffer(Offer target) { + realTime.removeOffer(target); + } + + @Override + public void addOffer(Offer offer) { + realTime.addOffer(offer); + updateFilteredOfferList(PREDICATE_SHOW_ALL_OFFERS); + } + + @Override + public Offer getOffer(Name name, Address address) { + return realTime.getOffer(name, address); + } + + @Override + public void setOffer(Offer target, Offer editedOffer) { + requireAllNonNull(target, editedOffer); + + realTime.setOffer(target, editedOffer); + } + + @Override + public boolean hasMeeting(Meeting meeting) { + requireNonNull(meeting); + return realTime.hasMeeting(meeting); + } + + @Override + public void deleteMeeting(Meeting meeting) { + realTime.removeMeeting(meeting); + } + + @Override + public void addMeeting(Meeting meeting) { + realTime.addMeeting(meeting); + updateFilteredMeetingList(PREDICATE_SHOW_ALL_MEETINGS); + } + + @Override + public Meeting getMeeting(Name name, Address address) { + return realTime.getMeeting(name, address); + } + + @Override + public void setMeeting(Meeting target, Meeting editedMeeting) { + requireAllNonNull(target, editedMeeting); + + realTime.setMeeting(target, editedMeeting); + } + + //=========== Filtered List Accessors ============================================================= + + /** + * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of + * {@code versionedRealTime} + */ + @Override + public ObservableList getFilteredPersonList() { + return filteredPersons; + } + + @Override + public void updateFilteredPersonList(Predicate predicate) { + requireNonNull(predicate); + filteredPersons.setPredicate(predicate); + } + + + @Override + public ObservableList getFilteredClientList() { + return filteredClients; + } + + @Override + public void updateFilteredClientList(Predicate predicate) { + requireNonNull(predicate); + filteredClients.setPredicate(predicate); + } + + @Override + public ObservableList getFilteredListingList() { + return filteredListings; + } + + @Override + public void updateFilteredListingList(Predicate predicate) { + requireNonNull(predicate); + filteredListings.setPredicate(predicate); + } + + @Override + public ObservableList getFilteredOfferList() { + return filteredOffers; + } + + @Override + public void updateFilteredOfferList(Predicate predicate) { + requireNonNull(predicate); + filteredOffers.setPredicate(predicate); + } + + @Override + public ObservableList getFilteredMeetingList() { + return filteredMeetings; + } + + @Override + public void updateFilteredMeetingList(Predicate predicate) { + requireNonNull(predicate); + filteredMeetings.setPredicate(predicate); + } + + @Override + public boolean equals(Object obj) { + // short circuit if same object + if (obj == this) { + return true; + } + + // instanceof handles nulls + if (!(obj instanceof ModelManager)) { + return false; + } + + // state check + ModelManager other = (ModelManager) obj; + return realTime.equals(other.realTime) + && userPrefs.equals(other.userPrefs) + && filteredClients.equals(other.filteredClients) + && filteredListings.equals(other.filteredListings) + && filteredOffers.equals(other.filteredOffers) + && filteredMeetings.equals(other.filteredMeetings); + } +} diff --git a/src/main/java/seedu/realtime/model/ReadOnlyRealTime.java b/src/main/java/seedu/realtime/model/ReadOnlyRealTime.java new file mode 100644 index 00000000000..5f596406fbf --- /dev/null +++ b/src/main/java/seedu/realtime/model/ReadOnlyRealTime.java @@ -0,0 +1,44 @@ +package seedu.realtime.model; + +import javafx.collections.ObservableList; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.meeting.Meeting; +import seedu.realtime.model.offer.Offer; +import seedu.realtime.model.person.Client; +import seedu.realtime.model.person.Person; + +/** + * Unmodifiable view of an address book + */ +public interface ReadOnlyRealTime { + + /** + * Returns an unmodifiable view of the persons list. + * This list will not contain any duplicate persons. + */ + ObservableList getPersonList(); + + /** + * Returns an unmodifiable view of the clients list. + * This list will not contain any duplicate persons. + */ + ObservableList getClientList(); + + /** + * Returns an unmodifiable view of the listings list. + * This list will not contain any duplicate listings. + */ + ObservableList getListingList(); + + /** + * Returns an unmodifiable view of the offers list. + * This list will not contain any duplicate offers. + */ + ObservableList getOfferList(); + + /** + * Returns an unmodifaibale view of the meetings list. + * This list will not contain any duplicate meetings. + */ + ObservableList getMeetingList(); +} diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/realtime/model/ReadOnlyUserPrefs.java similarity index 57% rename from src/main/java/seedu/address/model/ReadOnlyUserPrefs.java rename to src/main/java/seedu/realtime/model/ReadOnlyUserPrefs.java index befd58a4c73..e9e81538ff0 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/seedu/realtime/model/ReadOnlyUserPrefs.java @@ -1,8 +1,8 @@ -package seedu.address.model; +package seedu.realtime.model; import java.nio.file.Path; -import seedu.address.commons.core.GuiSettings; +import seedu.realtime.commons.core.GuiSettings; /** * Unmodifiable view of user prefs. @@ -11,6 +11,6 @@ public interface ReadOnlyUserPrefs { GuiSettings getGuiSettings(); - Path getAddressBookFilePath(); + Path getRealTimeFilePath(); } diff --git a/src/main/java/seedu/realtime/model/RealTime.java b/src/main/java/seedu/realtime/model/RealTime.java new file mode 100644 index 00000000000..991f0c1d529 --- /dev/null +++ b/src/main/java/seedu/realtime/model/RealTime.java @@ -0,0 +1,409 @@ +package seedu.realtime.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.listing.UniqueListingList; +import seedu.realtime.model.meeting.Meeting; +import seedu.realtime.model.meeting.UniqueMeetingList; +import seedu.realtime.model.offer.Offer; +import seedu.realtime.model.offer.UniqueOfferList; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Client; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.person.Person; +import seedu.realtime.model.person.UniqueClientList; +import seedu.realtime.model.person.UniquePersonList; + +/** + * Wraps all data at the real-time level + * Duplicates are not allowed (by .isSamePerson comparison) + */ +public class RealTime implements ReadOnlyRealTime { + + private final UniqueClientList clients; + private final UniquePersonList persons; + private final UniqueListingList listings; + private final UniqueOfferList offers; + private final UniqueMeetingList meetings; + + /* + * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication + * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html + * + * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * among constructors. + */ + { + persons = new UniquePersonList(); + clients = new UniqueClientList(); + listings = new UniqueListingList(); + offers = new UniqueOfferList(); + meetings = new UniqueMeetingList(); + } + + public RealTime() {} + + /** + * Creates an RealTime using the Persons in the {@code toBeCopied} + */ + public RealTime(ReadOnlyRealTime toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// list overwrite operations + + /** + * Replaces the contents of the person list with {@code persons}. + * {@code persons} must not contain duplicate persons. + */ + public void setPersons(List persons) { + this.persons.setPersons(persons); + } + + + /** + * Replaces the contents of the client list with {@code clients}. + * {@code clients} must not contain duplicate clients. + */ + public void setClients(List clients) { + this.clients.setClients(clients); + } + + /** + * Replaces the contents of the listing list with {@code listings}. + * {@code listings} must not contain duplicate listings. + */ + public void setListings(List listings) { + this.listings.setListings(listings); + } + + /** + * Replaces the contents of the offer list with {@code offers}. + * {@code clients} must not contain duplicate offers. + */ + public void setOffers(List offers) { + this.offers.setOffers(offers); + } + + /** + * Resets the existing data of this {@code RealTime} with {@code newData}. + */ + public void resetData(ReadOnlyRealTime newData) { + requireNonNull(newData); + setOffers(newData.getOfferList()); + setClients(newData.getClientList()); + setPersons(newData.getPersonList()); + setListings(newData.getListingList()); + } + + //// person-level operations + + /** + * Returns true if a person with the same identity as {@code person} exists in realtime. + */ + public boolean hasPerson(Person person) { + requireNonNull(person); + return persons.contains(person); + } + + /** + * Adds a person to realtime. + * The person must not already exist in realtime. + */ + public void addPerson(Person p) { + persons.add(p); + } + + /** + * Gets the person with the given name {@code name}. + * @param name name of the person + * @return person with given name + */ + public Person getPerson(Name name) { + return persons.getPerson(name); + } + + /** + * Replaces the given person {@code target} in the list with {@code editedPerson}. + * {@code target} must exist in realtime. + * The person identity of {@code editedPerson} must not be the same as another existing person in realtime. + */ + public void setPerson(Person target, Person editedPerson) { + requireNonNull(editedPerson); + + persons.setPerson(target, editedPerson); + } + + /** + * Removes {@code key} from this {@code RealTime}. + * {@code key} must exist in realtime. + */ + public void removePerson(Person key) { + persons.remove(key); + } + + /** + * Removes all listings owned by {@code key} from this {@code RealTime} + */ + public void removeAllListingOwnedBy(Client key) { + listings.deleteListingsOfClient(key); + } + + public void removeAllOffersMadeBy(Client key) { + offers.deleteOffersOfClient(key); + } + + public void removeAllMeetingsWith(Client key) { + meetings.deleteMeetingsWithClient(key); + } + + + //// client-level operations + + /** + * Returns true if a client with the same identity as {@code client} exists in realtime. + */ + public boolean hasClient(Client client) { + requireNonNull(client); + return clients.contains(client); + } + + /** + * Adds a client to realtime. + * The client must not already exist in realtime. + */ + public void addClient(Client p) { + clients.add(p); + } + + /** + * Gets the vn with the given name {@code name}. + * @param name name of the client + * @return client with given name + */ + public Client getClient(Name name) { + return clients.getClient(name); + } + + /** + * Replaces the given client {@code target} in the list with {@code editedClient}. + * {@code target} must exist in realtime. + * The client identity of {@code editedClient} must not be the same as another existing client in realtime. + */ + public void setClient(Client target, Client editedClient) { + requireNonNull(editedClient); + + clients.setClient(target, editedClient); + } + + /** + * Removes {@code key} from this {@code RealTime}. + * {@code key} must exist in realtime. + */ + public void removeClient(Client key) { + clients.remove(key); + } + + //// listing-level operations + + /** + * Returns true if a listing with the same identity as {@code listing} exists in realtime. + */ + public boolean hasListing(Listing listing) { + requireNonNull(listing); + return listings.contains(listing); + } + + /** + * Adds a lsiting to realtime. + * The listing must not already exist in realtime. + */ + public void addListing(Listing l) { + listings.add(l); + } + + /** + * Gets the listing with the given id {@code id}. + * @param id id of the listing + * @return listing with given id + */ + public Listing getListing(ListingId id) { + return listings.getListing(id); + } + + /** + * Replaces the given listing {@code target} in the list with {@code editedListing}. + * {@code target} must exist in realtime. + * The listing identity of {@code editedListing} must not be the same + * as another existing listing in realtime. + */ + public void setListing(Listing target, Listing editedListing) { + requireNonNull(editedListing); + listings.setListing(target, editedListing); + } + + /** + * Removes {@code listing} from this {@code RealTime}. + * {@code listing} must exist in realtime. + */ + public void removeListing(Listing listing) { + listings.remove(listing); + } + + public void removeAllOffersFor(Listing target) { + offers.deleteOffersForListing(target); + } + + public void removeAllMeetingsAbout(Listing target) { + meetings.deleteMeetingsAboutListing(target); + } + + //// offer-level operations + + /** + * Returns true if an offer with the same identity as {@code offer} exists in realtime. + */ + public boolean hasOffer(Offer offer) { + requireNonNull(offer); + return offers.contains(offer); + } + + /** + * Adds an offer to realtime. + * The offer must not already exist in realtime. + */ + public void addOffer(Offer o) { + offers.add(o); + } + + /** + * Gets the offer from the given name {@code name} and listing address {@code address}. + * @param name name of the person in offer + * @param address listing address of offer + * @return offer with given name and listing address + */ + public Offer getOffer(Name name, Address address) { + return offers.getOffer(name, address); + } + + /** + * Replaces the given offer {@code target} in the list with {@code editedOffer}. + * {@code target} must exist in realtime. + * The offer identity of {@code editedOffer} must not be the same as another existing offer in realtime. + */ + public void setOffer(Offer target, Offer editedOffer) { + requireNonNull(editedOffer); + + offers.setOffer(target, editedOffer); + } + + /** + * Removes {@code key} from this {@code RealTime}. + * {@code key} must exist in realtime. + */ + public void removeOffer(Offer key) { + offers.remove(key); + } + + /** + * Returns true if a meeting with the same identity as {@code meeting} exists in realtime. + */ + public boolean hasMeeting(Meeting meeting) { + requireNonNull(meeting); + return meetings.contains(meeting); + } + + /** + * Removes {@code key} from this {@code RealTime}. + * {@code key} must exist in realtime. + */ + public void removeMeeting(Meeting meeting) { + meetings.remove(meeting); + } + + /** + * Adds a meeting to realtime. + * The meeting must not already exist in realtime. + */ + public void addMeeting(Meeting meeting) { + meetings.add(meeting); + } + + /** + * Gets the meeting from the given name {@code name} and listing address {@code address}. + * @param name name of the person in meeting + * @param address listing address of meeting + * @return meeting with given name and listing address + */ + public Meeting getMeeting(Name name, Address address) { + return meetings.getMeeting(name, address); + } + + /** + * Replaces the given meeting {@code target} in the list with {@code editedMeeting}. + * {@code target} must exist in realtime. + * The meeting identity of {@code editedMeeting} must not be the same as another existing offer in realtime. + */ + public void setMeeting(Meeting target, Meeting editedMeeting) { + requireNonNull(editedMeeting); + + meetings.setMeeting(target, editedMeeting); + } + + //// util methods + + @Override + public String toString() { + int clientListSize = clients.asUnmodifiableObservableList().size(); + int personListSize = persons.asUnmodifiableObservableList().size(); + int listingListSize = listings.asUnmodifiableObservableList().size(); + int offerListSize = offers.asUnmodifiableObservableList().size(); + return String.format("%d clients, %d listings, %d offers", clientListSize, listingListSize, offerListSize); + // TODO: refine later + } + + @Override + public ObservableList getClientList() { + return clients.asUnmodifiableObservableList(); + } + + @Override + public ObservableList getPersonList() { + return persons.asUnmodifiableObservableList(); + } + + @Override + public ObservableList getListingList() { + return listings.asUnmodifiableObservableList(); + } + + @Override + public ObservableList getOfferList() { + return offers.asUnmodifiableObservableList(); + } + + @Override + public ObservableList getMeetingList() { + return meetings.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof RealTime) // instanceof handles nulls + && (clients.equals(((RealTime) other).clients)) + && (listings.equals(((RealTime) other).listings)) + && (offers.equals(((RealTime) other).offers)); + } + + @Override + public int hashCode() { + return clients.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/realtime/model/UserPrefs.java similarity index 70% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/seedu/realtime/model/UserPrefs.java index 25a5fd6eab9..eaf20e3a40e 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/realtime/model/UserPrefs.java @@ -1,4 +1,4 @@ -package seedu.address.model; +package seedu.realtime.model; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.nio.file.Paths; import java.util.Objects; -import seedu.address.commons.core.GuiSettings; +import seedu.realtime.commons.core.GuiSettings; /** * Represents User's preferences. @@ -14,7 +14,7 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path realTimeFilePath = Paths.get("data" , "realTime.json"); /** * Creates a {@code UserPrefs} with default values. @@ -35,7 +35,7 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) { public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); - setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setRealTimeFilePath(newUserPrefs.getRealTimeFilePath()); } public GuiSettings getGuiSettings() { @@ -47,13 +47,13 @@ public void setGuiSettings(GuiSettings guiSettings) { this.guiSettings = guiSettings; } - public Path getAddressBookFilePath() { - return addressBookFilePath; + public Path getRealTimeFilePath() { + return realTimeFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - this.addressBookFilePath = addressBookFilePath; + public void setRealTimeFilePath(Path realTimeFilePath) { + requireNonNull(realTimeFilePath); + this.realTimeFilePath = realTimeFilePath; } @Override @@ -68,19 +68,19 @@ public boolean equals(Object other) { UserPrefs o = (UserPrefs) other; return guiSettings.equals(o.guiSettings) - && addressBookFilePath.equals(o.addressBookFilePath); + && realTimeFilePath.equals(o.realTimeFilePath); } @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, realTimeFilePath); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Gui Settings : " + guiSettings); - sb.append("\nLocal data file location : " + addressBookFilePath); + sb.append("\nLocal data file location : " + realTimeFilePath); return sb.toString(); } diff --git a/src/main/java/seedu/realtime/model/listing/IdContainsKeywordsPredicate.java b/src/main/java/seedu/realtime/model/listing/IdContainsKeywordsPredicate.java new file mode 100644 index 00000000000..641fd8fe4ba --- /dev/null +++ b/src/main/java/seedu/realtime/model/listing/IdContainsKeywordsPredicate.java @@ -0,0 +1,29 @@ +package seedu.realtime.model.listing; + +import java.util.function.Predicate; + +import seedu.realtime.commons.util.StringUtil; + +/** + * Tests that a {@code Listing}'s {@code Id} matches any of the keywords given. + */ +public class IdContainsKeywordsPredicate implements Predicate { + private final String keyword; + + public IdContainsKeywordsPredicate(String keyword) { + this.keyword = keyword; + } + + @Override + public boolean test(Listing listing) { + return StringUtil.containsWordIgnoreCase(listing.getId().value, keyword); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof IdContainsKeywordsPredicate // instanceof handles nulls + && keyword.equals(((IdContainsKeywordsPredicate) other).keyword)); // state check + } + +} diff --git a/src/main/java/seedu/realtime/model/listing/Listing.java b/src/main/java/seedu/realtime/model/listing/Listing.java new file mode 100644 index 00000000000..9b4e5f3376a --- /dev/null +++ b/src/main/java/seedu/realtime/model/listing/Listing.java @@ -0,0 +1,226 @@ +package seedu.realtime.model.listing; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.realtime.model.meeting.Meeting; +import seedu.realtime.model.offer.Offer; +import seedu.realtime.model.offer.Price; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.tag.Tag; + +/** + * Listing object contains a currently listed property, its owner, asking price, and offers and clients. + */ +public class Listing implements Comparable { + + // Identity fields + private final ListingId id; + private final Address address; + private final Name owner; + private final Price askingPrice; + + // Data fields + private final List interestedClients; + private final Set tags = new HashSet<>(); + + /** + * * Constructor for Listing + * + * @param address Address + * @param owner Person + * @param askingPrice int + */ + public Listing(ListingId id, Address address, Name owner, Price askingPrice) { + this.id = id; + this.address = address; + this.owner = owner; + this.askingPrice = askingPrice; + interestedClients = new ArrayList<>(); + } + + /** + * Gets the id of this listing. + * + * @return id of listing + */ + public ListingId getId() { + return this.id; + } + + /** + * Gets the name of this owner. + * + * @return name of owner + */ + public Name getName() { + return this.owner; + } + + + /** + * Gets the address of this listing. + * + * @return address of listing + */ + public Address getAddress() { + return this.address; + } + + /** + * Gets the asking price of this listing. + * + * @return asking price of listing + */ + public Price getAskingPrice() { + return this.askingPrice; + } + + /** + * Getter for a list of interested clients. + * + * @return List(Person) + */ + public List getInterestedClients() { + return interestedClients; + } + + /** + * Adds prospective client to the interestedClients list. + * + * @param client the interested client + */ + public void addInterestedClient(Name client) { + this.interestedClients.add(client); + } + + /** + * Checks if the client is already in the listing. + * + * @param client name of the client + * @return true if the client is in the listing, false otherwise + */ + public boolean hasInterestedClient(Name client) { + return this.interestedClients.contains(client); + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Set getTags() { + return Collections.unmodifiableSet(tags); + } + + /** + * Adds the tags to the listing. + * + * @param tags to be added + */ + public void addTags(Set tags) { + this.tags.addAll(tags); + } + + /** + * Checks if the tag already exists for this listing. + * + * @param toCheck the tags to be checked + * @return true if the tag exists, false otherwise + */ + public boolean hasTag(Set toCheck) { + for (Tag tag : toCheck) { + if (tags.contains(tag)) { + return true; + } + } + return false; + } + + /** + * Returns true if both Listings have the same Address. + * This defines a weaker notion of equality between two Listings. + */ + public boolean isSameListing(Listing otherListing) { + if (otherListing == this) { + return true; + } + + return otherListing != null + && (otherListing.address.equals(this.address) + || otherListing.id.equals(this.id)); + } + + public boolean doNotHaveMeeting(Meeting toCheck) { + return !this.getName().equals(toCheck.getClient()); + } + + public boolean doNotHaveOffer(Offer toCheck) { + return !this.getName().equals(toCheck.getClient()); + } + + /** + * Compares current listing to l + */ + @Override + public int compareTo(Listing l) { + return this.id.value.compareTo(l.id.value); + } + + /** + * Returns true if both persons have the same identity and data fields. + * This defines a stronger notion of equality between two persons. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Listing)) { + return false; + } + + Listing otherListing = (Listing) other; + return otherListing.getId().equals(getId()) + || otherListing.getAddress().equals(getAddress()); + } + + /** + * String representation of Listing. + * + * @return String + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("; ID: ") + .append(this.id) + .append("; Address: ") + .append(this.address) + .append("; Owner: ") + .append(this.owner) + .append("; Asking Price: ") + .append(this.askingPrice); + + Set tags = getTags(); + if (!tags.isEmpty()) { + builder.append("; Tags: "); + tags.forEach(builder::append); + } + + List interestedClients = getInterestedClients(); + + if (!interestedClients.isEmpty()) { + builder.append("; Interested Clients: "); + for (Name client : interestedClients) { + builder.append(client).append("\n"); + } + } + + return builder.toString(); + } +} diff --git a/src/main/java/seedu/realtime/model/listing/ListingId.java b/src/main/java/seedu/realtime/model/listing/ListingId.java new file mode 100644 index 00000000000..7a3c3685e4b --- /dev/null +++ b/src/main/java/seedu/realtime/model/listing/ListingId.java @@ -0,0 +1,49 @@ +package seedu.realtime.model.listing; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.commons.util.AppUtil.checkArgument; + +/** + * Represents a ListingId of a specific listing in the address book. + */ +public class ListingId { + public static final String MESSAGE_CONSTRAINTS = + "ListingId should not be blank"; + public static final String VALIDATION_REGEX = "[^\\s].*"; + public final String value; + + /** + * Constructs a {@Code ListingId} + * + * @param id A valid listingID. + */ + public ListingId(String id) { + requireNonNull(id); + checkArgument(isValidListingId(id), MESSAGE_CONSTRAINTS); + value = id; + } + + /** + * Returns true if a given string is a valid listingID. + */ + public static boolean isValidListingId(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ListingId // instanceof handles nulls + && value.equals(((ListingId) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/realtime/model/listing/UniqueListingList.java b/src/main/java/seedu/realtime/model/listing/UniqueListingList.java new file mode 100644 index 00000000000..9871875eb7d --- /dev/null +++ b/src/main/java/seedu/realtime/model/listing/UniqueListingList.java @@ -0,0 +1,165 @@ +package seedu.realtime.model.listing; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.realtime.commons.core.Messages; +import seedu.realtime.model.listing.exceptions.DuplicateListingException; +import seedu.realtime.model.listing.exceptions.ListingNotFoundException; +import seedu.realtime.model.person.Client; + +/** + * A list of persons that enforces uniqueness between its elements and does not allow nulls. + * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of + * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is + * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so + * as to ensure that the person with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Listing#isSameListing(Listing) + */ +public class UniqueListingList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent listing as the given argument. + */ + public boolean contains(Listing toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameListing); + } + + /** + * Adds a listing to the list. + * The listing must not already exist in the list. + */ + public void add(Listing toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateListingException(); + } + internalList.add(toAdd); + Collections.sort(internalList); + } + + /** + * Gets the listing with the given id {@code id}. + * @param id id of the listing + * @return listing with given id + */ + public Listing getListing(ListingId id) { + for (Listing listing : internalList) { + if (listing.getId() == (id)) { + return listing; + } + } + throw new ListingNotFoundException(Messages.MESSAGE_LISTING_DOES_NOT_EXIST); + } + + /** + * Replaces the listing {@code target} in the list with {@code editedListing}. + * {@code target} must exist in the list. + * The listing identity of {@code editedListing} must not be the same as another existing listing in the list. + */ + public void setListing(Listing target, Listing editedListing) { + requireAllNonNull(target, editedListing); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new ListingNotFoundException(Messages.MESSAGE_LISTING_DOES_NOT_EXIST); + } + + if (!target.isSameListing(editedListing) && contains(editedListing)) { + throw new DuplicateListingException(); + } + + internalList.set(index, editedListing); + } + + /** + * Removes the equivalent listing from the list. + * The person must exist in the list. + */ + public void remove(Listing toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new ListingNotFoundException(Messages.MESSAGE_LISTING_DOES_NOT_EXIST); + } + } + + public void setListings(UniqueListingList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code listings}. + * {@code listings} must not contain duplicate persons. + */ + public void setListings(List listings) { + requireAllNonNull(listings); + if (!listingsAreUnique(listings)) { + throw new DuplicateListingException(); + } + + internalList.setAll(listings); + } + + /** + * Remove all listing owned by the client. + */ + public void deleteListingsOfClient(Client toBeRemoved) { + List newInternalList = internalList.stream().filter(toBeRemoved::doNotOwn) + .collect(Collectors.toList()); + setListings(newInternalList); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueListingList // instanceof handles nulls + && internalList.equals(((UniqueListingList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code persons} contains only unique persons. + */ + private boolean listingsAreUnique(List listings) { + for (int i = 0; i < listings.size() - 1; i++) { + for (int j = i + 1; j < listings.size(); j++) { + if (listings.get(i).isSameListing(listings.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/realtime/model/listing/exceptions/DuplicateListingException.java b/src/main/java/seedu/realtime/model/listing/exceptions/DuplicateListingException.java new file mode 100644 index 00000000000..43e1927e635 --- /dev/null +++ b/src/main/java/seedu/realtime/model/listing/exceptions/DuplicateListingException.java @@ -0,0 +1,11 @@ +package seedu.realtime.model.listing.exceptions; + +/** + * Signals that the operation will result in duplicate Listings + * (Listings are considered duplicates if they have the same address). + */ +public class DuplicateListingException extends RuntimeException { + public DuplicateListingException() { + super("Listing already exist in REal-Time."); + } +} diff --git a/src/main/java/seedu/realtime/model/listing/exceptions/ListingNotFoundException.java b/src/main/java/seedu/realtime/model/listing/exceptions/ListingNotFoundException.java new file mode 100644 index 00000000000..c52721536dc --- /dev/null +++ b/src/main/java/seedu/realtime/model/listing/exceptions/ListingNotFoundException.java @@ -0,0 +1,10 @@ +package seedu.realtime.model.listing.exceptions; + +/** + * Signals that the operation is unable to find the specified listing. + */ +public class ListingNotFoundException extends RuntimeException { + public ListingNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/seedu/realtime/model/meeting/Meeting.java b/src/main/java/seedu/realtime/model/meeting/Meeting.java new file mode 100644 index 00000000000..ad62e58ffb0 --- /dev/null +++ b/src/main/java/seedu/realtime/model/meeting/Meeting.java @@ -0,0 +1,133 @@ +package seedu.realtime.model.meeting; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.tag.Tag; + +/** + * Meeting class is a meeting a client has at a listing on a specific dateTime. + */ +public class Meeting implements Comparable { + + /** + * Date and Time of meeting. + */ + private final LocalDateTime dateTime; + /** + * Client involved in the meeting. + */ + private final Name client; + /** + * Listing involved in the meeting. + */ + private final ListingId listing; + + /** + * Tag for type of Meeting. + */ + private Set tags = new HashSet<>(); + + /** + * Constructor for meeting. + * @param client Person + * @param listing Listing + * @param dateTime dateTime + */ + public Meeting(Name client, ListingId listing, LocalDateTime dateTime) { + this.client = client; + this.listing = listing; + this.dateTime = dateTime; + } + + /** + * Constructor for meeting with tags. + * @param client Person + * @param listing Listing + * @param dateTime dateTime + */ + public Meeting(Name client, ListingId listing, LocalDateTime dateTime, Set tags) { + this.client = client; + this.listing = listing; + this.dateTime = dateTime; + this.tags = tags; + } + + /** + * Getter for client. + * @return Person + */ + public Name getClient() { + return client; + } + + /** + * Getter for listing. + * @return Listing + */ + public ListingId getListing() { + return listing; + } + + /** + * Getter for date time. + * @return LocalDatetime + */ + public LocalDateTime getdateTime() { + return dateTime; + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Set getTags() { + return Collections.unmodifiableSet(tags); + } + + /** + * Returns true if both Meeting have the same identity and data fields. + * This defines a stronger notion of equality between two meetings. + */ + public boolean isSameMeeting(Meeting other) { + if (other == this) { + return true; + } + + if (!(other instanceof Meeting)) { + return false; + } + Meeting otherMeeting = (Meeting) other; + return otherMeeting.getClient().equals(getClient()) + && otherMeeting.getListing().equals(getListing()) + && otherMeeting.getdateTime().equals(getdateTime()); + } + + /** + * Compares this meeting to another meeting + */ + @Override + public int compareTo(Meeting o) { + if (this.dateTime.isAfter(o.dateTime)) { + return 1; + } else if (this.dateTime.isBefore(o.dateTime)) { + return 1; + } else { + return 0; + } + } + + /** + * String representation of meeting. + * @return String + */ + @Override + public String toString() { + return String.format("%s is meeting at %s on %s", getClient(), getListing(), dateTime); + } + +} diff --git a/src/main/java/seedu/realtime/model/meeting/UniqueMeetingList.java b/src/main/java/seedu/realtime/model/meeting/UniqueMeetingList.java new file mode 100644 index 00000000000..fe4502eaef5 --- /dev/null +++ b/src/main/java/seedu/realtime/model/meeting/UniqueMeetingList.java @@ -0,0 +1,179 @@ +package seedu.realtime.model.meeting; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.meeting.exceptions.DuplicateMeetingException; +import seedu.realtime.model.meeting.exceptions.MeetingNotFoundException; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Client; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.person.exceptions.DuplicatePersonException; + +/** + * A list of meetings that enforces uniqueness between its elements and does not allow nulls. + * A meeting is considered unique by comparing using {@code Meeting#isSameMeeting(Meeting)}. As such, adding and + * updating of Meeting uses Meeting#isSameMeeting(Meeting) for equality to ensure that the Meeting being added or + * updated is unique in terms of identity in the UniqueMeetingList. However, the removal of an Meeting uses + * Meeting#equals(Object) to ensure that the Meeting with exactly the same fields will be removed. + *

+ * Supports a minimal set of list operations. + * + * @see Meeting#isSameMeeting(Meeting) + */ +public class UniqueMeetingList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent meeting as the given argument. + */ + public boolean contains(Meeting toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameMeeting); + } + + /** + * Adds a meeting to the list. + * The meeting must not already exist in the list. + */ + public void add(Meeting toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateMeetingException(); + } + internalList.add(toAdd); + Collections.sort(internalList); + } + + /** + * Gets the meeting from the given name {@code name} and listing address {@code address}. + * @param name name of the person in meeting + * @param address listing address of meeting + * @return meeting with given name and listing address + */ + public Meeting getMeeting(Name name, Address address) { + requireNonNull(name); + for (Meeting meeting : internalList) { + if (meeting.getClient().equals(name) && meeting.getListing().equals(address)) { + return meeting; + } + } + throw new MeetingNotFoundException(); + } + + /** + * Replaces the meeting {@code target} in the list with {@code editedMeeting}. + * {@code target} must exist in the list. + * The meeting identity of {@code editedMeeting} must not be the same as another existing meeting in the list. + */ + public void setMeeting(Meeting target, Meeting editedMeeting) { + requireAllNonNull(target, editedMeeting); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new MeetingNotFoundException(); + } + + if (!target.isSameMeeting(editedMeeting) && contains(editedMeeting)) { + throw new DuplicatePersonException(); + } + + internalList.set(index, editedMeeting); + } + + /** + * Removes the equivalent meeting from the list. + * The person must exist in the list. + */ + public void remove(Meeting toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new MeetingNotFoundException(); + } + } + + public void setMeetings(UniqueMeetingList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code meetings}. + * {@code meetings} must not contain duplicate persons. + */ + public void setMeetings(List meetings) { + requireAllNonNull(meetings); + if (!meetingsAreUnique(meetings)) { + throw new DuplicateMeetingException(); + } + + internalList.setAll(meetings); + } + + /** + * Remove all meetings with the client. + */ + public void deleteMeetingsWithClient(Client toBeRemoved) { + List newInternalList = internalList.stream().filter(toBeRemoved::doNotHaveMeeting) + .collect(Collectors.toList()); + setMeetings(newInternalList); + } + + /** + * Remove all meetings about the listing. + */ + public void deleteMeetingsAboutListing(Listing toBeRemoved) { + List newInternalList = internalList.stream().filter(toBeRemoved::doNotHaveMeeting) + .collect(Collectors.toList()); + setMeetings(newInternalList); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueMeetingList // instanceof handles nulls + && internalList.equals(((UniqueMeetingList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code persons} contains only unique persons. + */ + private boolean meetingsAreUnique(List listings) { + for (int i = 0; i < listings.size() - 1; i++) { + for (int j = i + 1; j < listings.size(); j++) { + if (listings.get(i).isSameMeeting(listings.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/realtime/model/meeting/exceptions/DuplicateMeetingException.java b/src/main/java/seedu/realtime/model/meeting/exceptions/DuplicateMeetingException.java new file mode 100644 index 00000000000..0c2fda02bf0 --- /dev/null +++ b/src/main/java/seedu/realtime/model/meeting/exceptions/DuplicateMeetingException.java @@ -0,0 +1,11 @@ +package seedu.realtime.model.meeting.exceptions; + +/** + * Signals that the operation will result in duplicate Meetings + * (Meetings are considered duplicates if they have the same identity). + */ +public class DuplicateMeetingException extends RuntimeException { + public DuplicateMeetingException() { + super("Operation would result in duplicate meeting"); + } +} diff --git a/src/main/java/seedu/realtime/model/meeting/exceptions/MeetingNotFoundException.java b/src/main/java/seedu/realtime/model/meeting/exceptions/MeetingNotFoundException.java new file mode 100644 index 00000000000..bf9e791d108 --- /dev/null +++ b/src/main/java/seedu/realtime/model/meeting/exceptions/MeetingNotFoundException.java @@ -0,0 +1,7 @@ +package seedu.realtime.model.meeting.exceptions; + +/** + * Signals that the operation is unable to find the specified meeting. + */ +public class MeetingNotFoundException extends RuntimeException { +} diff --git a/src/main/java/seedu/realtime/model/offer/Offer.java b/src/main/java/seedu/realtime/model/offer/Offer.java new file mode 100644 index 00000000000..9ba9bfbc70a --- /dev/null +++ b/src/main/java/seedu/realtime/model/offer/Offer.java @@ -0,0 +1,123 @@ +package seedu.realtime.model.offer; + +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.person.Name; + +/** + * Offer Class represents a client's offer for a listing, containing an offer price. + */ +public class Offer implements Comparable { + + /** + * Name of client making the offer. + */ + private final Name client; + /** + * The listing the offer is for. + */ + private final ListingId listing; + /** + * Price client is offering for the listing. + */ + private final Price offerPrice; + + /** + * Constructor for offer object. + * + * @param client Name + * @param listing Address + * @param offerPrice Price + */ + public Offer(Name client, ListingId listing, Price offerPrice) { + this.client = client; + this.listing = listing; + this.offerPrice = offerPrice; + } + + /** + * Getter for client. + * @return Person + */ + public Name getClient() { + return client; + } + + /** + * Getter for listing. + * @return Listing + */ + public ListingId getListing() { + return listing; + } + + /** + * Getter for offer price. + * @return int + */ + public Price getOfferPrice() { + return offerPrice; + } + + /** + * Returns true if both offers have the same identity and data fields. + * This defines a stronger notion of equality between two offers. + */ + public boolean isSameOffer(Offer other) { + if (other == this) { + return true; + } + + if (!(other instanceof Offer)) { + return false; + } + Offer otherOffer = (Offer) other; + return otherOffer.getClient().equals(getClient()) + && otherOffer.getListing().equals(getListing()) + && otherOffer.getOfferPrice().equals(getOfferPrice()); + } + + /** + * Compares this offer to another offer. + */ + @Override + public int compareTo(Offer o) { + return this.listing.value.compareTo(o.listing.value); + } + + /** + * Returns true if both offers have the same identity and data fields. + * This defines a stronger notion of equality between two offers. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Offer)) { + return false; + } + + Offer otherOffer = (Offer) other; + return otherOffer.getClient().equals(getClient()) + && otherOffer.getListing().equals(getListing()) + && otherOffer.getOfferPrice().equals(getOfferPrice()); + } + + + /** + * String representation of offer. + * @return String + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("Listing address: ") + .append(getListing()) + .append("; Client: ") + .append(getClient()) + .append("; Offer Price: S$") + .append(getOfferPrice()); + return builder.toString(); + } +} diff --git a/src/main/java/seedu/realtime/model/offer/OfferContainsListingIdPredicate.java b/src/main/java/seedu/realtime/model/offer/OfferContainsListingIdPredicate.java new file mode 100644 index 00000000000..ef3bb7146d9 --- /dev/null +++ b/src/main/java/seedu/realtime/model/offer/OfferContainsListingIdPredicate.java @@ -0,0 +1,30 @@ +package seedu.realtime.model.offer; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.realtime.commons.util.StringUtil; + +/** + * Tests that a {@code Offer}'s {@code ListingId} matches the ListingId given. + */ +public class OfferContainsListingIdPredicate implements Predicate { + private final List listingId; + + public OfferContainsListingIdPredicate(List listingId) { + this.listingId = listingId; + } + + @Override + public boolean test(Offer offer) { + return listingId.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(offer.getListing().toString(), keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof OfferContainsListingIdPredicate // instanceof handles nulls + && listingId.equals(((OfferContainsListingIdPredicate) other).listingId)); // state check + } +} diff --git a/src/main/java/seedu/realtime/model/offer/Price.java b/src/main/java/seedu/realtime/model/offer/Price.java new file mode 100644 index 00000000000..250f9272383 --- /dev/null +++ b/src/main/java/seedu/realtime/model/offer/Price.java @@ -0,0 +1,49 @@ +package seedu.realtime.model.offer; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.commons.util.AppUtil.checkArgument; + +/** + * Represents a Price that a client offers or an owner asks for in the address book. + */ +public class Price { + public static final String MESSAGE_CONSTRAINTS = + "Prices should only contain numbers, and it should be at least 1 digit long"; + public static final String VALIDATION_REGEX = "\\d{1,}"; + public final String value; + + /** + * Constructs a {@Code Price} + * + * @param price A valid price. + */ + public Price(String price) { + requireNonNull(price); + checkArgument(isValidPrice(price), MESSAGE_CONSTRAINTS); + value = price; + } + + /** + * Returns true if a given string is a valid price. + */ + public static boolean isValidPrice(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Price // instanceof handles nulls + && value.equals(((Price) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/realtime/model/offer/UniqueOfferList.java b/src/main/java/seedu/realtime/model/offer/UniqueOfferList.java new file mode 100644 index 00000000000..f8c6525321a --- /dev/null +++ b/src/main/java/seedu/realtime/model/offer/UniqueOfferList.java @@ -0,0 +1,179 @@ +package seedu.realtime.model.offer; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.offer.exceptions.DuplicateOfferException; +import seedu.realtime.model.offer.exceptions.OfferNotFoundException; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Client; +import seedu.realtime.model.person.Name; + +/** + * A list of offers that enforces uniqueness between its elements and does not allow nulls. + * An offer is considered unique by comparing using {@code Offer#isSameOffer(Offer)}. As such, adding and + * updating of Offers uses Offer#isSameOffer(Offer) for equality to ensure that the Offer being added or + * updated is unique in terms of identity in the UniqueOfferList. However, the removal of an Offer uses + * Offer#equals(Object) to ensure that the Offer with exactly the same fields will be removed. + *

+ * Supports a minimal set of list operations. + * + * @see Offer#isSameOffer(Offer) + */ +public class UniqueOfferList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent Offer as the given argument. + */ + public boolean contains(Offer toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameOffer); + } + + /** + * Adds an Offer to the list. + * The Offer must not already exist in the list. + */ + public void add(Offer toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateOfferException(); + } + internalList.add(toAdd); + Collections.sort(internalList); + } + + /** + * Replaces the Offer {@code target} in the list with {@code editedOffer}. + * {@code target} must exist in the list. + * The Offer identity of {@code editedOffer} must not be the same as another existing Offer in the list. + */ + public void setOffer(Offer target, Offer editedOffer) { + requireAllNonNull(target, editedOffer); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new OfferNotFoundException(); + } + + if (!target.isSameOffer(editedOffer) && contains(editedOffer)) { + throw new DuplicateOfferException(); + } + + internalList.set(index, editedOffer); + } + + /** + * Gets the offer from the given name {@code name} and listing address {@code address}. + * @param name name of the person in offer + * @param address listing address of offer + * @return offer with given name and listing address + */ + public Offer getOffer(Name name, Address address) { + requireNonNull(name); + for (Offer offer : internalList) { + if (offer.getClient().equals(name) && offer.getListing().equals(address)) { + return offer; + } + } + throw new OfferNotFoundException(); + } + + /** + * Removes the equivalent Offer from the list. + * The Offer must exist in the list. + */ + public void remove(Offer toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new OfferNotFoundException(); + } + } + + public void setOffers(UniqueOfferList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code Offers}. + * {@code Offers} must not contain duplicate Offers. + */ + public void setOffers(List offers) { + requireAllNonNull(offers); + if (!offersAreUnique(offers)) { + throw new DuplicateOfferException(); + } + + internalList.setAll(offers); + } + + /** + * Remove all offers made by the client. + */ + public void deleteOffersOfClient(Client toBeRemoved) { + List newInternalList = internalList.stream().filter(toBeRemoved::doNotHaveOffer) + .collect(Collectors.toList()); + setOffers(newInternalList); + } + + /** + * Remove all offers related to listing. + */ + public void deleteOffersForListing(Listing toBeRemoved) { + List newInternalList = internalList.stream().filter(toBeRemoved::doNotHaveOffer) + .collect(Collectors.toList()); + setOffers(newInternalList); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueOfferList // instanceof handles nulls + && internalList.equals(((UniqueOfferList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code offers} contains only unique Offers. + */ + private boolean offersAreUnique(List offers) { + for (int i = 0; i < offers.size() - 1; i++) { + for (int j = i + 1; j < offers.size(); j++) { + if (offers.get(i).isSameOffer(offers.get(j))) { + return false; + } + } + } + return true; + } +} + diff --git a/src/main/java/seedu/realtime/model/offer/exceptions/DuplicateOfferException.java b/src/main/java/seedu/realtime/model/offer/exceptions/DuplicateOfferException.java new file mode 100644 index 00000000000..6ec6407a0ed --- /dev/null +++ b/src/main/java/seedu/realtime/model/offer/exceptions/DuplicateOfferException.java @@ -0,0 +1,12 @@ +package seedu.realtime.model.offer.exceptions; + +/** + * Signals that the operation will result in duplicate Offers (Offers are considered duplicates if they have the same + * identity). + */ +public class DuplicateOfferException extends RuntimeException { + public DuplicateOfferException() { + super("Operation would result in duplicate offers"); + } + +} diff --git a/src/main/java/seedu/realtime/model/offer/exceptions/OfferNotFoundException.java b/src/main/java/seedu/realtime/model/offer/exceptions/OfferNotFoundException.java new file mode 100644 index 00000000000..c2f66f7235e --- /dev/null +++ b/src/main/java/seedu/realtime/model/offer/exceptions/OfferNotFoundException.java @@ -0,0 +1,7 @@ +package seedu.realtime.model.offer.exceptions; + +/** + * Signals that the operation is unable to find the specified offer. + */ +public class OfferNotFoundException extends RuntimeException { +} diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/realtime/model/person/Address.java similarity index 93% rename from src/main/java/seedu/address/model/person/Address.java rename to src/main/java/seedu/realtime/model/person/Address.java index 60472ca22a0..a3a7c3edaf4 100644 --- a/src/main/java/seedu/address/model/person/Address.java +++ b/src/main/java/seedu/realtime/model/person/Address.java @@ -1,7 +1,7 @@ -package seedu.address.model.person; +package seedu.realtime.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.realtime.commons.util.AppUtil.checkArgument; /** * Represents a Person's address in the address book. diff --git a/src/main/java/seedu/realtime/model/person/Client.java b/src/main/java/seedu/realtime/model/person/Client.java new file mode 100644 index 00000000000..e3cc70c168f --- /dev/null +++ b/src/main/java/seedu/realtime/model/person/Client.java @@ -0,0 +1,103 @@ +package seedu.realtime.model.person; + +import java.util.HashSet; +import java.util.Set; + +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.meeting.Meeting; +import seedu.realtime.model.offer.Offer; +import seedu.realtime.model.tag.Tag; + +/** + * Represents a Client in the address book. + * Guarantees: details are present and not null, field values are validated, immutable + * except meetinglist, listinglist and offerlist. + */ + +public class Client extends Person implements Comparable { + + + // Identity fields + private final Set tags = new HashSet<>(); + + + /** + * Every field must be present and not null. + * + * @param name + * @param phone + * @param email + * @param address + * @param tags + */ + public Client(Name name, Phone phone, Email email, Address address, Set tags) { + super(name, phone, email, address, tags); + + } + + /** + * Constructor for sample client + */ + public Client() { + super( + new Name("Amy Bee"), + new Phone("85355255"), + new Email("amy@gmail.com"), + new Address("123, Jurong West Ave 6, #08-111"), new HashSet<>()); + } + + + /** + * Returns true if both client have the same name. + * This defines a weaker notion of equality between two client. + */ + public boolean isSameClient(Client otherClient) { + if (otherClient == this) { + return true; + } + + return otherClient != null + && this.getName().toString().toLowerCase().equals(( + otherClient.getName().toString().toLowerCase())); + } + + /** + * Returns true if this Listing is not owned by toCheck. + */ + public boolean doNotOwn(Listing toCheck) { + return !this.equals(toCheck.getName()); + } + + public boolean doNotHaveMeeting(Meeting toCheck) { + return !this.getName().equals(toCheck.getClient()); + } + + public boolean doNotHaveOffer(Offer toCheck) { + return !this.getName().equals(toCheck.getClient()); + } + + @Override + public int compareTo(Client o) { + return this.getName().fullName.compareTo(o.getName().fullName); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName()) + .append("; Phone: ") + .append(getPhone()) + .append("; Email: ") + .append(getEmail()) + .append("; Address: ") + .append(getAddress()); + + + Set tags = getTags(); + if (!tags.isEmpty()) { + builder.append("; Tags: "); + tags.forEach(builder::append); + } + return builder.toString(); + } +} diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/realtime/model/person/Email.java similarity index 96% rename from src/main/java/seedu/address/model/person/Email.java rename to src/main/java/seedu/realtime/model/person/Email.java index f866e7133de..b0c0efe3f4a 100644 --- a/src/main/java/seedu/address/model/person/Email.java +++ b/src/main/java/seedu/realtime/model/person/Email.java @@ -1,7 +1,7 @@ -package seedu.address.model.person; +package seedu.realtime.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.realtime.commons.util.AppUtil.checkArgument; /** * Represents a Person's email in the address book. diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/realtime/model/person/Name.java similarity index 87% rename from src/main/java/seedu/address/model/person/Name.java rename to src/main/java/seedu/realtime/model/person/Name.java index 79244d71cf7..40d3062b415 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/seedu/realtime/model/person/Name.java @@ -1,7 +1,7 @@ -package seedu.address.model.person; +package seedu.realtime.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.realtime.commons.util.AppUtil.checkArgument; /** * Represents a Person's name in the address book. @@ -48,7 +48,7 @@ public String toString() { public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof Name // instanceof handles nulls - && fullName.equals(((Name) other).fullName)); // state check + && fullName.toLowerCase().equals(((Name) other).fullName.toLowerCase())); // state check } @Override diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/realtime/model/person/NameContainsKeywordsPredicate.java similarity index 75% rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/seedu/realtime/model/person/NameContainsKeywordsPredicate.java index c9b5868427c..11dfdfde4db 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/seedu/realtime/model/person/NameContainsKeywordsPredicate.java @@ -1,14 +1,14 @@ -package seedu.address.model.person; +package seedu.realtime.model.person; import java.util.List; import java.util.function.Predicate; -import seedu.address.commons.util.StringUtil; +import seedu.realtime.commons.util.StringUtil; /** - * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. + * Tests that a {@code Client}'s {@code Name} matches any of the keywords given. */ -public class NameContainsKeywordsPredicate implements Predicate { +public class NameContainsKeywordsPredicate implements Predicate { private final List keywords; public NameContainsKeywordsPredicate(List keywords) { @@ -16,9 +16,9 @@ public NameContainsKeywordsPredicate(List keywords) { } @Override - public boolean test(Person person) { + public boolean test(Client client) { return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(client.getName().fullName, keyword)); } @Override diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/realtime/model/person/Person.java similarity index 95% rename from src/main/java/seedu/address/model/person/Person.java rename to src/main/java/seedu/realtime/model/person/Person.java index 8ff1d83fe89..30c5a4972ac 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/seedu/realtime/model/person/Person.java @@ -1,13 +1,13 @@ -package seedu.address.model.person; +package seedu.realtime.model.person; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.realtime.commons.util.CollectionUtil.requireAllNonNull; import java.util.Collections; import java.util.HashSet; import java.util.Objects; import java.util.Set; -import seedu.address.model.tag.Tag; +import seedu.realtime.model.tag.Tag; /** * Represents a Person in the address book. diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/realtime/model/person/Phone.java similarity index 92% rename from src/main/java/seedu/address/model/person/Phone.java rename to src/main/java/seedu/realtime/model/person/Phone.java index 872c76b382f..8a5468fe3d6 100644 --- a/src/main/java/seedu/address/model/person/Phone.java +++ b/src/main/java/seedu/realtime/model/person/Phone.java @@ -1,7 +1,7 @@ -package seedu.address.model.person; +package seedu.realtime.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.realtime.commons.util.AppUtil.checkArgument; /** * Represents a Person's phone number in the address book. diff --git a/src/main/java/seedu/realtime/model/person/UniqueClientList.java b/src/main/java/seedu/realtime/model/person/UniqueClientList.java new file mode 100644 index 00000000000..1bebd7713cb --- /dev/null +++ b/src/main/java/seedu/realtime/model/person/UniqueClientList.java @@ -0,0 +1,166 @@ +package seedu.realtime.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.realtime.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.realtime.model.person.exceptions.ClientNotFoundException; +import seedu.realtime.model.person.exceptions.DuplicateClientException; + + +/** + * A list of clients that enforces uniqueness between its elements and does not allow nulls. + * A client is considered unique by comparing using {@code Client#isSameClient(Client)}. As such, adding and updating of + * clients uses Client#isSameClient(Client)for equality so as to ensure that the client being added or updated is + * unique in terms of identity in the UniqueClientList. However, the removal of a client uses Client#equals(Object) so + * as to ensure that the client with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Client#isSameClient(Client) + */ +public class UniqueClientList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent client as the given argument. + */ + public boolean contains(Client toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameClient); + } + + /** + * Adds a client to the list. + * The client must not already exist in the list. + */ + public void add(Client toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateClientException(); + } + internalList.add(toAdd); + Collections.sort(internalList); + } + + + /** + * Gets the client with the given name {@code name}. + * @param name name of the client + * @return client with given name + */ + public Client getClient(Name name) { + requireNonNull(name); + for (Client client : internalList) { + if (client.getName().equals(name)) { + return client; + } + } + throw new ClientNotFoundException(); + } + + /** + * Replaces the client {@code target} in the list with {@code editedClient}. + * {@code target} must exist in the list. + * The client identity of {@code editedClient} must not be the same as another existing Client in the list. + */ + public void setClient(Client target, Client editedClient) { + requireAllNonNull(target, editedClient); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new ClientNotFoundException(); + } + + if (!target.isSameClient(editedClient) && contains(editedClient)) { + throw new DuplicateClientException(); + } + + internalList.set(index, editedClient); + } + + /** + * Removes the equivalent client from the list. + * The client must exist in the list. + */ + public void remove(Client toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new ClientNotFoundException(); + } + } + + public void setClients(UniqueClientList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code clients}. + * {@code clients} must not contain duplicate clients. + */ + public void setClients(List clients) { + requireAllNonNull(clients); + if (!clientsAreUnique(clients)) { + throw new DuplicateClientException(); + } + + internalList.setAll(clients); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueClientList // instanceof handles nulls + && internalList.equals(((UniqueClientList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code clients} contains only unique clients. + */ + private boolean clientsAreUnique(List clients) { + for (int i = 0; i < clients.size() - 1; i++) { + for (int j = i + 1; j < clients.size(); j++) { + if (clients.get(i).isSameClient(clients.get(j))) { + return false; + } + } + } + return true; + } + + private Client findClient(Name name) throws ClientNotFoundException { + for (int i = 0; i < internalList.size(); i++) { + Client temp = internalList.get(i); + if (temp.getName() == name) { + return temp; + } + } throw new ClientNotFoundException(); + } +} + diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/realtime/model/person/UniquePersonList.java similarity index 86% rename from src/main/java/seedu/address/model/person/UniquePersonList.java rename to src/main/java/seedu/realtime/model/person/UniquePersonList.java index 0fee4fe57e6..b082722dff8 100644 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ b/src/main/java/seedu/realtime/model/person/UniquePersonList.java @@ -1,15 +1,15 @@ -package seedu.address.model.person; +package seedu.realtime.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.realtime.commons.util.CollectionUtil.requireAllNonNull; import java.util.Iterator; import java.util.List; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; +import seedu.realtime.model.person.exceptions.DuplicatePersonException; +import seedu.realtime.model.person.exceptions.PersonNotFoundException; /** * A list of persons that enforces uniqueness between its elements and does not allow nulls. @@ -48,6 +48,21 @@ public void add(Person toAdd) { internalList.add(toAdd); } + /** + * Gets the person with the given name {@code name}. + * @param name name of the person + * @return person with given name + */ + public Person getPerson(Name name) { + requireNonNull(name); + for (Person person : internalList) { + if (person.getName().equals(name)) { + return person; + } + } + throw new PersonNotFoundException(); + } + /** * Replaces the person {@code target} in the list with {@code editedPerson}. * {@code target} must exist in the list. diff --git a/src/main/java/seedu/realtime/model/person/exceptions/ClientNotFoundException.java b/src/main/java/seedu/realtime/model/person/exceptions/ClientNotFoundException.java new file mode 100644 index 00000000000..c527a57c9a1 --- /dev/null +++ b/src/main/java/seedu/realtime/model/person/exceptions/ClientNotFoundException.java @@ -0,0 +1,6 @@ +package seedu.realtime.model.person.exceptions; + +/** + * Signals that the operation is unable to find the specified client. + */ +public class ClientNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/realtime/model/person/exceptions/DuplicateClientException.java b/src/main/java/seedu/realtime/model/person/exceptions/DuplicateClientException.java new file mode 100644 index 00000000000..d269a96c29d --- /dev/null +++ b/src/main/java/seedu/realtime/model/person/exceptions/DuplicateClientException.java @@ -0,0 +1,11 @@ +package seedu.realtime.model.person.exceptions; + +/** + * Signals that the operation will result in duplicate Clients (Clients are considered duplicates if they have the same + * identity). + */ +public class DuplicateClientException extends RuntimeException { + public DuplicateClientException() { + super("Operation would result in duplicate clients"); + } +} diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/realtime/model/person/exceptions/DuplicatePersonException.java similarity index 86% rename from src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java rename to src/main/java/seedu/realtime/model/person/exceptions/DuplicatePersonException.java index d7290f59442..f509d9a01a7 100644 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ b/src/main/java/seedu/realtime/model/person/exceptions/DuplicatePersonException.java @@ -1,4 +1,4 @@ -package seedu.address.model.person.exceptions; +package seedu.realtime.model.person.exceptions; /** * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/realtime/model/person/exceptions/PersonNotFoundException.java similarity index 75% rename from src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java rename to src/main/java/seedu/realtime/model/person/exceptions/PersonNotFoundException.java index fa764426ca7..84af3e5f7b3 100644 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ b/src/main/java/seedu/realtime/model/person/exceptions/PersonNotFoundException.java @@ -1,4 +1,4 @@ -package seedu.address.model.person.exceptions; +package seedu.realtime.model.person.exceptions; /** * Signals that the operation is unable to find the specified person. diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/realtime/model/tag/Tag.java similarity index 93% rename from src/main/java/seedu/address/model/tag/Tag.java rename to src/main/java/seedu/realtime/model/tag/Tag.java index b0ea7e7dad7..ba4f32fe7b5 100644 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ b/src/main/java/seedu/realtime/model/tag/Tag.java @@ -1,7 +1,7 @@ -package seedu.address.model.tag; +package seedu.realtime.model.tag; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.realtime.commons.util.AppUtil.checkArgument; /** * Represents a Tag in the address book. diff --git a/src/main/java/seedu/realtime/model/util/SampleDataUtil.java b/src/main/java/seedu/realtime/model/util/SampleDataUtil.java new file mode 100644 index 00000000000..ff802b3cb8a --- /dev/null +++ b/src/main/java/seedu/realtime/model/util/SampleDataUtil.java @@ -0,0 +1,123 @@ +package seedu.realtime.model.util; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import seedu.realtime.model.ReadOnlyRealTime; +import seedu.realtime.model.RealTime; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.offer.Offer; +import seedu.realtime.model.offer.Price; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Client; +import seedu.realtime.model.person.Email; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.person.Person; +import seedu.realtime.model.person.Phone; +import seedu.realtime.model.tag.Tag; + + +/** + * Contains utility methods for populating {@code realTime} with sample data. + */ +public class SampleDataUtil { + public static Person[] getSamplePersons() { + return new Person[] { + new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), + new Address("Blk 30 Geylang Street 29, #06-40"), + getTagSet("friends")), + new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), + new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), + getTagSet("colleagues", "friends")), + new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), + new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), + getTagSet("neighbours")), + new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), + new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), + getTagSet("family")), + new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), + new Address("Blk 47 Tampines Street 20, #17-35"), + getTagSet("classmates")), + new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), + new Address("Blk 45 Aljunied Street 85, #11-31"), + getTagSet("colleagues")) + }; + } + + + public static Client[] getSampleClients() { + return new Client[] { + new Client(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), + new Address("Blk 30 Geylang Street 29, #06-40"), + getTagSet("friends")), + new Client(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), + new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), + getTagSet("colleagues", "friends")), + new Client(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), + new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), + getTagSet("neighbours")), + new Client(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), + new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), + getTagSet("family")), + new Client(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), + new Address("Blk 47 Tampines Street 20, #17-35"), + getTagSet("classmates")), + new Client(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), + new Address("Blk 45 Aljunied Street 85, #11-31"), + getTagSet("colleagues")) + }; + } + + public static Offer[] getSampleOffers() { + return new Offer[] { + new Offer(new Name("Alex Yeoh"), new ListingId("30_GL_ST29_0640"), + new Price("1000000")), + new Offer(new Name("Bernice Yu"), new ListingId("30_SERGARDENS_LOR23_0718"), + new Price("900000")), + new Offer(new Name("Charlotte Oliveiro"), new ListingId("11_AMK_ST74_1104"), + new Price("900000")), + new Offer(new Name("David Li"), new ListingId("436_SERGARDENS_ST26_16-43"), + new Price("950000")), + new Offer(new Name("Irfan Ibrahim"), new ListingId("47_TAMP_ST20_1735"), + new Price("960000")), + new Offer(new Name("Roy Balakrishnan"), new ListingId("45_ALJU_ST85_1135"), + new Price("965000")) + }; + } + + public static Listing[] getSamepleListings() { + return new Listing[] { + new Listing(new ListingId("BEDOK_NORTH"), new Address("Bedok North Blk 123 #02-2222"), + new Name("Billy Joe"), new Price("760000")) + }; + } + + + + public static ReadOnlyRealTime getSampleRealTime() { + RealTime sampleRt = new RealTime(); + for (Client sampleClient : getSampleClients()) { + sampleRt.addClient(sampleClient); + } + for (Offer sampleOffer : getSampleOffers()) { + sampleRt.addOffer(sampleOffer); + } + + for (Listing sampleListing : getSamepleListings()) { + sampleRt.addListing(sampleListing); + } + return sampleRt; + } + + /** + * Returns a tag set containing the list of strings given. + */ + public static Set getTagSet(String... strings) { + return Arrays.stream(strings) + .map(Tag::new) + .collect(Collectors.toSet()); + } + +} diff --git a/src/main/java/seedu/realtime/storage/JsonAdaptedClient.java b/src/main/java/seedu/realtime/storage/JsonAdaptedClient.java new file mode 100644 index 00000000000..e1d5530e01c --- /dev/null +++ b/src/main/java/seedu/realtime/storage/JsonAdaptedClient.java @@ -0,0 +1,110 @@ +package seedu.realtime.storage; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.realtime.commons.exceptions.IllegalValueException; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Client; +import seedu.realtime.model.person.Email; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.person.Phone; +import seedu.realtime.model.tag.Tag; + +/** + * Jackson-friendly version of {@link Client}. + */ +class JsonAdaptedClient { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Client's %s field is missing!"; + + private final String name; + private final String phone; + private final String email; + private final String address; + private final List tagged = new ArrayList<>(); + + /** + * Constructs a {@code JsonAdaptedClient} with the given client details. + */ + @JsonCreator + public JsonAdaptedClient(@JsonProperty("name") String name, @JsonProperty("phone") String phone, + @JsonProperty("email") String email, @JsonProperty("address") String address, + @JsonProperty("tagged") List tagged) { + this.name = name; + this.phone = phone; + this.email = email; + this.address = address; + if (tagged != null) { + this.tagged.addAll(tagged); + } + } + + /** + * Converts a given {@code Client} into this class for Jackson use. + */ + public JsonAdaptedClient(Client source) { + name = source.getName().fullName; + phone = source.getPhone().value; + email = source.getEmail().value; + address = source.getAddress().value; + tagged.addAll(source.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); + } + + /** + * Converts this Jackson-friendly adapted client object into the model's {@code Client} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted client. + */ + public Client toModelType() throws IllegalValueException { + final List clientTags = new ArrayList<>(); + for (JsonAdaptedTag tag : tagged) { + clientTags.add(tag.toModelType()); + } + + if (name == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); + } + if (!Name.isValidName(name)) { + throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); + } + final Name modelName = new Name(name); + + if (phone == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); + } + if (!Phone.isValidPhone(phone)) { + throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); + } + final Phone modelPhone = new Phone(phone); + + if (email == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); + } + if (!Email.isValidEmail(email)) { + throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); + } + final Email modelEmail = new Email(email); + + if (address == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); + } + if (!Address.isValidAddress(address)) { + throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); + } + final Address modelAddress = new Address(address); + + final Set modelTags = new HashSet<>(clientTags); + return new Client(modelName, modelPhone, modelEmail, modelAddress, modelTags); + } + +} + diff --git a/src/main/java/seedu/realtime/storage/JsonAdaptedListing.java b/src/main/java/seedu/realtime/storage/JsonAdaptedListing.java new file mode 100644 index 00000000000..53b3c07a9f8 --- /dev/null +++ b/src/main/java/seedu/realtime/storage/JsonAdaptedListing.java @@ -0,0 +1,110 @@ +package seedu.realtime.storage; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.realtime.commons.exceptions.IllegalValueException; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.offer.Price; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.tag.Tag; + +/** + * Jackson-friendly version of {@link Listing}. + */ +public class JsonAdaptedListing { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Listing's %s field is missing!"; + + private final String listingId; + private final String address; + private final String name; + private final String askingPrice; + private final List tagged = new ArrayList<>(); + + /** + * Constructs a {@code JsonAdaptedClient} with the given client details. + */ + @JsonCreator + public JsonAdaptedListing(@JsonProperty("listingId") String listingId, @JsonProperty("address") String address, + @JsonProperty("name") String name, @JsonProperty("askingPrice") String askingPrice, + @JsonProperty("tagged") List tagged) { + this.listingId = listingId; + this.address = address; + this.name = name; + this.askingPrice = askingPrice; + if (tagged != null) { + this.tagged.addAll(tagged); + } + } + + /** + * Converts a given {@code Client} into this class for Jackson use. + */ + public JsonAdaptedListing(Listing source) { + listingId = source.getId().value; + address = source.getAddress().value; + name = source.getName().fullName; + askingPrice = source.getAskingPrice().value; + tagged.addAll(source.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); + } + + /** + * Converts this Jackson-friendly adapted client object into the model's {@code Listing} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted client. + */ + public Listing toModelType() throws IllegalValueException { + final List listingTags = new ArrayList<>(); + for (JsonAdaptedTag tag : tagged) { + listingTags.add(tag.toModelType()); + } + + if (listingId == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + ListingId.class.getSimpleName())); + } + if (!ListingId.isValidListingId(listingId)) { + throw new IllegalValueException(ListingId.MESSAGE_CONSTRAINTS); + } + final ListingId modelListingId = new ListingId(listingId); + + if (address == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); + } + if (!Address.isValidAddress(address)) { + throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); + } + final Address modelAddress = new Address(address); + + if (name == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); + } + if (!Name.isValidName(name)) { + throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); + } + final Name modelName = new Name(name); + + if (askingPrice == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Price.class.getSimpleName())); + } + if (!Price.isValidPrice(askingPrice)) { + throw new IllegalValueException(Price.MESSAGE_CONSTRAINTS); + } + final Price modelAskingPrice = new Price(askingPrice); + + + final Set modelTags = new HashSet<>(listingTags); + return new Listing(modelListingId, modelAddress, modelName, modelAskingPrice); + } +} diff --git a/src/main/java/seedu/realtime/storage/JsonAdaptedOffer.java b/src/main/java/seedu/realtime/storage/JsonAdaptedOffer.java new file mode 100644 index 00000000000..7d6af14f364 --- /dev/null +++ b/src/main/java/seedu/realtime/storage/JsonAdaptedOffer.java @@ -0,0 +1,78 @@ +package seedu.realtime.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.realtime.commons.exceptions.IllegalValueException; +import seedu.realtime.model.listing.ListingId; +import seedu.realtime.model.offer.Offer; +import seedu.realtime.model.offer.Price; +import seedu.realtime.model.person.Name; + +/** + * Jackson-friendly version of {@link Offer}. + */ +public class JsonAdaptedOffer { + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Offer's %s field is missing!"; + + private final String name; + private final String listing; + private final String offerPrice; + + /** + * Constructs a {@code JsonAdaptedOffer} with the given person details. + */ + @JsonCreator + public JsonAdaptedOffer(@JsonProperty("name") String name, @JsonProperty("listing") String listing, + @JsonProperty("offerPrice") String offerPrice) { + this.name = name; + this.listing = listing; + this.offerPrice = offerPrice; + } + + /** + * Converts a given {@code Offer} into this class for Jackson use. + */ + public JsonAdaptedOffer(Offer source) { + name = source.getClient().fullName; + listing = source.getListing().value; + offerPrice = source.getOfferPrice().value; + } + + /** + * Converts this Jackson-friendly adapted offer object into the model's {@code Offer} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted offer. + */ + public Offer toModelType() throws IllegalValueException { + if (name == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); + } + if (!Name.isValidName(name)) { + throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); + } + final Name modelName = new Name(name); + + if (listing == null) { + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, ListingId.class.getSimpleName())); + } + if (!ListingId.isValidListingId(listing)) { + throw new IllegalValueException(ListingId.MESSAGE_CONSTRAINTS); + } + final ListingId modelListingId = new ListingId(listing); + + if (offerPrice == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Price.class.getSimpleName())); + } + + if (!Price.isValidPrice(offerPrice)) { + throw new IllegalValueException(Price.MESSAGE_CONSTRAINTS); + } + + final Price modelOfferPrice = new Price(offerPrice); + + + return new Offer(modelName, modelListingId, modelOfferPrice); + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/realtime/storage/JsonAdaptedPerson.java similarity index 84% rename from src/main/java/seedu/address/storage/JsonAdaptedPerson.java rename to src/main/java/seedu/realtime/storage/JsonAdaptedPerson.java index a6321cec2ea..f3a3576f1c4 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ b/src/main/java/seedu/realtime/storage/JsonAdaptedPerson.java @@ -1,4 +1,4 @@ -package seedu.address.storage; +package seedu.realtime.storage; import java.util.ArrayList; import java.util.HashSet; @@ -9,13 +9,13 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import seedu.realtime.commons.exceptions.IllegalValueException; +import seedu.realtime.model.person.Address; +import seedu.realtime.model.person.Email; +import seedu.realtime.model.person.Name; +import seedu.realtime.model.person.Person; +import seedu.realtime.model.person.Phone; +import seedu.realtime.model.tag.Tag; /** * Jackson-friendly version of {@link Person}. @@ -35,8 +35,8 @@ class JsonAdaptedPerson { */ @JsonCreator public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone, - @JsonProperty("email") String email, @JsonProperty("address") String address, - @JsonProperty("tagged") List tagged) { + @JsonProperty("email") String email, @JsonProperty("address") String address, + @JsonProperty("tagged") List tagged) { this.name = name; this.phone = phone; this.email = email; @@ -55,8 +55,8 @@ public JsonAdaptedPerson(Person source) { email = source.getEmail().value; address = source.getAddress().value; tagged.addAll(source.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList())); + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); } /** diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/realtime/storage/JsonAdaptedTag.java similarity index 89% rename from src/main/java/seedu/address/storage/JsonAdaptedTag.java rename to src/main/java/seedu/realtime/storage/JsonAdaptedTag.java index 0df22bdb754..84681752692 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java +++ b/src/main/java/seedu/realtime/storage/JsonAdaptedTag.java @@ -1,10 +1,10 @@ -package seedu.address.storage; +package seedu.realtime.storage; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; +import seedu.realtime.commons.exceptions.IllegalValueException; +import seedu.realtime.model.tag.Tag; /** * Jackson-friendly version of {@link Tag}. diff --git a/src/main/java/seedu/realtime/storage/JsonRealTimeStorage.java b/src/main/java/seedu/realtime/storage/JsonRealTimeStorage.java new file mode 100644 index 00000000000..ebc51b8a90e --- /dev/null +++ b/src/main/java/seedu/realtime/storage/JsonRealTimeStorage.java @@ -0,0 +1,80 @@ +package seedu.realtime.storage; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.realtime.commons.core.LogsCenter; +import seedu.realtime.commons.exceptions.DataConversionException; +import seedu.realtime.commons.exceptions.IllegalValueException; +import seedu.realtime.commons.util.FileUtil; +import seedu.realtime.commons.util.JsonUtil; +import seedu.realtime.model.ReadOnlyRealTime; + +/** + * A class to access realTime data stored as a json file on the hard disk. + */ +public class JsonRealTimeStorage implements RealTimeStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonRealTimeStorage.class); + + private Path filePath; + + public JsonRealTimeStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getRealTimeFilePath() { + return filePath; + } + + @Override + public Optional readRealTime() throws DataConversionException { + return readRealTime(filePath); + } + + /** + * Similar to {@link #readRealTime()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readRealTime(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonRealTime = JsonUtil.readJsonFile( + filePath, JsonSerializableRealTime.class); + if (!jsonRealTime.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonRealTime.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveRealTime(ReadOnlyRealTime realTime) throws IOException { + saveRealTime(realTime, filePath); + } + + /** + * Similar to {@link #saveRealTime(ReadOnlyRealTime)}. + * + * @param filePath location of the data. Cannot be null. + */ + public void saveRealTime(ReadOnlyRealTime realTime, Path filePath) throws IOException { + requireNonNull(realTime); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableRealTime(realTime), filePath); + } + +} diff --git a/src/main/java/seedu/realtime/storage/JsonSerializableRealTime.java b/src/main/java/seedu/realtime/storage/JsonSerializableRealTime.java new file mode 100644 index 00000000000..1bfd6219246 --- /dev/null +++ b/src/main/java/seedu/realtime/storage/JsonSerializableRealTime.java @@ -0,0 +1,91 @@ +package seedu.realtime.storage; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; + +import seedu.realtime.commons.exceptions.IllegalValueException; +import seedu.realtime.model.ReadOnlyRealTime; +import seedu.realtime.model.RealTime; +import seedu.realtime.model.listing.Listing; +import seedu.realtime.model.offer.Offer; +import seedu.realtime.model.person.Client; + +/** + * An Immutable RealTime that is serializable to JSON format. + */ +@JsonRootName(value = "RealTime") +class JsonSerializableRealTime { + + + public static final String MESSAGE_DUPLICATE_CLIENT = "Clients list contains duplicate client(s)."; + public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; + public static final String MESSAGE_DUPLICATE_OFFER = "Offers list contains duplicate offer(s)"; + public static final String MESSAGE_DUPLICATE_LISTING = "Listings list contains duplicate listing(s)."; + + private final List persons = new ArrayList<>(); + private final List clients = new ArrayList<>(); + private final List offers = new ArrayList<>(); + private final List listings = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableRealTime} with the given clients and offers. + */ + @JsonCreator + public JsonSerializableRealTime(@JsonProperty("clients") List clients, + @JsonProperty("offers") List offers, + @JsonProperty("listings") List listings) { + this.clients.addAll(clients); + this.offers.addAll(offers); + this.listings.addAll(listings); + } + + /** + * Converts a given {@code ReadOnlyRealTime} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableRealTime}. + */ + public JsonSerializableRealTime(ReadOnlyRealTime source) { + clients.addAll(source.getClientList().stream().map(JsonAdaptedClient::new).collect(Collectors.toList())); + offers.addAll(source.getOfferList().stream().map(JsonAdaptedOffer::new).collect(Collectors.toList())); + listings.addAll(source.getListingList().stream().map(JsonAdaptedListing::new).collect(Collectors.toList())); + } + + /** + * Converts realtime into the model's {@code RealTime} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public RealTime toModelType() throws IllegalValueException { + RealTime realTime = new RealTime(); + for (JsonAdaptedClient jsonAdaptedClient : clients) { + Client client = jsonAdaptedClient.toModelType(); + if (realTime.hasClient(client)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_CLIENT); + } + realTime.addClient(client); + } + + for (JsonAdaptedOffer jsonAdaptedOffer : offers) { + Offer offer = jsonAdaptedOffer.toModelType(); + if (realTime.hasOffer(offer)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_OFFER); + } + realTime.addOffer(offer); + } + + for (JsonAdaptedListing jsonAdaptedListing : listings) { + Listing listing = jsonAdaptedListing.toModelType(); + if (realTime.hasListing(listing)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_LISTING); + } + realTime.addListing(listing); + } + return realTime; + } + +} diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/seedu/realtime/storage/JsonUserPrefsStorage.java similarity index 82% rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java rename to src/main/java/seedu/realtime/storage/JsonUserPrefsStorage.java index bc2bbad84aa..3d58d4f2a82 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/seedu/realtime/storage/JsonUserPrefsStorage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package seedu.realtime.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.realtime.commons.exceptions.DataConversionException; +import seedu.realtime.commons.util.JsonUtil; +import seedu.realtime.model.ReadOnlyUserPrefs; +import seedu.realtime.model.UserPrefs; /** * A class to access UserPrefs stored in the hard disk as a json file diff --git a/src/main/java/seedu/realtime/storage/RealTimeStorage.java b/src/main/java/seedu/realtime/storage/RealTimeStorage.java new file mode 100644 index 00000000000..e4c2a37891e --- /dev/null +++ b/src/main/java/seedu/realtime/storage/RealTimeStorage.java @@ -0,0 +1,46 @@ +package seedu.realtime.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.realtime.commons.exceptions.DataConversionException; +import seedu.realtime.model.ReadOnlyRealTime; +import seedu.realtime.model.RealTime; + +/** + * Represents a storage for {@link RealTime}. + */ +public interface RealTimeStorage { + + /** + * Returns the file path of the data file. + */ + Path getRealTimeFilePath(); + + /** + * Returns realTime data as a {@link ReadOnlyRealTime}. + * Returns {@code Optional.empty()} if storage file is not found. + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional readRealTime() throws DataConversionException, IOException; + + /** + * @see #getRealTimeFilePath() + */ + Optional readRealTime(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyRealTime} to the storage. + * @param realTime cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveRealTime(ReadOnlyRealTime realTime) throws IOException; + + /** + * @see #saveRealTime(ReadOnlyRealTime) + */ + void saveRealTime(ReadOnlyRealTime realTime, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/realtime/storage/Storage.java b/src/main/java/seedu/realtime/storage/Storage.java new file mode 100644 index 00000000000..1538ab07be8 --- /dev/null +++ b/src/main/java/seedu/realtime/storage/Storage.java @@ -0,0 +1,32 @@ +package seedu.realtime.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.realtime.commons.exceptions.DataConversionException; +import seedu.realtime.model.ReadOnlyRealTime; +import seedu.realtime.model.ReadOnlyUserPrefs; +import seedu.realtime.model.UserPrefs; + +/** + * API of the Storage component + */ +public interface Storage extends RealTimeStorage, UserPrefsStorage { + + @Override + Optional readUserPrefs() throws DataConversionException, IOException; + + @Override + void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; + + @Override + Path getRealTimeFilePath(); + + @Override + Optional readRealTime() throws DataConversionException, IOException; + + @Override + void saveRealTime(ReadOnlyRealTime realTime) throws IOException; + +} diff --git a/src/main/java/seedu/realtime/storage/StorageManager.java b/src/main/java/seedu/realtime/storage/StorageManager.java new file mode 100644 index 00000000000..d0843fdacbc --- /dev/null +++ b/src/main/java/seedu/realtime/storage/StorageManager.java @@ -0,0 +1,78 @@ +package seedu.realtime.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.realtime.commons.core.LogsCenter; +import seedu.realtime.commons.exceptions.DataConversionException; +import seedu.realtime.model.ReadOnlyRealTime; +import seedu.realtime.model.ReadOnlyUserPrefs; +import seedu.realtime.model.UserPrefs; + +/** + * Manages storage of realTime data in local storage. + */ +public class StorageManager implements Storage { + + private static final Logger logger = LogsCenter.getLogger(StorageManager.class); + private RealTimeStorage realTimeStorage; + private UserPrefsStorage userPrefsStorage; + + /** + * Creates a {@code StorageManager} with the given {@code RealTimeStorage} and {@code UserPrefStorage}. + */ + public StorageManager(RealTimeStorage realTimeStorage, UserPrefsStorage userPrefsStorage) { + this.realTimeStorage = realTimeStorage; + this.userPrefsStorage = userPrefsStorage; + } + + // ================ UserPrefs methods ============================== + + @Override + public Path getUserPrefsFilePath() { + return userPrefsStorage.getUserPrefsFilePath(); + } + + @Override + public Optional readUserPrefs() throws DataConversionException, IOException { + return userPrefsStorage.readUserPrefs(); + } + + @Override + public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { + userPrefsStorage.saveUserPrefs(userPrefs); + } + + + // ================ realTime methods ============================== + + @Override + public Path getRealTimeFilePath() { + return realTimeStorage.getRealTimeFilePath(); + } + + @Override + public Optional readRealTime() throws DataConversionException, IOException { + return readRealTime(realTimeStorage.getRealTimeFilePath()); + } + + @Override + public Optional readRealTime(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return realTimeStorage.readRealTime(filePath); + } + + @Override + public void saveRealTime(ReadOnlyRealTime realTime) throws IOException { + saveRealTime(realTime, realTimeStorage.getRealTimeFilePath()); + } + + @Override + public void saveRealTime(ReadOnlyRealTime realTime, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + realTimeStorage.saveRealTime(realTime, filePath); + } + +} diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/seedu/realtime/storage/UserPrefsStorage.java similarity index 71% rename from src/main/java/seedu/address/storage/UserPrefsStorage.java rename to src/main/java/seedu/realtime/storage/UserPrefsStorage.java index 29eef178dbc..ec3a58ff411 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/seedu/realtime/storage/UserPrefsStorage.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package seedu.realtime.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.realtime.commons.exceptions.DataConversionException; +import seedu.realtime.model.ReadOnlyUserPrefs; +import seedu.realtime.model.UserPrefs; /** - * Represents a storage for {@link seedu.address.model.UserPrefs}. + * Represents a storage for {@link seedu.realtime.model.UserPrefs}. */ public interface UserPrefsStorage { @@ -27,7 +27,7 @@ public interface UserPrefsStorage { Optional readUserPrefs() throws DataConversionException, IOException; /** - * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage. + * Saves the given {@link seedu.realtime.model.ReadOnlyUserPrefs} to the storage. * @param userPrefs cannot be null. * @throws IOException if there was any problem writing to the file. */ diff --git a/src/main/java/seedu/realtime/ui/ClientCard.java b/src/main/java/seedu/realtime/ui/ClientCard.java new file mode 100644 index 00000000000..3c83ff2b110 --- /dev/null +++ b/src/main/java/seedu/realtime/ui/ClientCard.java @@ -0,0 +1,79 @@ +package seedu.realtime.ui; + +import java.util.Comparator; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.realtime.model.person.Client; + +/** + * An UI component that displays information of a {@code Client}. + */ +public class ClientCard extends UiPart { + + + private static final String FXML = "ClientListCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + public final Client client; + + @javafx.fxml.FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label phone; + @FXML + private Label address; + @FXML + private Label email; + @FXML + private FlowPane tags; + + + /** + * Creates a {@code ClientCode} with the given {@code Client} and index to display. + */ + public ClientCard(Client client, int displayedIndex) { + super(FXML); + this.client = client; + id.setText(displayedIndex + ". "); + name.setText(client.getName().fullName); + phone.setText(client.getPhone().value); + address.setText(client.getAddress().value); + email.setText(client.getEmail().value); + client.getTags().stream() + .sorted(Comparator.comparing(tag -> tag.tagName)) + .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ClientCard)) { + return false; + } + + // state check + ClientCard card = (ClientCard) other; + return id.getText().equals(card.id.getText()) + && client.equals(card.client); + } +} diff --git a/src/main/java/seedu/realtime/ui/ClientListPanel.java b/src/main/java/seedu/realtime/ui/ClientListPanel.java new file mode 100644 index 00000000000..454515b6c3f --- /dev/null +++ b/src/main/java/seedu/realtime/ui/ClientListPanel.java @@ -0,0 +1,49 @@ +package seedu.realtime.ui; + +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import seedu.realtime.commons.core.LogsCenter; +import seedu.realtime.model.person.Client; + +/** + * Panel containing the list of client. + */ +public class ClientListPanel extends UiPart { + private static final String FXML = "ClientListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(ClientListPanel.class); + + @FXML + private ListView clientListView; + + /** + * Creates a {@code ClientListPanel} with the given {@code ObservableList}. + */ + public ClientListPanel(ObservableList clientList) { + super(FXML); + clientListView.setItems(clientList); + clientListView.setCellFactory(listView -> new ClientListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Client} using a {@code ClientCard}. + */ + class ClientListViewCell extends ListCell { + @Override + protected void updateItem(Client client, boolean empty) { + super.updateItem(client, empty); + + if (empty || client == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new ClientCard(client, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/realtime/ui/CommandBox.java similarity index 89% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/seedu/realtime/ui/CommandBox.java index 9e75478664b..dbbc1a0a22c 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/realtime/ui/CommandBox.java @@ -1,12 +1,12 @@ -package seedu.address.ui; +package seedu.realtime.ui; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.TextField; import javafx.scene.layout.Region; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.realtime.logic.commands.CommandResult; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.logic.parser.exceptions.ParseException; /** * The UI component that is responsible for receiving user command inputs. @@ -77,7 +77,7 @@ public interface CommandExecutor { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see seedu.realtime.logic.Logic#execute(String) */ CommandResult execute(String commandText) throws CommandException, ParseException; } diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/realtime/ui/HelpWindow.java similarity index 70% rename from src/main/java/seedu/address/ui/HelpWindow.java rename to src/main/java/seedu/realtime/ui/HelpWindow.java index 3f16b2fcf26..9cc71385ded 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/realtime/ui/HelpWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.realtime.ui; import java.util.logging.Logger; @@ -8,15 +8,28 @@ import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.Stage; -import seedu.address.commons.core.LogsCenter; +import seedu.realtime.commons.core.LogsCenter; /** * Controller for a help page */ public class HelpWindow extends UiPart { - public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html"; - public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; + public static final String USERGUIDE_URL = "https://ay2223s1-cs2103t-w15-2.github.io/tp/UserGuide.html"; + public static final String HELP_URL = "Refer to the user guide for more information: " + USERGUIDE_URL; + public static final String HELP_LIST = "These are all the commands available:\n" + + "addC - adds a client\n" + + "addL - adds a listing\n" + + "addO - adds an offer\n" + + "addTags - adds tags to a listing\n" + + "clear - clears the entire list of clients, meetings, offers and listings\n" + + "deleteC - deletes the specified client\n" + + "deleteL - deletes the specified listing\n" + + "deleteO - deletes the specified offer\n" + + "editC - edits the specified client\n" + + "editL - edits the specified listing\n" + + "editO - edits the specified offer\n"; + public static final String HELP_MESSAGE = HELP_LIST + HELP_URL; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); private static final String FXML = "HelpWindow.fxml"; diff --git a/src/main/java/seedu/realtime/ui/ListingCard.java b/src/main/java/seedu/realtime/ui/ListingCard.java new file mode 100644 index 00000000000..25a2c3c20e3 --- /dev/null +++ b/src/main/java/seedu/realtime/ui/ListingCard.java @@ -0,0 +1,78 @@ +package seedu.realtime.ui; + +import java.util.Comparator; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.realtime.model.listing.Listing; + +/** + * An UI component that displays information of a {@code Listing}. + */ +public class ListingCard extends UiPart { + + private static final String FXML = "ListingListCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + public final Listing listing; + + @FXML + private HBox cardPane; + @FXML + private Label id; + @FXML + private Label listingId; + @FXML + private Label name; + @FXML + private Label address; + @FXML + private Label askingPrice; + @FXML + private FlowPane tags; + + + /** + * Creates a {@code ClientCode} with the given {@code Client} and index to display. + */ + public ListingCard(Listing listing, int displayedIndex) { + super(FXML); + this.listing = listing; + id.setText(displayedIndex + ". "); + listingId.setText(listing.getId().value); + name.setText(listing.getName().fullName); + address.setText(listing.getAddress().value); + askingPrice.setText(listing.getAskingPrice().value); + listing.getTags().stream() + .sorted(Comparator.comparing(tag -> tag.tagName)) + .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ListingCard)) { + return false; + } + + // state check + ListingCard card = (ListingCard) other; + return id.getText().equals(card.id.getText()) + && listing.equals(card.listing); + } +} diff --git a/src/main/java/seedu/realtime/ui/ListingListPanel.java b/src/main/java/seedu/realtime/ui/ListingListPanel.java new file mode 100644 index 00000000000..542e8ba7734 --- /dev/null +++ b/src/main/java/seedu/realtime/ui/ListingListPanel.java @@ -0,0 +1,49 @@ +package seedu.realtime.ui; + +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import seedu.realtime.commons.core.LogsCenter; +import seedu.realtime.model.listing.Listing; + +/** + * Panel containing the list of listings. + */ +public class ListingListPanel extends UiPart { + + private static final String FXML = "ListingListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(ListingListPanel.class); + + @FXML + private ListView

listingListView; + + /** + * Creates a {@code ListingListPanel} with the given {@code ObservableList}. + */ + public ListingListPanel(ObservableList listingList) { + super(FXML); + listingListView.setItems(listingList); + listingListView.setCellFactory(listView -> new ListingListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Listing} using a {@code ListingCard}. + */ + class ListingListViewCell extends ListCell { + @Override + protected void updateItem(Listing listing, boolean empty) { + super.updateItem(listing, empty); + + if (empty || listing == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new ListingCard(listing, getIndex() + 1).getRoot()); + } + } + } +} diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/realtime/ui/MainWindow.java similarity index 74% rename from src/main/java/seedu/address/ui/MainWindow.java rename to src/main/java/seedu/realtime/ui/MainWindow.java index 9106c3aa6e5..dbd83d2f6ca 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/realtime/ui/MainWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.realtime.ui; import java.util.logging.Logger; @@ -10,12 +10,12 @@ import javafx.scene.input.KeyEvent; import javafx.scene.layout.StackPane; import javafx.stage.Stage; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.logic.Logic; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.realtime.commons.core.GuiSettings; +import seedu.realtime.commons.core.LogsCenter; +import seedu.realtime.logic.Logic; +import seedu.realtime.logic.commands.CommandResult; +import seedu.realtime.logic.commands.exceptions.CommandException; +import seedu.realtime.logic.parser.exceptions.ParseException; /** * The Main Window. Provides the basic application layout containing @@ -27,13 +27,16 @@ public class MainWindow extends UiPart { private final Logger logger = LogsCenter.getLogger(getClass()); - private Stage primaryStage; - private Logic logic; + private final Stage primaryStage; + private final Logic logic; // Independent Ui parts residing in this Ui container - private PersonListPanel personListPanel; + private ClientListPanel clientListPanel; + private OfferListPanel offerListPanel; + private ListingListPanel listingListPanel; + private MeetingListPanel meetingListPanel; private ResultDisplay resultDisplay; - private HelpWindow helpWindow; + private final HelpWindow helpWindow; @FXML private StackPane commandBoxPlaceholder; @@ -42,7 +45,16 @@ public class MainWindow extends UiPart { private MenuItem helpMenuItem; @FXML - private StackPane personListPanelPlaceholder; + private StackPane clientListPanelPlaceholder; + + @FXML + private StackPane offerListPanelPlaceholder; + + @FXML + private StackPane listingListPanelPlaceholder; + + @FXML + private StackPane meetingListPanelPlaceholder; @FXML private StackPane resultDisplayPlaceholder; @@ -110,13 +122,22 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { * Fills up all the placeholders of this window. */ void fillInnerParts() { - personListPanel = new PersonListPanel(logic.getFilteredPersonList()); - personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + clientListPanel = new ClientListPanel(logic.getFilteredClientList()); + clientListPanelPlaceholder.getChildren().add(clientListPanel.getRoot()); + + offerListPanel = new OfferListPanel(logic.getFilteredOfferList()); + offerListPanelPlaceholder.getChildren().add(offerListPanel.getRoot()); + + listingListPanel = new ListingListPanel(logic.getFilteredListingList()); + listingListPanelPlaceholder.getChildren().add(listingListPanel.getRoot()); + + meetingListPanel = new MeetingListPanel(logic.getFilteredMeetingList()); + meetingListPanelPlaceholder.getChildren().add(meetingListPanel.getRoot()); resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); - StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); + StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getRealTimeFilePath()); statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); CommandBox commandBox = new CommandBox(this::executeCommand); @@ -163,14 +184,28 @@ private void handleExit() { primaryStage.hide(); } - public PersonListPanel getPersonListPanel() { - return personListPanel; + public ClientListPanel getClientListPanel() { + return clientListPanel; } + public OfferListPanel getOfferListPanel() { + return offerListPanel; + } + + public ListingListPanel getListingListPanel() { + return listingListPanel; + } + + public MeetingListPanel getMeetingListPanel() { + return meetingListPanel; + } + + + /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see seedu.realtime.logic.Logic#execute(String) */ private CommandResult executeCommand(String commandText) throws CommandException, ParseException { try { diff --git a/src/main/java/seedu/realtime/ui/MeetingCard.java b/src/main/java/seedu/realtime/ui/MeetingCard.java new file mode 100644 index 00000000000..24b43eab162 --- /dev/null +++ b/src/main/java/seedu/realtime/ui/MeetingCard.java @@ -0,0 +1,69 @@ +package seedu.realtime.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.realtime.model.meeting.Meeting; + + + +/** + * A UI component that displays information of a {@code meeting}. + */ +public class MeetingCard extends UiPart { + + private static final String FXML = "meetingListCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + public final Meeting meeting; + + @FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label dateTime; + @FXML + private Label client; + @FXML + private Label listingId; + + /** + * Creates a {@code meetingCode} with the given {@code meeting} and index to display. + */ + public MeetingCard(Meeting meeting, int displayedIndex) { + super(FXML); + this.meeting = meeting; + name.setText(meeting.getClient().fullName); + listingId.setText(meeting.getListing().value); + dateTime.setText(meeting.getdateTime().toString()); + + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof MeetingCard)) { + return false; + } + + // state check + MeetingCard card = (MeetingCard) other; + return client.getText().equals(card.client.getText()) + && meeting.equals(card.meeting); + } +} + diff --git a/src/main/java/seedu/realtime/ui/MeetingListPanel.java b/src/main/java/seedu/realtime/ui/MeetingListPanel.java new file mode 100644 index 00000000000..20e88819386 --- /dev/null +++ b/src/main/java/seedu/realtime/ui/MeetingListPanel.java @@ -0,0 +1,50 @@ +package seedu.realtime.ui; + +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import seedu.realtime.commons.core.LogsCenter; +import seedu.realtime.model.meeting.Meeting; + +/** + * Panel containing the list of meetings. + */ +public class MeetingListPanel extends UiPart { + private static final String FXML = "MeetingListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(MeetingListPanel.class); + + @FXML + private ListView meetingListView; + + /** + * Creates a {@code MeetingListPanel} with the given {@code ObservableList}. + */ + public MeetingListPanel(ObservableList meetingList) { + super(FXML); + meetingListView.setItems(meetingList); + meetingListView.setCellFactory(listView -> new MeetingListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Meeting} using a {@code MeetingCard}. + */ + class MeetingListViewCell extends ListCell { + @Override + protected void updateItem(Meeting meeting, boolean empty) { + super.updateItem(meeting, empty); + + if (empty || meeting == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new MeetingCard(meeting, getIndex() + 1).getRoot()); + } + } + } + +} + diff --git a/src/main/java/seedu/realtime/ui/OfferCard.java b/src/main/java/seedu/realtime/ui/OfferCard.java new file mode 100644 index 00000000000..ff521b44bd5 --- /dev/null +++ b/src/main/java/seedu/realtime/ui/OfferCard.java @@ -0,0 +1,67 @@ +package seedu.realtime.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.realtime.model.offer.Offer; + + +/** + * A UI component that displays information of a {@code Offer}. + */ +public class OfferCard extends UiPart { + + private static final String FXML = "OfferListCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + public final Offer offer; + + @FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label listing; + @FXML + private Label offerPrice; + + /** + * Creates a {@code OfferCode} with the given {@code Offer} and index to display. + */ + public OfferCard(Offer offer, int displayedIndex) { + super(FXML); + this.offer = offer; + id.setText(displayedIndex + ". "); + name.setText(offer.getClient().fullName); + listing.setText(offer.getListing().value); + offerPrice.setText("S$" + offer.getOfferPrice().value); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof OfferCard)) { + return false; + } + + // state check + OfferCard card = (OfferCard) other; + return id.getText().equals(card.id.getText()) + && offer.equals(card.offer); + } +} diff --git a/src/main/java/seedu/realtime/ui/OfferListPanel.java b/src/main/java/seedu/realtime/ui/OfferListPanel.java new file mode 100644 index 00000000000..4358ab7f8b8 --- /dev/null +++ b/src/main/java/seedu/realtime/ui/OfferListPanel.java @@ -0,0 +1,49 @@ +package seedu.realtime.ui; + +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import seedu.realtime.commons.core.LogsCenter; +import seedu.realtime.model.offer.Offer; + +/** + * Panel containing the list of offers. + */ +public class OfferListPanel extends UiPart { + private static final String FXML = "OfferListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(OfferListPanel.class); + + @FXML + private ListView offerListView; + + /** + * Creates a {@code OfferListPanel} with the given {@code ObservableList}. + */ + public OfferListPanel(ObservableList offerList) { + super(FXML); + offerListView.setItems(offerList); + offerListView.setCellFactory(listView -> new OfferListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Offer} using a {@code OfferCard}. + */ + class OfferListViewCell extends ListCell { + @Override + protected void updateItem(Offer offer, boolean empty) { + super.updateItem(offer, empty); + + if (empty || offer == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new OfferCard(offer, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/realtime/ui/PersonCard.java similarity index 88% rename from src/main/java/seedu/address/ui/PersonCard.java rename to src/main/java/seedu/realtime/ui/PersonCard.java index 7fc927bc5d9..2c896ec6434 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/seedu/realtime/ui/PersonCard.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.realtime.ui; import java.util.Comparator; @@ -7,7 +7,7 @@ import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; -import seedu.address.model.person.Person; +import seedu.realtime.model.person.Person; /** * An UI component that displays information of a {@code Person}. @@ -53,8 +53,8 @@ public PersonCard(Person person, int displayedIndex) { address.setText(person.getAddress().value); email.setText(person.getEmail().value); person.getTags().stream() - .sorted(Comparator.comparing(tag -> tag.tagName)) - .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + .sorted(Comparator.comparing(tag -> tag.tagName)) + .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); } @Override @@ -72,6 +72,6 @@ public boolean equals(Object other) { // state check PersonCard card = (PersonCard) other; return id.getText().equals(card.id.getText()) - && person.equals(card.person); + && person.equals(card.person); } } diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/realtime/ui/PersonListPanel.java similarity index 92% rename from src/main/java/seedu/address/ui/PersonListPanel.java rename to src/main/java/seedu/realtime/ui/PersonListPanel.java index f4c501a897b..1fb8b6f6723 100644 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ b/src/main/java/seedu/realtime/ui/PersonListPanel.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.realtime.ui; import java.util.logging.Logger; @@ -7,8 +7,8 @@ import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.layout.Region; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; +import seedu.realtime.commons.core.LogsCenter; +import seedu.realtime.model.person.Person; /** * Panel containing the list of persons. diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/realtime/ui/ResultDisplay.java similarity index 95% rename from src/main/java/seedu/address/ui/ResultDisplay.java rename to src/main/java/seedu/realtime/ui/ResultDisplay.java index 7d98e84eedf..7b09bc3ac21 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/seedu/realtime/ui/ResultDisplay.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.realtime.ui; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/realtime/ui/StatusBarFooter.java similarity index 96% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/seedu/realtime/ui/StatusBarFooter.java index b577f829423..00b7d0e8cc3 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/realtime/ui/StatusBarFooter.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.realtime.ui; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/seedu/realtime/ui/Ui.java similarity index 85% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/seedu/realtime/ui/Ui.java index 17aa0b494fe..67d3867dc40 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/seedu/realtime/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.realtime.ui; import javafx.stage.Stage; diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/realtime/ui/UiManager.java similarity index 93% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/seedu/realtime/ui/UiManager.java index fdf024138bc..ad2b8b25478 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/realtime/ui/UiManager.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.realtime.ui; import java.util.logging.Logger; @@ -7,10 +7,10 @@ import javafx.scene.control.Alert.AlertType; import javafx.scene.image.Image; import javafx.stage.Stage; -import seedu.address.MainApp; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; +import seedu.realtime.MainApp; +import seedu.realtime.commons.core.LogsCenter; +import seedu.realtime.commons.util.StringUtil; +import seedu.realtime.logic.Logic; /** * The manager of the UI component. diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/realtime/ui/UiPart.java similarity index 97% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/seedu/realtime/ui/UiPart.java index fc820e01a9c..264c9591ca3 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/realtime/ui/UiPart.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.realtime.ui; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.net.URL; import javafx.fxml.FXMLLoader; -import seedu.address.MainApp; +import seedu.realtime.MainApp; /** * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. diff --git a/src/main/resources/view/ClientListCard.fxml b/src/main/resources/view/ClientListCard.fxml new file mode 100644 index 00000000000..68505d7a800 --- /dev/null +++ b/src/main/resources/view/ClientListCard.fxml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/ClientListPanel.fxml b/src/main/resources/view/ClientListPanel.fxml new file mode 100644 index 00000000000..c9c25a875a6 --- /dev/null +++ b/src/main/resources/view/ClientListPanel.fxml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/main/resources/view/ListingListCard.fxml b/src/main/resources/view/ListingListCard.fxml new file mode 100644 index 00000000000..fd40c720dd4 --- /dev/null +++ b/src/main/resources/view/ListingListCard.fxml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/ListingListPanel.fxml b/src/main/resources/view/ListingListPanel.fxml new file mode 100644 index 00000000000..b3e14052d67 --- /dev/null +++ b/src/main/resources/view/ListingListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index a431648f6c0..03f44292d88 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -12,7 +12,7 @@ + title="Real-Time" minWidth="450" minHeight="600" onCloseRequest="#handleExit"> @@ -46,13 +46,37 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MeetingListCard.fxml b/src/main/resources/view/MeetingListCard.fxml new file mode 100644 index 00000000000..9380786d186 --- /dev/null +++ b/src/main/resources/view/MeetingListCard.fxml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MeetingListPanel.fxml b/src/main/resources/view/MeetingListPanel.fxml new file mode 100644 index 00000000000..a03df9f5104 --- /dev/null +++ b/src/main/resources/view/MeetingListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/view/OfferListCard.fxml b/src/main/resources/view/OfferListCard.fxml new file mode 100644 index 00000000000..062a1d49d1d --- /dev/null +++ b/src/main/resources/view/OfferListCard.fxml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/OfferListPanel.fxml b/src/main/resources/view/OfferListPanel.fxml new file mode 100644 index 00000000000..9b16e1e4326 --- /dev/null +++ b/src/main/resources/view/OfferListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml index f08ea32ad55..d1a7eb614f8 100644 --- a/src/main/resources/view/PersonListCard.fxml +++ b/src/main/resources/view/PersonListCard.fxml @@ -31,6 +31,7 @@