Developer Guide
- Acknowledgements
- Setting up, getting started
- Design
- Implementation
- Documentation, logging, testing, configuration, dev-ops
- Planned Enhancements
- Appendix: Requirements
- Appendix: Instructions for manual testing
Acknowledgements
Libraries used:
- JavaFX
- Jackson
- JUnit5
- AddressBook Level 3 (Original Source)
Setting up, getting started
Refer to the guide Setting up and getting started.
Design
.puml files used to create diagrams in this document docs/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 (consisting of classes Main and MainApp) is in charge of the app launch and shut down.
- At app launch, it initializes the other components in the correct sequence, and connects them up with each other.
- At shut down, it shuts down the other components and invokes cleanup methods where necessary.
The bulk of the app’s work is done by the following 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.
Commons represents a collection of classes used by multiple other 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.

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, TagList 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 displaysPersonobject residing in theModel.
Logic Component
API : Logic.java
Here’s a (partial) class diagram of the Logic component:

The sequence diagram below illustrates the interactions within the Logic component, taking execute("delete 1") API call as an example.

Below is another sequence diagram, taking execute("tag 1 t/friends") API call as example.
![[Interactions Inside the Logic Component for the `tag 1 t/friends` Command]](/tp/images/TagSequenceDiagram.png)
DeleteCommandParser and TagCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram.
How the Logic component works:
- When
Logicis called upon to execute a command, it is passed to anAddressBookParserobject which in turn creates a parser that matches the command (e.g.,DeleteCommandParser,TagCommandParser) and uses it to parse the command. - This results in a
Commandobject (more precisely, an object of one of its subclasses e.g.,DeleteCommand,TagCommand) which is executed by theLogicManager. - The command can communicate with the
Modelwhen it is executed (e.g. to delete a person).
Note that although this is shown as a single step in the diagram above (for simplicity), in the code it can take several interactions (between the command object and theModel) to achieve. - The result of the command execution is encapsulated as a
CommandResultobject which is returned back fromLogic.
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
AddressBookParserclass creates anXYZCommandParser(XYZis a placeholder for the specific command name e.g.,AddCommandParser,TagCommandParser) which uses the other classes shown above to parse the user command and create aXYZCommandobject (e.g.,AddCommand,TagCommand) which theAddressBookParserreturns back as aCommandobject. - All
XYZCommandParserclasses (e.g.,AddCommandParser,DeleteCommandParser, …) inherit from theParserinterface so that they can be treated similarly where possible e.g, during testing.
Diving into specific implementation of the XYZCommand and XYZCommandParser classes, the user command undergoes extensive checks for validity.
Demonstrating with an example, the following activity diagrams summarise what happens when a user executes a newtag and tag command:

These extensive input checks in the Logic classes protect against potential malicious and invalid inputs that could undermine the usage of the code.
The class diagram below provides an overview of Command.

Commands that extends the UndoableCommand class have a concrete implementation of undo, which is called during the execution of an UndoCommand. During the execution of an UndoableCommand, the changes to the guest list are stored, and will be used for undo.
Model Component
API : Model.java

The Model component,
- stores the address book data i.e., all
Personobjects (which are contained in aUniquePersonListobject). - stores the current list of tags as an
ObservableList<Tag>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. This allowsAddessBookto only require oneTagobject per unique tag, instead of eachPersonneeding their ownTagobjects. - stores the currently ‘selected’
Personobjects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiableObservableList<Person>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 changes. - stores a
UserPrefobject that represents the user’s preferences. This is exposed to the outside as aReadOnlyUserPrefobjects. - stores a
Commandobject that represents the user’s last executed command.
Storage Component
API : Storage.java

The Storage component,
- can save both address book data and user preference data in JSON format, and read them back into corresponding objects.
- inherits from both
AddressBookStorageandUserPrefStorage, 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.address.commons package.
Implementation
This section describes some noteworthy details on how certain features are implemented.
[Proposed] Augment undo, add redo feature
Proposed Implementation
Currently, the undo feature facilitated by the class UndoCommand. It is implemented by having an abstract class UndoableCommand, which the command extends, proving a concrete implementation of UndoableCommand#undo(). and is implemented by having an undo method under each command, which implements an UndoableCommand interface. It does not utilise saved states of the address book.
The proposed augmented undo and new 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:
-
VersionedAddressBook#commit()— Saves the current address book state in its history. -
VersionedAddressBook#undo()— Restores the previous address book state from its history. -
VersionedAddressBook#redo()— Restores a previously undone address book state from its history.
These operations are exposed in the Model interface as Model#commitAddressBook(), Model#undoAddressBook() and Model#redoAddressBook() respectively.
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 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 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.

Model#commitAddressBook(), so the address book state will not be saved into the addressBookStateList.
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.

currentStatePointer is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The undo command uses Model#canUndoAddressBook() to check if this is the case. If so, it will return an error to the user rather
than attempting to perform the undo.
The following sequence diagram shows how an undo operation goes through the Logic component:

UndoCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Similarly, how an undo operation goes through the Model component is shown below:

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.
currentStatePointer is at index addressBookStateList.size() - 1, pointing to the latest address book state, then there are no undone AddressBook states to restore. The redo command uses Model#canRedoAddressBook() to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
Step 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 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.

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

Design considerations
Aspect: How undo & redo executes:
-
Alternative 1: Saves the entire address book.
- Pros: Easy to implement.
- Cons: May have performance issues in terms of memory usage.
-
Alternative 2 (current implementation): 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
Documentation, logging, testing, configuration, dev-ops
Planned Enhancements
In the current v1.6 iteration, we are aware of certain feature flaws and possible areas for improvements. Below are some of the upcoming works scheduled to be addressed.
Fix filter, add and undo commands
Currently, running any filter command that changes the list displayed, followed by an add command that successfully adds a guest changes the displayed list to the full guest list as intended.
However, the filters inside the filter panel are not removed, which can hinder users from performing filter functions.
This would also imply that the undo command does not operate as intended after the add command.
A workaround is to run the command list to reset the filters after adding a guest.
Standardise tag colours
The intention to differentiate tags by colours was to enable users to distinguish tags easily.
Currently, the colour of the tags changes randomly everytime the app is reopened.
We understand that experienced users may associate certain colours to certain tags. Thus, we plan to map each tag to a fixed colour, ensuring a standardised tag colour everytime the app is used.
Rigorous checks on user inputs
Currently, fields like Phone accepts only 8 digits, but does not check whether the starting digit is a 6, 8, or 9 (in compliance with Singapore’s phone numbers).
Other commands such as newtag 1 t/friends will successfully create a new tag called friends while ignoring the redundant input 1.
To provide clarity and ease to users, we plan to have more rigorous checks and display clearer messages to the user. For the latter example, “The input ‘1’ is ignored.”
Empty fields for Email and Phone
We understand that not both fields are always required. Users can contact guests either through phone or email.
Hence, we plan to only require one of the two fields to be inputted.
Appendix: Requirements
Product Scope
Target user profile:
Wedding planners who
- needs to manage a significant number (50-100) of wedding guests
- only have guests with Singapore phone numbers
- only needs to plan 1 wedding at a time
- prefer desktop applications over other types (e.g. Web-based)
- can type fast
- much prefers typing to cursor interactions
- is reasonably comfortable using Command Line Interface (CLI) applications
Value proposition: Reduce the time taken to add, tag and filter guests, allowing planners to focus more time on other wedding needs.
User stories
Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *
| Priority | As a … | I want to … | So that I can… |
|---|---|---|---|
* * * |
wedding planner new to the app | see usage instructions and examples | refer to instructions when I forget how to use the App |
* * * |
wedding planner | add a new person to the guest list | track the person using the guest list |
* * * |
wedding planner | delete a person | remove someone that has been uninvited |
* * * |
wedding planner | view the entire guest list | get an view all the guests in my guest list |
* * * |
wedding planner | find a guest by name | locate specific guests without having to go through the entire list |
* * * |
wedding planner | create customisable tags which can be applied to guests | have a personalised way to identify each group of guests |
* * * |
wedding planner managing distinct groups of guests | apply customisable tags to guests | categorise my guests easily |
* * * |
wedding planner managing distinct groups of guests | delete tags associated with guests | re-categorise my guests if needed |
* * * |
wedding planner | view the RSVP status of each guest | see if each guest is attending, not attending or have not RSVP’ed to the wedding |
* * |
wedding planner | filter guests according to their RSVP status | view guests that are attending, not attending or have not RSVP’ed to the wedding only |
* * |
wedding planner managing distinct groups of guests | filter guests according to their tags | view specific groups of guests without having to go through the entire list |
* * |
wedding planner managing many guests | apply or remove existing tags on multiple guests at once | quickly tag or untag large groups of guests in the guest list at once |
* * |
wedding planner managing many tags | apply or remove multiple existing tags at once on each guest | quickly tag or untag multiple tags onto each guest at once |
* * |
wedding planner managing many distinct groups of guests | create and delete multiple predefined tags at once | quickly create or delete multiple predefined tags at once |
* |
wedding planner with frequent guest category changes | rename predefined tags that were already created | change the description of tags without altering their association to the guests in the list |
* |
wedding planner | apply multiple filters to the list | view only a specific group of guests |
* |
wedding planner | have colour coded tags and fields | distinguish tags and different fields easily |
* |
wedding planner | see the list of tags already created | easily tag guests without checking if the tag has been already created |
* |
wedding planner handling multiple guests and tags at once | undo my last command | avoid retyping long commands or having to recall my mistakes |
Use Cases
(For all use cases below, the System is the AddressBook and the Actor is the user, unless specified otherwise)
Use Case 1 (UC1): Add Guest
MSS:
- User enters the command to add a guest.
- The system prompts the user for guest’s name, contact number and email address.
- User inputs their name, contact number and email address.
- The system validates the input.
-
The system saves the guest and display confirmation message.
Use case ends.
Extensions:
- 3a. User inputs invalid contact number.
- 3a1. System displays error message and prompts for corrected input.
- Use case resumes at step 3.
- 3b. User enters duplicate guest name.
- 3b1. System displays an error message, showing user already exists and prompts for corrected input.
- Use case resumes at step 3.
Use Case 2 (UC2): Delete Guest
Preconditions: List displayed is not empty.
MSS:
- User enters the command to delete a guest.
- The system prompts for guest.
- User enters the guest.
- The system verifies the input.
-
The system deletes the guest and display confirmation message.
Use case ends.
Extensions:
- 3a. User inputs invalid index.
- 3a1. System displays error message and prompts for corrected input.
- Use case resumes at step 3.
Use Case 3 (UC3): Find Guest By Name
MSS:
- User enters the command to find a guest.
- The system prompts for the guest’s name.
- User enters the name.
- The system searches for the guest.
-
The system displays a list of matching guest(s).
Use case ends.
Extensions:
- 5a. No guests match the keyword.
- 5a1. System displays a message indicating no guest found.
- Use case ends.
Use Case 4 (UC4): Set RSVP Status
Preconditions: List displayed is not empty.
MSS:
- User enters the command to set RSVP status.
- The system prompts for the guest and RSVP status.
- User enters the guest and RSVP status.
- The system verifies the input.
-
The system updates the RSVP status of the guest in the list and shows confirmation message.
Use case ends.
Extensions:
- 3a. User enters an invalid guest or status.
- 3a1. System displays error message and prompt for indexes.
- Use case resumes at step 3.
Use Case 5 (UC5): Create Tag(s)
MSS:
- User enters the command to create tag(s).
- The system prompts for the description of the tag(s).
- User enters the description of the tag(s).
- The system verifies the input.
-
The system updates the tag list to show the created tag and shows confirmation message.
Use case ends.
Extensions:
- 3a. User enters at least one invalid description (e.g. containing non-ASCII characters, or is too long)
- 3a1. System displays error message and the correct usage.
- Use case ends.
- 4a. Adding the new tag(s) would exceed the limit on the number of predefined tags allowed.
- 4a1. System displays a message indicating the limit will be reached.
- Use case ends.
- 4b. User enters at least one duplicate tag description (i.e. tag already exists in the tag list)
- 4b1. System continues to update the tag list for non-offending tag descriptions (if any).
- 4b2. System displays a message indicating the tag(s) that already exist(s).
- Use case ends.
Use Case 6 (UC6): Delete Tag(s)
Preconditions: Tag(s) is/are not on any guest in the entire list, or the command has to have the –force flag.
MSS:
- User enters the command to delete tag(s).
- The system prompts for the description of the tag(s).
- User enters the description of the tag(s).
- The system verifies the input.
- The system updates the tag list to remove the deleted tag(s) and shows confirmation message.
Use case ends.
Extensions
- 3a. User enters at least one invalid description (e.g. containing non-ASCII characters, or is too long).
- 3a1. System displays error message and the correct usage.
- Use case ends.
- 4a. User enters at least one tag description that does not match any of the predefined tags.
- 4a1. System continues to update the tag list for non-offending tag descriptions (if any).
- 4a2. System displays a message indicating the tag(s) that already exist(s).
- Use case ends.
Use Case 7 (UC7): Rename Tag
Preconditions: The new tag name is not in use yet, and the tag to be renamed exists in the tag list.
MSS
- User enters the command to rename tag.
- The system prompts for the description of the tag.
- User enters the description of the tag to be renamed, and the new description.
- The system verifies the input.
-
The system updates the tag description and shows confirmation message.
Use case ends.
Extensions:
- 3a. User enters an invalid description.
- 3a1. System displays error message and prompt tag description.
- Use case ends.
- 3b. User enters a description not matching any existing tags.
- 3b1. System displays a message indicating the tag does not exist.
- Use case ends.
Use Case 8 (UC8): Tag Guest
MSS:
- User enters the command to tag guest.
- The system prompts for the guest and the tag.
- User enters the guest and the tag.
- The system verifies the input.
-
The system updates the guest list to show the tag on the guest and shows confirmation message.
Use case ends.
Extensions:
- 3a. User enters an invalid tag.
- 3a1. System displays error message and prompt for the guest and corrected tag.
- Use case resumes at step 3.
- 3b. User enters invalid guest.
- 3b1. System displays error message and prompt for corrected guest and tag.
- Use case resumes at step 3.
- 5a. Tag not in tag list.
- 5a1. System displays a message indicating the tag does not exist in tag list.
- 5a2. User creates a new tag (UC5).
- Use case ends.
Use Case 9 (UC9): Untag Guest
MSS:
- User enters the command to untag guest.
- The system prompts for the guest and the tag.
- User enters the guest and tag.
- The system verifies the input.
-
The system updates the guest list to show that the tag on the guest are removed and shows confirmation message.
Use case ends.
Extensions:
- 3a. User enters invalid guest.
- 3a1. System displays error message and prompt for the corrected guest and tag.
- Use case resumes at step 3.
- 5a. Tag not on guest
- 5a1. System displays a message indicating the tag does not exist on the guest.
- Use case ends.
Use Case 10 (UC10): Filter by Tag or RSVP Status
MSS:
- User enters the command to filter guest list.
- The system prompts for the desired predicate.
- User enters the desired predicate.
- The system verifies the input.
-
The system shows the list of guests matching the predicate and shows confirmation message.
Use case ends.
Extensions:
- 3a. User enters an invalid RSVP status.
- 3a1. System displays error message indicating invalid RSVP status and prompts for the corrected predicate.
- Use case resumes at step 3.
- 3b. User enters an invalid tag.
- 3b1. System displays error message indicating the tag does not exist in tag list and prompts for the corrected predicate.
- Use case resumes at step 3.
Use Case 10 (UC10): Undo Last Command
Preconditions: Last command was successfully executed partially or fully.
MSS:
- User enters the command to undo the last action.
- The system verifies the input.
-
The system shows the list before last action was taken shows confirmation message.
Use case ends.
Extensions:
- 2a. No last action present.
- 2a1. System displays error message indicating there is no action to undo.
- Use case ends.
- 2b. Last action was an undo.
- 2b1. System displays error message indicating last action was an undo.
- Use case ends.
Non-Functional Requirements
- Should work on any mainstream OS as long as it has Java
17or above installed. - Should be able to hold up to 100 guests 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.
- Should only be able to support one wedding event at a time.
- Should load the list of guests in under 3 seconds, even when filtering the list.
- GUI should be intuitive and easy to learn for wedding planners to manage the guest list.
- Guest list should be saved and updated throughout uses.
Glossary
- Mainstream OS: Windows, Linux, Unix, MacOS
- GUI: Graphical User Interface allows users to interact with the address book codebase through visual representation
- CLI: Command Line Interface accepts text input to execute functions of the application
- JAR file: A package file that contains many class files, associated metadata and resources that build the application
- RSVP status: A field on each guest that indicates if the guest has responded, attending or not attending the wedding, which can be used for filtering purposes
- Customisable tags: Tags on guests containing text written by user, which can be used for filtering purposes
-
Tag list: A list of tags created using the
newtagcommand that can be used to tag guests with - Delete: Removes a guest and all details regarding the guest from the address book
- Add: Creates and saves a new guest into the address book
- Filter: Only show guests that contains the text input by the user
- Filter panel: Shows the current filter predicates applied to the list
- New Tag: Creates a new tag and adds it to the tag list
- Delete Tag: Removes a tag from the tag list and all instances of it on all guests in the address book
- Undo: Undoes the last command if it was successfully executed partially or fully
- Untag: Removes a tag from guest but does not remove it from the tag list or other guests not specified
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.
-
Open a command terminal in your computer.
-
cdinto the folder you put the jar file in (e.g If the jar file is stored in/Desktop/TestFile, then the command should becd /Desktop/TestFile). -
Use the
java -jar ab3myguest.jarcommand to run the application. 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.
-
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. -
Test case:
delete 0
Expected: No person is deleted. Error details shown in the status message. -
Other incorrect delete commands to try:
delete,delete x,...(where x is larger than the list size)
Expected: Similar to previous.
-
Creating a tag
-
Creating a tag
-
Prerequisites: Tag list contains less than 30 tags. Tag
familydoes not exist whilefriendsdoes. -
Test case:
newtag t/family
Expected: Tagfamilyis created and shown in the tag list. Details of the created tag shown in the status message. -
Test case:
newtag t/friends
Expected: No tag is created. Error details shown in the status message. -
Other incorrect
newtagcommands to try:newtag,newtag t/,newtag t/x(where x is a non-ASCII character)
Expected: Similar to previous.
-
Saving data
- Dealing with corrupted data files
- Run the JAR file and note the error message for the corrupted data file
- Open the project using an IDE (recommended to use IntelliJ IDEA)
- Navigate to
/datafolder - Open the JSON file inside, it should be named
ab3myguest.json - Edit the file to fix the corrupted data
- Dealing with missing data files
- Open the project using an IDE (recommended to use IntelliJ IDEA)
- Navigate to
/datafolder - Open the JSON file inside, it should be named
ab3myguest.json - If there is none, you can copy the default data file from our code space