Developer Guide
- Acknowledgements
- Setting up, getting started
- Design
-
Implementation
- [Proposed] Undo/redo feature
- [Implemented] Add Client feature
- [Implemented] Delete Client feature
- [Implemented] Edit Client feature
- [Implemented] Add Offer feature
- [Implemented] Delete Offer feature
- [Implemented] Edit Offer feature
- [Implemented] Add Listing feature
- [Implemented] Delete Listing feature
- [Implemented] Edit Listing feature
- [Implemented] Add Meeting feature
- [Implemented] Delete Meeting feature
- [Implemented] Edit Meeting feature
- Documentation, logging, testing, configuration, dev-ops
- Appendix: Requirements
- Appendix: Instructions for manual testing
Acknowledgements
- {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries – include links to the original source as well}
Setting up, getting started
Refer to the guide Setting up and getting started.
Design
.puml files used to create diagrams in this document can be found in the diagrams folder. Refer to the PlantUML Tutorial at se-edu/guides to learn how to create and edit diagrams.
Architecture

The Architecture Diagram given above explains the high-level design of the App.
Given below is a quick overview of main components and how they interact with each other.
Main components of the architecture
Main has two classes called Main and MainApp. It is responsible for,
- At app launch: Initializes the components in the correct sequence, and connects them up with each other.
- At shut down: Shuts down the components and invokes cleanup methods where necessary.
Commons represents a collection of classes used by multiple other components.
The rest of the App consists of four components.
-
UI: The UI of the App. -
Logic: The command executor. -
Model: Holds the data of the App in memory. -
Storage: Reads data from, and writes data to, the hard disk.
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 delC 1.

Each of the four main components (also shown in the diagram above),
- defines its API in an
interfacewith the same name as the Component. - implements its functionality using a concrete
{Component Name}Managerclass (which follows the corresponding APIinterfacementioned 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.

The sections below give more details of each component.
UI component
The API of this component is specified in Ui.java

The UI consists of a MainWindow that is made up of parts e.g.CommandBox, ResultDisplay,
PersonListPanel, StatusBarFooter etc. All these, including the MainWindow, inherit from the abstract
UiPart class 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
is specified in MainWindow.fxml
The UI component,
- executes user commands using the
Logiccomponent. - listens for changes to
Modeldata so that the UI can be updated with the modified data. - keeps a reference to the
Logiccomponent, because theUIrelies on theLogicto execute commands. - depends on some classes in the
Modelcomponent, as it displaysClientobject residing in theModel.
Logic component
API : Logic.java
Here’s a (partial) class diagram of the Logic component:

How the Logic component works:
- When
Logicis called upon to execute a command, it uses theRealTimeParserclass to parse the user command. - This results in a
Commandobject (more precisely, an object of one of its subclasses e.g.,AddClientCommand) which is executed by theLogicManager. - The command can communicate with the
Modelwhen it is executed (e.g. to add a person). - The result of the command execution is encapsulated as a
CommandResultobject which is returned back fromLogic.
The Sequence Diagram below illustrates the interactions within the Logic component for the execute("delC 1") API call.

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:

How the parsing works:
- When called upon to parse a user command, the
RealTimeParserclass creates anXYZCommandParser(XYZis a placeholder for the specific command name e.g.,AddClientCommandParser) which uses the other classes shown above to parse the user command and create aXYZCommandobject (e.g.,AddClientCommand) which theRealTimeParserreturns back as aCommandobject. - All
XYZCommandParserclasses (e.g.,AddClientCommandParser,DeleteOfferCommandParser, …) inherit from theParserinterface so that they can be treated similarly where possible e.g, during testing.
Model component
API : Model.java

The Model component,
- stores the data in RealTime i.e., all
Client,Offer,ListingandMeetingobjects (which are contained in aUniqueClientList,UniqueOfferList,UniqueListingListandUniqueMeetingListobject respectively). - stores the currently ‘selected’
Client,Offer,ListingandMeetingobjects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiableObservableList<Client>,ObservableList<Offer>,ObservableList<Listing>andObservableList<Meeting>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
UserPrefobject that represents the user’s preferences. This is exposed to the outside as aReadOnlyUserPrefobjects. - does not depend on any of the other three components (as the
Modelrepresents data entities of the domain, they should make sense on their own without depending on other components)
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.
Storage component
API : Storage.java

The Storage component,
- can save both RealTime data and user preference data in json format, and read them back into corresponding objects.
- inherits from both
RealTimeStorageandUserPrefStorage, which means it can be treated as either one (if only the functionality of only one is needed). - depends on some classes in the
Modelcomponent (because theStoragecomponent’s job is to save/retrieve objects that belong to theModel)
Common classes
Classes used by multiple components are in the seedu.realtime.commons package.
Implementation
This section describes some noteworthy details on how certain features are implemented.
[Proposed] Undo/redo feature
Proposed Implementation
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:
-
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#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 VersionedRealTime will be initialized with the initial address book state, and the currentStatePointer pointing to that single 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.

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.

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#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.

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.
The following sequence diagram shows how the undo operation works:

UndoCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
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.
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#commitRealTime(), Model#undoRealTime() or Model#redoRealTime(). Thus, the realTimeStateList remains unchanged.

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.

The following activity diagram summarizes what happens when a user executes a new command:

Design considerations:
Aspect: How undo & redo executes:
-
Alternative 1 (current choice): Saves the entire address book.
- Pros: Easy to implement.
- Cons: May have performance issues in terms of memory usage.
-
Alternative 2: Individual command knows how to undo/redo by
itself.
- Pros: Will use less memory (e.g. for
delete, just save the person being deleted). - Cons: We must ensure that the implementation of each individual command are correct.
- Pros: Will use less memory (e.g. for
{more aspects and alternatives to be added}
[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.

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
ClientandListingobjects
-
Alternative 2: Allow Clients to have a Listing attribute instead
- Pros: Easier to reference between
ClientandListing - Cons: Large dependency between the two.
- Pros: Easier to reference between
[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.

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
ClientifIndexis mistyped
-
Alternative 2: Attribute specific
- Pros: User does not have to find for specific
Index - Cons: Many
Clientobjects might share same attribute such asAddressandPhone Number
- Pros: User does not have to find for specific
[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.

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
ClientandOfferobjects
-
Alternative 2: Allow Clients to have an Offer attribute instead
- Pros: Easier to reference between
ClientandOffer - Cons: Large dependency between the two.
- Pros: Easier to reference between
[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.

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
OfferifIndexis mistyped
-
Alternative 2: Attribute specific
- Pros: User does not have to find for specific
Index - Cons: Many
Offerobjects might share same attribute such asListingIdandPrice
- Pros: User does not have to find for specific
[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.

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:
Offerreferences theListingIdinstead ofListingobjects.
-
Alternative 2: Allow Offers to have a Listing attribute instead
- Pros: Easier to reference between
OfferandListing - Cons: Large dependency between the two.
- Pros: Easier to reference between
[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.

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
ListingifIndexis mistyped
-
Alternative 2: Attribute specific
- Pros: User does not have to find for specific
Index - Cons: Many
Listingobjects might share same attribute such asAsking PriceandOwner.
- Pros: User does not have to find for specific
[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.
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.

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:
Offerreferences theListingIdinstead ofListingobjects.
-
Alternative 2: Allow Offers to have a Listing attribute instead
- Pros: Easier to reference between
ListingandMeeting - Cons: Large dependency between the two.
- Pros: Easier to reference between
[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.

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
MeetingifIndexis mistyped
-
Alternative 2: Attribute specific
- Pros: User does not have to find for specific
Index - Cons: Many
Meetingobjects might share same attribute such asListing IdandName.
- Pros: User does not have to find for specific
[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.
Documentation, logging, testing, configuration, dev-ops
Appendix: Requirements
Product scope
Target user profile: Real Estate Agents
- has a need to manage a significant number of contacts
- prefer desktop apps over other types
- can type fast
- prefers typing to mouse interactions
- is reasonably comfortable using CLI apps
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 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 RealTime and the Actor is the user, unless specified otherwise)
Use case: UC01 - Reorder the list
MSS
- User requests to list persons
- RealTime shows a list of persons
- User requests to switch a specific person in the list with another
-
RealTime swaps their index
Use case ends.
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: UC02 - Delete a person
MSS
- User requests to list persons
- RealTime shows a list of persons
- User requests to delete a specific person in the list
-
RealTime deletes the person
Use case ends.
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
- User requests to add an offer.
-
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
- User requests to edit an offer.
-
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
- User requests to delete an offer.
-
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
- Should work on any mainstream OS as long as it has Java
11or above installed. - Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.
- A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
- Application should start in under a few seconds.
{More to be added}
Glossary
- Mainstream OS: Windows, Linux, Unix, OS-X
- 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
Appendix: Instructions for manual testing
Given below are instructions to test the app manually.
Launch and shutdown
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
-
-
Saving window preferences
-
Resize the window to an optimum size. Move the window to a different location. Close the window.
-
Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
-
-
{ more test cases … }
Adding a Listing
-
Adding a listings
- Prerequisites: The owner of the Listing must already be added as a Person.
- 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. - Other incorrect delete commands to try:
listing id/... ap/.. n.. a/..,listing,...
Expected: Error message to warn incorrect input format.
-
{ more test cases … }
Deleting a person
-
Deleting a person while all persons are being shown
-
Prerequisites: List all persons using the
listcommand. Multiple persons in the list. -
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. -
Test case:
delete 0
Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. -
Other incorrect delete commands to try:
delete,delete x,...(where x is larger than the list size)
Expected: Similar to previous.
-
-
{ more test cases … }
Saving data
-
Dealing with missing/corrupted data files
- {explain how to simulate a missing/corrupted file, and the expected behavior}
-
{ more test cases … }
Deleting a client
-
Deleting a person while all client are being shown
- Prerequisites: List all client using the
listcommand. Multiple client in the list. - 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. - Test case:
delete 0
Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. - Other incorrect delete commands to try:
delete,delete x,...(where x is larger than the list size)
Expected: Similar to previous.
- Prerequisites: List all client using the
Adding an Offer
- Adding an offer while all offers are being shown
- Prerequisites: The Offer being added must not exist in the current list.
-
Test case:
addO n/John Doe l/BEDOK_SOUTH o/700000Expected: An Offer with name John Doe, listing ID BEDOK_SOUTH and offer price of 700000 will be added to the list.
-
Test case:
addO n/Jackson Ang l/CALIFORNIA o/-10000Expected: No Offer will be added. Error message will appear in the response box.
- Other incorrect commands to try:
addO n/Bruce Wayne l/YISHUN o/123p123
Deleting an Offer
- Deleting an offer while all offers are being shown
- Prerequisites: At least one Offer has to exist in the list.
-
Test case:
delO 1Expected: First Offer is deleted from the list. Details of the deleted offer shown in the status message.
-
Test case:
delO 0Expected: No Offer is deleted. Error details shown in the status message.
- Other incorrect commands to try:
delO -1,delO a
Editing an Offer
- Editing an offer while all offers are being shown
- Prerequisites: At least one Offer must exist to be edited.
- Test case:
editO 1 n/John Doe
Expected: First Offer has the name changed to John Doe. - Test case:
editO 1 o/-1000
Expected: No Offer is edited. Error details shown in the status message.
Adding a Meeting
- Adding a meeting while all meeting are being shown
- Prerequisites: The Meeting being added must not exist in the current list.
-
Test case:
addM n/John Doe l/BEDOK_SOUTH d/2022-10-12 23:59Expected: A Meeting with name John Doe, listing ID BEDOK_SOUTH and dateTime 2022-10-12 23:59 will be added to the list.
-
Test case:
addM n/Jackson Ang l/CALIFORNIA d/23:59 2022-12-3Expected: No Meeting will be added. Error message will appear in the response box.
- Other incorrect commands to try:
addM n/Bruce Wayne l/YISHUN d/2022-100-100 27:20
Deleting a Meeting
- Deleting a meeting while all meetings are being shown
- Prerequisites: At least one Meeting has to exist in the list.
-
Test case:
delM 1Expected: First Meeting is deleted from the list. Details of the deleted meeting shown in the status message.
-
Test case:
delM 0Expected: No Meeting is deleted. Error details shown in the status message.
- Other incorrect commands to try:
delM -1,delM a
Editing a Meeting
- Editing a meeting while all meetings are being shown
- Prerequisites: At least one Meeting must exist to be edited.
- Test case:
editM 1 n/John Doe
Expected: First Meeting has the name changed to John Doe. - Test case:
editM 1 d/2023-30-20 12:00
Expected: No Meeting is edited. Error details shown in the status message.