A-Bash Book Developer Guide
- Setting up, getting started
- Introduction
- Design
- Implementation
- Documentation, logging, testing, configuration, dev-ops
- Appendix: Requirements
- Appendix: Instructions for manual testing
- Appendix: Effort
Setting up, getting started
Refer to the guide Setting up and getting started.
Introduction
A-Bash Book (ABB) is a Command Line Interface (CLI) based Contact Management System built to address the growing demands of businesses with an increasing business ecosystem. Especially in a climate where large amounts of business information are being stored in various decentralised places. ABB acts as a centralised platform for users to store contact details of business partners or colleagues.
This guide serves to aid A-Bash Book (ABB) developers by describing the Design, Architecture and Implementation behind each feature. Design Considerations can be found for certain features that would require extra attention when modifying or enhancing the feature. Future Enhancements for some features can also indicate the future state of the feature.
Various UML Diagrams are used to help describe the feature flow as well as the associations between objects and classes, to ensure this guide serves as a utilitarian guide.
Design
Architecture
The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.
.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.
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.
Each of the four components,
- defines its API in an
interface
with the same name as the Component. - exposes its functionality using a concrete
{Component Name}Manager
class (which implements the corresponding APIinterface
mentioned in the previous point.
For example, the Logic
component (see the class diagram given below) defines its API in the Logic.java
interface and exposes its functionality using the LogicManager.java
class which implements the Logic
interface.
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 sections below give more details of each component.
UI component
API :
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.
The UI
component uses 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
Logic
component. - Listens for changes to
Model
data so that the UI can be updated with the modified data.
Logic component
API :
Logic.java
-
Logic
uses theAddressBookParser
class to parse the user command. - This results in a
Command
object which is executed by theLogicManager
. - The command execution can affect the
Model
(e.g. adding a person). - The result of the command execution is encapsulated as a
CommandResult
object which is passed back to theUi
. - In addition, the
CommandResult
object can also instruct theUi
to perform certain actions, such as displaying help to the user.
Given below is the Sequence Diagram for interactions within the Logic
component for the execute("delete 1")
API call.
DeleteCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Model component
API : Model.java
The Model
,
- stores a
UserPref
object that represents the user’s preferences. - stores the address book data.
- exposes an unmodifiable
ObservableList<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 change. - stores the alias data.
- exposes an unmodifiable
ObservableList<CommandAlias>
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. - does not depend on any of the other three components.
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
object.Storage component
API : Storage.java
The Storage
component,
- can save
UserPref
objects in json format and read it back. - can save the address book data in json format and read it back.
- can save the aliases data in json format and read it back.
Common classes
Classes used by multiple components are in the seedu.addressbook.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 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 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#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 summarizes what happens when a user executes a new command:
Design consideration:
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}
Filtering PersonCard
A new DisplayFilterPredicate
is added in Model
.
Executing a FilterCommand
will trigger an update of the DisplayFilterPredicate
that is stored in
PersonListPanel
.
PersonListView
will need to be re-drawn since certain UI elements will have its visibility updated.
Re-drawing of the PersonListView
will re-create all the PersonCard
, allowing it to show or hide
UI elements based on the DisplayFilterPredicate
. This has to be done so that the dimension of the
hidden UI element will not be included during the layoutBounds calculations.
Design Considerations
Aspect: Implementation for predicate
-
Alternative 1 (current choice): Store the predicate in
Model
and expose it toUI (MainWindow)
viaLogic
- Pros: Separation of Concerns principle (SoC) is applied here to improve modularity.
The
Model
deals with the creation of the predicate while theUI (MainWindow)
retrieves the predicate fromModel
viaLogic
. - Cons: Will require new getter methods to the
Logic
andModel
class as long as
- Pros: Separation of Concerns principle (SoC) is applied here to improve modularity.
The
-
Alternative 2: Store the predicate as a global variable
- Pros: Simple to implement as a global variable is accessible by both the UI components and Model.
- Cons: Creates implicit links between code segments.
Autocomplete
There are 3 autocomplete features currently implemented.
1) Commands 2) Flags 3) Index
Commands
Implementation
EDIT
and DELETE
.
The current implementation consists of a Ui component called AutoCompleteListPanel
which is made up of AutocompleteCells
.
Each AutoCompleteCell
contains a command word. Command words are retrieved by calling LogicManager#getAutocompleteCommands()
and populated
by MainWindow#fillInnerParts()
.
The method CommandBox#setKeyUpCallback()
triggers AutocompleteListPanel#updateList()
on every release of a key. This updates the existing command panel
with the correct filtered commands.
- The
setKeyUpCallback()
usesaddEventFilter()
and detects when a key is released before triggering the function to handle it.
Event filters are added to the root by MainWindow
and the corresponding keys (`TAB`, `UP`, `DOWN`, ENTER)
are listened to.
- On
TAB
key release,AutocompleteListPanel#processTabKey()
will be called to handle the toggling between commands.
[Expected Behaviour]
Flags
Implementation
ADD
and EDIT
commands.
The current implementation consists of MainWindow
, CommandBox
and LogicManager
.
MainWindow
has an eventListener listening to the Tab key. LogicManager#getAvailableFlags()
is called to
attempt to get the available flags for the command.
LogicManager#getAvailableFlags()
returns null if command is incomplete. Instead, Tab key results in
Command Autocompletion
.
LogicManager#getAvailableFlags()
checks if the supplied command string is supported by Flag Autocompletion
.
LogicManager#filterExistingCommands()
is called to determine which flags are already in the command string
.
This is important as this check is required everytime Tab key is pressed.
When LogicManager#getAvailableFlags()
returns a populated list of strings, it continues to check the last tag in
command string
. If the flag has no content, that flag will be replaced by CommandBox#setTextvalue()
. If the flag has
content, the next available flag will be appended by CommandBox#setAndAppendFlag()
.
Index
EDIT
and DELETE
commands.
The current implementation consists of MainWindow
, PersonListPanel
, CommandBox
.
On Up/Down key press, the PersonListPanel#selectNext()
and PersonListPanel#selectPrev()
methods which will be called
to handle the toggling between contacts. Then the CommandBox#setAndAppendIndex()
method appends the index to the
existing command string
in CommandBox
.
[Expected Behaviour]
Future Enhancements
Autocomplete commands could include alias commands.
Design Considerations
Initially, autocomplete flags were untoggleable, and would just append to the command string
regardless of content.
This was originally implemented to allow users to, but to maintain consistency in the future. Consider keeping similar
autocomplete functionalities to be toggleable.
When adding new flags, LogicManager#getAutocompleteFlags()
should be updated with the new flags.
When adding new commands, LogicManager#getAutocompleteCommands()
should be updated with the new commands.
When adding autocomplete flag support for new commands, LogicManager#isAutocompleteFlag()
and
LogicManager#getAvailableFlags()
should add the new commands.
Remark
The current implementation is such that Remark
is added as an attribute of the Person
class. Remark
is intended
as a way to allow users to add any kind of comment about a specified contact, and therefore does not require any
validity check (an empty remark is also valid). Accordingly, Remark
is an optional field that can be specified when
adding/editing a contact. When editing a person’s Remark
and no value is provided, said person’s Remark
will be set
as empty.
Initially, an alternative implementation was considered: to introduce a new Remark
command which would be used to add
remarks to a contact. However the current implementation is used instead, in favour of consistency. Remark
is
after all an attribute of a Person
. No other such attribute has its own dedicated command. As such, Remark is
ultimately implemented as a field to the add
/edit
commands, which is consistent with all the other Person
attributes.
Find
The current implementation of the find
command only searches the name, email, remark and tag fields. Potential
improvements of the feature is to search all fields including phone number, address, company, and job title.
The implementation of general search is via a FieldsContainsKeywordsPredicate
predicate class. This predicate simply
propagate the keywords down to each individual predicate. Its Test function is basically the boolean or of all the
individual specific field predicate’s test function.
Below is the class diagram for the entire Find command
Fuzzy Find
The current implementation of find
command uses the Java port of Python’s
fuzzywuzzy algorithm. Current implementation matches using partial match of more
than 60% similarity.
Considerations
Key requirements for fuzzy search is the following
- 3 character name matches
- Fuzzy matching should support 3 character names with delta of 1 character
-
Eva
andIva
- 66% similarity -
Tim
andTom
- 66% similarity
-
- Fuzzy matching should support 3 character names with delta of 1 character
- Partial name matches
- Name matching should support partial matches where shortened nicknames are used
-
Ben
andBenjamin
-
Sam
andSamantha
-
Jon
andJonathan
-
- Name matching should support partial matches where shortened nicknames are used
- String matching should be one way
- Queries should match with data and not the other way round
-
Tom
should partially match withThomas
-
Thomas
should not partially match withTom
-
- Queries should match with data and not the other way round
Side Effects and Missed Matches
Due to the above considerations, partial matching is chosen for partial name matches and 60% threshold is chosen for 3 character names. However, side effects occur with these design choices.
- Middle of name matches
-
Sam
matches withBenjamin
with 66% partial similarity due tojam
inbenjamin
being 1 character delta fromsam
-
Additionally, there are also some missed out features
- Phonetically similar name matches
-
Shawn
doesnt match withSean
due to it below the 60% simiarity threshold
-
Potential changes
In the future, a combination of full word and partial matches can be used with weighted metrics to avoid middle of name matches. To avoid both issue, string fuzzy search may not be sufficient. Levenshtein distance is not able to account for phonetic differences in names and expected result when doing name searches.
Alias feature
Allows the user to create shortcut command (also known as command alias) to the actual command in
alias { add | delete | list } [ALIAS] [COMMAND]
format. The ALIAS
must be one word and cannot be an existing command,
while the COMMAND
must be a valid existing command.
Implementation
The AliasCommand
is split into three sub-commands AddAliasCommand
, DeleteAliasCommand
and ListAliasCommand
.
Supporting these classes are the AliasCommandParser
, AddAliasCommandParser
, DeleteAliasCommandParser
and
ListAliasCommandParser
which helps to parse user input into their respective alias sub-commands.
Step 1. The user input will be parsed through the AddressBookParser
which will then pass the user input to the
AliasCommandParser
when it checks that the user input is trying to execute an alias command.
Step 2. The user input will be parsed through the AliasCommandParser
which will then pass the user input to either
AddAliasCommandParser
, DeleteAliasCommandParser
or ListAliasCommandParser
after it checks which alias sub-command
the user input is trying to execute.
Step 3. The user input will be parsed through the AddAliasCommandParser
, DeleteAliasCommandParser
or
ListAliasCommandParser
and the respective Parser
will check if the user input is valid.
-
ALIAS
must be one word and not an existing command -
COMMAND
must be a valid existing command.
Step 4. Once the user input is successfully parsed, a AddAliasCommand
, DeleteAliasCommand
or ListAliasCommand
will be initialised and returned from their respective Parser
classes and executed subsequently.
Notes:
-
AddAliasCommand
will check if alias exists inmodel
before adding as duplicate alias is not allowed. -
DeleteAliasCommand
will check if alias exists inmodel
before deleting as alias must exist for it to be deleted.
Design Considerations
Aspect: Implementation for alias
command
-
Alternative 1 (current choice): Create a separate
AliasCommand
with sub-commands- Pros:
AliasCommand
will be independent fromAddCommand
. Easier to implement, test and debug. - Cons:
alias add
compared toadd alias
might be less intuitive for users.
- Pros:
-
Alternative 2: Implement in
AddCommand
withalias
as a sub-command ofadd
. e.g.add alias
.- Pros:
add alias
compared toalias add
might be more intuitive for users. - Cons: Will require huge changes to
AddCommand
.AddCommand
will require more testing and debugging.
- Pros:
Tag feature
Allows the user to create and delete one or more tag
from one or more person in
tag { add | delete | INDEX... } -t TAG...
format. Tags are case-insensitive, therefore Photoshop
and photoshop
are
treated as the same tag. There must be at one index and one tag for the command to be valid.
Implementation
The TagCommand
is split into two sub-commands AddTagCommand
and DeleteTagCommand
. Supporting these classes are the
TagCommandParser
, AddTagCommandParser
and DeleteTagCommandParser
which helps to parse user input into their
respective tag sub-commands.
Step 1. The user input will be parsed through the AddressBookParser
which will then pass the user input to the
TagCommandParser
when it checks that the user input is trying to execute a tag command.
Step 2. The user input will be parsed through the TagCommandParser
which will then pass the user input to either
AddTagCommandParser
or DeleteTagCommandParser
after it checks which tag sub-command the user input is trying to
execute.
Step 3. The user input will be parsed through the AddTagCommandParser
or DeleteTagCommandParser
and the respective
Parser
will check if the user input is valid.
- The index argument can only be
shown
,selected
orINDEX...
. -
INDEX...
must be valid positive integers. -
-t TAG...
must be valid tags which are alphanumeric.
Step 4. Once the user input is successfully parsed, a AddTagCommand
or DeleteTagCommand
will be initialised and
returned from their respective Parser
classes and executed subsequently.
Notes:
- Tags are stored in a
HashSet
inPerson
class. -
tag add
command can be executed successfully even if the persons already have the tags. The tags will just not be added by theHashSet
due to the property ofHashSet
. -
tag delete
command can be executed successfully even if the persons does not have the tags. The tags will just not be deleted by theHashSet
due to the property ofHashSet
.
Design Considerations
Aspect: Command result for tag
command
-
Alternative 1 (current choice): Command results will show how many persons the command has been executed on, but
not the actual number of persons which tags are added to or deleted from.
- Pros: Easy to implement, test and debug. The goal of the command will still be achieved even when the tags are not
added or deleted, e.g. a
delete tag
command deletingPhotoshop
tag from a person without the tag will still result in the person without the tag. - Cons: Command results does not reflect the exact number of persons tags are added to or deleted from when the command is executed. An additional note in the command result will be required to warn users of this behaviour.
- Pros: Easy to implement, test and debug. The goal of the command will still be achieved even when the tags are not
added or deleted, e.g. a
-
Alternative 2: Command results will show exactly the number of persons tags are added to or deleted from.
- Pros: Command results are clearer for users as it will reflect the exact number of persons tags are added to or deleted from when the command is executed.
- Cons: Will require many checks to show the exact number of persons modified, and it gets even more complicated when adding multiple tags to multiple persons or deleting multiple tags from multiple persons.
Selecting Persons
SelectCommand allows a user to select Person object(s) to apply actions on.
Overview of Implementation
Implementation
ModelManager
contains a list of Person
object which are selected by the user.
When SelectShowCommand
is called, a predicate will be applied onto the filteredPersonList
to show
only the selected list of Person
objects. The application of predicate follows the same method as
FindCommand
and ListCommand
.
Design Considerations
Aspect: Implementation for select
command
- Alternative 1 (current choice): Use a separate
List<Person>
to store the selected person objects.- Pros: Simple implementation with Separation of Concerns (SoC) principle applied as the
Model
stores the selected persons whileUI
retrieves a predicate to act upon (which will be updated everytime the user makes a new selection). - Cons: Model will have to ensure that after a person object is deleted, the object reference has to be deleted.
- Pros: Simple implementation with Separation of Concerns (SoC) principle applied as the
- Alternative 2: Use a global static
List<Person>
to store the selected person objects.- Pros: Simple to implement and access by both the
UI
andModel
components. - Cons: Create implicit links between code segments.
- Pros: Simple to implement and access by both the
Email Person(s)
The email command allows the user to open the operating system’s email client with the ‘to’ field filled up with the email of contacts.
Implementation
The current implementation consists of using the ‘mailto’ URI scheme to trigger the operating system’s email client.
Design Considerations
Aspect: Implementation for email
command
- Alternative 1 (current choice): Use ‘mailto’ URI scheme
- Pros: Simple implementation as the operating system will be in charge of resolving and opening the email client.
- Cons: Impossible to determine if the operating system has opened the email client successfully.
No other possible alternatives as it would be overly complex at this point in time.
Documentation, logging, testing, configuration, dev-ops
Appendix: Requirements
Product scope
Target user profile:
- Users who need to manage a significant number of professional contacts.
- Users who prefer desktop apps over other types.
- Users who can type fast.
- Users who prefers typing to mouse interactions.
- Users who is reasonably comfortable using CLI apps.
- Users who prefer a Bash-like experience.
Value proposition:
- Manage contacts faster than a typical mouse/GUI driven app, via keyboard commands.
- Enter commands at ease with convenience features such as auto-complete.
- Simultaneously manage several contacts with bulk actions such as select.
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 | find my friends via their email address | find my friends easily |
* * * |
user | autocomplete my commands | minimise the amount of typing for a command |
* * * |
user | set my own commands alias | type and execute commands faster |
* * * |
experienced Bash user | use the app with Bash-like commands and options format | work smoothly with a highly familiar and intuitive user experience |
* * |
user | find my friends without typing their exact full name | find my friends easily |
* * |
user | find my friends that have names with similar spelling easily | find my friends easily |
* * |
user | add remarks to my contacts | easily keep track of information/comments regarding a specific contact |
* * |
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 |
{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
MSS
- User requests to list persons
- AddressBook shows a list of persons
- User requests to delete a specific person in the list
-
AddressBook deletes the person
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid.
-
3a1. AddressBook shows an error message.
Use case resumes at step 2.
-
-
*a. At any time, User can press tab to autocomplete the field if possible
- *a1. User confirm suggestion by continuing his command
-
*a2. User rejects suggestion by deleting the suggestion
Use case ends.
Use case: Setting a command alias
MSS
- User frequently uses a command
- User sets a command alias for frequent command
-
AddressBook adds alias to existing command list
Use case ends.
Extensions
-
2a. The alias name or command name is empty
Use case ends.
-
2b. The command name is empty
Use case ends.
Use case: Edit remarks of an existing contact
MSS
- User wants to edit the remarks of a specific contact
- User provides a new remark to the specified contact
-
AddressBook updates the existing contact to have the specified remark
Use case ends.
Extensions
- 2a. The provided remark is empty
-
2a1. The remarks of the specified contact is emptied
Use case ends.
-
{More to be added}
Non-Functional Requirements
- Should work on any mainstream OS as long as it has Java
11
or above installed. - Should be able to hold up to 1000 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.
- Should work without any internet connection.
Glossary
- Mainstream OS: Windows, Linux, Unix, OS-X
- Private contact detail: A contact detail that is not meant to be shared with others
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.
-
Delete persons
-
Delete one person
-
Prerequisites: Must have at least one person in the list
-
Test case:
delete 1
Expected: Delete the first person in the list.
-
-
Delete multiple persons
-
Prerequisites: Must have at least 3 person in the list
-
Test case:
delete 1 2 3
Expected: Persons at index 1, 2 and 3 are deleted.
-
-
Delete shown person(s) in the list
-
Prerequisites: Must have at least 1 person in the list.
-
Test case:
delete shown
Expected: All person(s) in the visible person list are deleted.
-
-
Delete selected person(s)
-
Prerequisites: Must have at least 1 person selected
-
Test case:
delete selected
Expected: All selected person(s) will be deleted.
-
-
Invalid test cases
-
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.
-
Selecting persons
-
Select one person
- Test case:
select 1
Expected: First person is selected in the list.
- Test case:
-
Selecting persons multiple persons
-
Prerequisites: List must contain at least 3 persons.
-
Test case:
select 1 2 3
Expected: 1st, 2nd and 3rd person is marked as selected. -
Test case:
select shown
Expected: All person(s) in the current list will be selected
-
-
Selecting person(s) after find
-
Prerequisites:
find
command executed. -
Test case:
select shown
Expected: All person(s) that satisfies thefind
command will be selected
-
-
Clearing selection
-
Prerequisites: Must have at least 1 person selected
-
Test case:
select clear
Expected: All person(s) that are selected will be un-selected.
-
Email persons
Prerequisites: Must have an email client installed.
-
Email one person
-
Prerequisites: Must have at least 1 person in the list
-
Test case:
email 1
Expected: Email client is opened with the “to” field filled with the email of the person at index 1.
-
-
Email multiple persons
-
Prerequisites: Must have at least 3 person in the list
-
Test case:
email 1 2 3
Expected: Email client is opened with the “to” field filled with the email of the persons at index 1, 2 and 3.
-
-
Email shown person(s) in the list
-
Prerequisites: Must have at least 1 person in the list.
-
Test case:
email shown
Expected: Email client is opened with the “to” field filled with the email(s) of the person( s) in the person list.
-
-
Email selected person(s)
-
Prerequisites: Must have at least 1 person selected
-
Test case:
email selected
Expected: Email client is opened with the “to” field filled with the email(s) of the person( s)
-
Edit persons
-
Edit one person
-
Prerequisites: Must have at least one person in the list
-
Test case:
edit 1 -p 99998888
Expected: Phone number of person at index one is updated to “99998888”.
-
-
Edit multiple persons
-
Prerequisites: Must have at least 3 person in the list
-
Test case:
edit 1 2 3 -a 21 Lower Kent Ridge Rd
Expected: Address of persons at index 1, 2 and 3 is updated to “21 Lower Kent Ridge Rd”.
-
-
Edit shown person(s) in the list
-
Prerequisites: Must have at least 1 person in the list.
-
Test case:
edit shown -a 21 Lower Kent Ridge Rd
Expected: All person(s) address in the visible person list is updated to “21 Lower Kent Ridge Rd”.
-
-
Edit selected person(s)
-
Prerequisites: Must have at least 1 person selected
-
Test case:
edit selected -a 21 Lower Kent Ridge Rd
Expected: All selected person(s) address will be updated to “21 Lower Kent Ridge Rd”.
-
Filter fields
Prerequisites: Must have at least one person in the list to view the changes.
-
Filter to show only names
- Test case:
filter -n
Expected: Only names are shown.
- Test case:
-
Filter to show only names and addresses
-
Test case:
filter -n -a
Expected: Only names and addresses are shown. -
Test case:
filter -a
Expected: Only names and addresses are shown.
-
-
Remove filter
-
Prerequisites: Must have a filter applied (e.g.
filter -a -p
). -
Test case:
fitler
Expected: All fields are shown.
-
Editing Remark
- Edit a person’s remark to a non-empty remark.
- Prerequisites: List must contain at least 1 person.
- Test case:
edit 1 -r On leave
Expected: First person’s remark is changed to “On leave”.
- Edit a person’s remark without providing remark value.
- Prerequisites: List must contain at least 1 person.
- Test case:
edit 1 -r
Expected: First person’s remark is now empty.
Alias
-
Add alias
- Test case:
alias add ls list
Expected: Executingls
will behave exactly likelist
.
- Test case:
-
Delete alias
-
Prerequisites: Must have an alias named
ls
(e.g.alias add ls list
). -
Test case:
alias delete ls
Expected:ls
alias deleted.
-
-
List alias
- Test case:
alias list
Expected: All existing alias(es) are shown.
- Test case:
Tag
-
Add tag
-
Prerequisites: Must have at least 1 person in the list.
-
Test case:
tag add shown -t Photoshop
Expected: All shown person(s) will havePhotoshop
tag added. IfPhotoshop
tag exists before execution, nothing will change for that person. The command result will display the total number of persons the command have successfully executed on and not the total number of persons the tags are added to. -
Prerequisites: Must have at least selected 1 person.
-
Test case:
tag add selected -t Photoshop
Expected: All selected person(s) will havePhotoshop
tag added. IfPhotoshop
tag exists before execution, nothing will change for that person. The command result will display the total number of persons the command have successfully executed on and not the total number of persons the tags are added to.
-
-
Delete tag
- Test case:
tag delete shown -t Photoshop
Expected: All shown person(s) will havePhotoshop
tag removed. The command result will display the total number of persons the command have successfully executed on and not the total number of persons the tags are deleted from.
- Test case:
Autocomplete
-
Command Autocomplete
-
Test case:
e
tab
Expected:e
will be autocompleted to the next command in the command list panel ( e.g.edit
). -
Test case:
e
tab multiple times
Expected:e
will be autocompleted to the next command in the command list panel and will cycle through the options.
-
-
Flag Autocomplete
-
Test case:
add
tab
Expected: Pressing tab multiple times will cycle through all the flags available foradd
command. -
Test case:
edit 1
tab multiple times
Expected: Pressing tab multiple times will cycle through all the flags available foredit
command.
-
Find
-
Find All Fields
- Test case:
find coll
Expected: Names, emails, tags and remarks containingcoll
will be shown.
- Test case:
-
Find by Specific Fields
- Test case:
find -t coll
Expected: Tag(s) containingcoll
will be shown.
- Test case:
Saving data
-
Dealing with missing/corrupted data files
- {explain how to simulate a missing/corrupted file, and the expected behavior}
Appendix: Effort
Our team has put in significant effort in enhancing the usability of AB-3 into a more CLI centric address book. Much effort has been put in to replicate common controls and conventions seen in the popular terminal Bash. Our project has over 13k lines of code and over 500 automated tests. We detailed some challenges faced and achievements made while implementing A-Bash Book in the subsequent sections.
Challenges faced
-
Conversion to Bash style arguments
- We note that Bash uses the convention of
-
flags as well as enforcing a space after the arguments. - Much of the existing AB-3 command inputs are not following such conventions in Bash. To make such a change,
we had to change
ArgumentMultimap
class which is the core of the program. Much time was put into validating that the commands are still work properly after such a change as the modification affects every single command that parses arguments.
- We note that Bash uses the convention of
-
Addition of command aliasing
- In order for us to implement one of our quality of life changes available in Bash, namely command aliasing, a deep understanding of the command flow is required. The addition of command aliasing requires a loop back of commands back into the parser system after it has been converted from its aliased form to its complete form.
-
Fuzzy string matching for contact finding
- While the algorithm for fuzzy matching was ultimately included via a library import of the common FuzzyWuzzy matching library, much effort was spent in considering the way strings should be matched. Documented in the Fuzzy Find section, different considerations were made when deciding the level of tolerance, and how the way strings are matched.
-
UI additions
- An improvement was required to the UI to include the auto-complete command panel. Much of the UI code was also untestable within unit tests and require much manual testing. UI development also had to account for user limitations such as string length and screen size.
-
Attempt at WebView
- An attempt was made to include the WebView into our application for the user to have quick access to our user guide. However, after v1.3 was tested during PE-D, much issues surfaced from rendering issues when users use the webview to navigate outside the expected websites. It was eventually decided to scrap the WebView for a simple text based summary together with the existing link to our user guide.
-
Keyboard overrides
- To implement quality of life changes to be more keyboard centric, we implemented keyboard overrides to aid in selecting of contacts as well as to facilitate our autocomplete feature. Doing so required a big degree of understanding of JavaFX’s UI event system. This is especially the case when we had to override the tab key for auto-complete which already had a pre-existing function of navigating between UI elements.
-
Addition of special indices
- To support bulk commands, we added two special indices
shown
andselected
. These two keywords can replace anyINDEX
in any command. To do so, we had to modify pre-existing commands to add support for multiple modifications in a single command. New commands also have to account for these two special indices during development.
- To support bulk commands, we added two special indices
Achievements
- Streamlined and intuitive user experience for existing Bash users.
- Improved the feature set and usability while still managing to maintain code coverage.