Skip to content

Latest commit

 

History

History
1574 lines (1178 loc) · 67.8 KB

DeveloperGuide.adoc

File metadata and controls

1574 lines (1178 loc) · 67.8 KB

K.N.S. AddressBook v1.5.0 - Developer Guide

By: Team W11-B3      Since: Sep 2017      Licence: MIT

1. Introduction

Hello there! If you came here by choice, then chances are that you are interested in how we have further adapted from the original source code (AddressBook-Level4 project) provided by the SE-EDU initiative. This developer guide provides information that will help you understand what features we have implemented, and how we have done so.

2. Table Of Contents

3. Setting Up

3.1. Prerequisites

Before you get stared, do ensure that you have the following installed on your computer:

  • Java SE Development Kit (JDK) 1.8.0_60 or later You can download Java SE Development Kit here.

    ℹ️
    This application will not work with earlier versions of Java 8 (anything before 1.8.0_60).
  • IntelliJ Integrated Development Environment (IDE) You can download IntelliJ IDE here.

    ℹ️
    By default, IntelliJ has Gradle and JavaFx plugins installed; do not disable them! If you have disabled them, go to File > Settings > Plugins to re-enable them.

Once you have installed the above, you can continue on with the next step.

3.2. Setting up the project in your computer

  1. Fork this repository to your GitHub account, and clone the fork to your computer.

    ℹ️
    Don’t have a GitHub account? You can register for one here.
  2. Open IntelliJ (if you are not in the welcome screen, click File > Close Project to close the existing project dialog first)

  3. Set up the correct JDK version for Gradle

    1. Click Configure > Project Defaults > Project Structure

    2. Click New…​ and find the directory of the JDK

  4. Click Import Project

  5. Locate the build.gradle file and select it. Click OK

  6. Click Open as Project

  7. Click OK to accept the default settings

  8. Open a console and run the command

    1. gradlew processResources (on Windows)

    2. ./gradlew processResources (on Mac OS X/Linux)

      This will generate all resources required by the application and tests. You should see a BUILD SUCCESSFUL message.

3.3. Verifying the setup

  1. In Intellij, right click on the seedu.address.MainApp file and click Run MainApp().main. You should see the project building, and the application should start afterwards. Try a entering few commands in the application to ensure that the application is running.

  2. Run the tests to ensure they all pass.

3.4. Configurations to do before writing code

3.4.1. Configuring the coding style

This project follows oss-generic coding standards. IntelliJ’s default style is mostly compliant with ours but it uses a different import order from ours. To rectify this,

  1. Go to File > Settings…​ (Windows/Linux), or IntelliJ IDEA > Preferences…​ (Mac OS X)

  2. Select Editor > Code Style > Java

  3. Click on the Imports tab to set the order

    • For Class count to use import with '*' and Names count to use static import with '*': Set to 999 to prevent IntelliJ from contracting the import statements.

    • For Import Layout: The order is import static all other imports, import java.*, import javax.*, import org.*, import com.*, import all other imports. Add a <blank line> between each import

Optionally, you can follow the UsingCheckstyle.adoc document to configure Intellij to check style-compliance as you write code.

3.4.2. Setting up Continuous Integration (CI)

Set up Travis to perform CI for your fork. See UsingTravis.adoc to learn how to set it up.

Optionally, you can set up AppVeyor as a second CI (see UsingAppVeyor.adoc).

💡
Having both Travis and AppVeyor ensures your App works on both Unix-based platforms and Windows-based platforms (Travis is Unix-based and AppVeyor is Windows-based).

3.4.3. Before coding

Before you start coding, do read the Architecture section below so that you can get a clearer sense of the overall design of the application. This will help you understand how the application works, and how your changes can affect the entire system.

3.4.4. Updating documentation to match your fork

Lastly, if you plan to develop this as a separate product (i.e. instead of contributing to the CS2103AUG2017-W11-B3/main), you should replace the URL in the variable repoURL in DeveloperGuide.adoc and UserGuide.adoc with your fork’s URL, and make changes to the documentation where necessary.

4. Design

4.1. Introduction: Architecture

The Architecture Diagram below explains the high-level design of the application:

Architecture

Figure 4.1.1: Architecture Diagram

4.1.1. Main Component

Main has only one class called MainApp. It is responsible for,

  • At application 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 method where necessary.

4.1.2. Commons Component

Commons represents a collection of classes used by multiple other components. Two of those classes play important roles at the architecture level:

  • EventsCenter: This class (written using Google’s Event Bus library) is used by components to communicate with other components using events (i.e. a form of Event Driven design)

  • LogsCenter: Used by most classes to write log messages to the application’s log file.

4.1.3. The Other 4 Components

The rest of the App consists of four components:

  • UI: The user interface (UI) of the application.

  • 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 {Component Name}Manager class.

4.2. Events-Driven nature of the design

4.2.1. Components Interaction

The Sequence Diagram below shows how the components interact for the scenario where the user issues the command delete 1:

SDforDeletePerson

Figure 4.2.1.1: Component interactions for delete 1 command (part 1)

ℹ️
Model simply raises a AddressBookChangedEvent when the Address Book data is changed, instead of asking the Storage to save the updates to the hard disk.

The diagram below shows how the EventsCenter reacts to that event, which eventually results in the updates being saved to the hard disk and the status bar of the UI being updated to reflect the 'Last Updated' time.

SDforDeletePersonEventHandling

Figure 4.2.1.2: Component interactions for delete 1 command (part 2)

ℹ️
Note how the event is propagated through the EventsCenter to the Storage and UI without Model having to be coupled to either of them. This is an example of how this Event Driven approach helps us reduce direct coupling between components.

4.3. UI Component

UiClassDiagram

Figure 4.3.1: Structure of the UI Component

API: Ui.java

The UI consists of a MainWindow that is made up of parts e.g.CommandBox, ResultDisplay, PersonListPanel, StatusBarFooter, BrowserPanel etc. All of these parts, 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.

  • Binds itself to some data in the Model so that the UI can auto-update when data in the Model change.

  • Responds to events raised from various parts of the App and updates the UI accordingly.

4.4. Logic component

LogicClassDiagram

Figure 4.4.1: Structure of the Logic Component

LogicCommandClassDiagram

Figure 4.4.2: Structure of Commands in the Logic Component. This diagram shows finer details concerning XYZCommand and Command in Figure 4.4.1

API: Logic.java

  • Logic uses the AddressBookParser class to parse the user command.

  • This results in a Command object which is executed by the LogicManager.

  • The command execution can affect the Model (e.g. adding a person) and/or raise events.

  • The result of the command execution is encapsulated as a CommandResult object which is passed back to the UI.

Given below is the Sequence Diagram for interactions within the Logic component for the execute("delete 1") API call:

DeletePersonSdForLogic

Figure 4.4.3: Interactions Inside the Logic Component for the delete 1 Command

4.5. Model Component

ModelClassDiagram

Figure 4.5.1: Structure of the 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<ReadOnlyPerson> 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.

4.6. Storage Component

StorageClassDiagram

Figure 4.6.1: Structure of the Storage Component

The Storage component,

  • can save UserPref objects in json format and read it back.

  • can save the Address Book data in xml format and read it back.

4.7. Common Classes

Classes used by multiple components are in the seedu.addressbook.commons package.

💡
The .pptx files used to create diagrams in this document can be found in the diagrams folder. To update a diagram, just modify the objects inside .pptx file to your liking, and then Save as picture.

5. Implementation

This section describes some noteworthy details on how certain features are implemented.

5.1. Undo/Redo Mechanism

The undo/redo mechanism is facilitated by an UndoRedoStack, which resides inside LogicManager. It supports undoing and redoing of commands that modifies the state of the address book (e.g. add, edit). Such commands will inherit from UndoableCommand.

UndoRedoStack only deals with UndoableCommands. Commands that cannot be undone will inherit from Command instead. The following diagram shows the inheritance diagram for commands:

LogicCommandClassDiagram

Figure 5.1.1: Inheritance diagram for Undo/Redo command

UndoableCommand adds an extra layer between the abstract Command class and concrete commands that can be undone, such as the DeleteCommand. Note that extra tasks need to be done when executing a command in an undoable way, such as saving the state of the address book before execution. UndoableCommand contains the high-level algorithm for those extra tasks while the child classes implements the details of how to execute the specific command. Note that this technique of putting the high-level algorithm in the parent class and lower-level steps of the algorithm in child classes is also known as the template pattern.

Commands that are not undoable are implemented this way:

public class ListCommand extends Command {
    @Override
    public CommandResult execute() {
        // ... list logic ...
    }
}

With the extra layer, the commands that are undoable are implemented this way:

public abstract class UndoableCommand extends Command {
    @Override
    public CommandResult execute() {
        // ... undo logic ...

        executeUndoableCommand();
    }
}

public class DeleteCommand extends UndoableCommand {
    @Override
    public CommandResult executeUndoableCommand() {
        // ... delete logic ...
    }
}

Suppose that the user has just launched the application. The UndoRedoStack will be empty at the beginning.

The user executes a new UndoableCommand, delete 5, to delete the 5th person in the address book. The current state of the address book is saved before the delete 5 command executes. The delete 5 command will then be pushed onto the undoStack. The current state is saved together with the command as shown:

UndoRedoStartingStackDiagram

Figure 5.1.2

As the user continues to use the program, more commands are added into the undoStack. For example, the user may execute add n/David …​ to add a new person like so:

UndoRedoNewCommand1StackDiagram

Figure 5.1.3

ℹ️
If a command fails its execution, it will not be pushed to the UndoRedoStack at all.

The user now decides that adding the person was a mistake, and decides to undo that action using undo.

We will pop the most recent command out of the undoStack and push it back to the redoStack. We will restore the address book to the state before the add command executed as shown:

UndoRedoExecuteUndoStackDiagram

Figure 5.1.4

ℹ️
If the undoStack is empty, then there are no other commands left to be undone, and an Exception will be thrown when popping the undoStack.

5.1.1. Sequence Diagram

The following sequence diagram shows how the undo operation works:

UndoRedoSequenceDiagram

Figure 5.1.1.1

The redo does the exact opposite: pops from redoStack, push to undoStack, and restores the address book to the state after the command is executed.

ℹ️
If the redoStack is empty, then there are no other commands left to be redone, and an Exception will be thrown when popping the redoStack.

The user now decides to execute a new command, clear. As before, clear will be pushed into the undoStack. This time the redoStack is no longer empty. It will be purged as it no longer make sense to redo the add n/David command (this is the behavior that most modern desktop applications follow).

UndoRedoNewCommand2StackDiagram

Figure 5.1.1.2

Commands that are not undoable are not added into the undoStack. For example, list, which inherits from Command rather than UndoableCommand, will not be added after execution:

UndoRedoNewCommand3StackDiagram

Figure 5.1.1.3

The following activity diagram summarize what happens inside the UndoRedoStack when a user executes a new command:

UndoRedoActivityDiagram

Figure 5.1.1.4

5.1.2. Design Considerations

Aspect: Implementation of UndoableCommand.
Alternative 1 (current choice): Add a new abstract method executeUndoableCommand().
Pros: We will not lose any undone/redone functionality as it is now part of the default behaviour. Classes that deal with Command do not have to know that executeUndoableCommand() exist.
Cons: Hard for new developers to understand the template pattern.
Alternative 2: Just override execute().
Pros: Does not involve the template pattern, easier for new developers to understand.
Cons: Classes that inherit from UndoableCommand must remember to call super.execute(), or lose the ability to undo/redo.


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.


Aspect: Type of commands that can be undone/redone.
Alternative 1 (current choice): Only include commands that modifies the address book (add, clear, edit).
Pros: We only revert changes that are hard to change back (the view can easily be re-modified as no data is lost).
Cons: User might think that undo also applies when the list is modified (undoing filtering for example), only to realize that it does not do that, after executing undo.
Alternative 2: Include all commands.
Pros: Might be more intuitive for the user.
Cons: User have no way of skipping such commands if he or she just want to reset the state of the address book and not the view.
Additional Info: See our discussion here.


Aspect: Data structure to support the undo/redo commands.
Alternative 1 (current choice): Use separate stack for undo and redo.
Pros: Easy to understand for new Computer Science student undergraduates to understand, who are likely to be the new incoming developers of our project.
Cons: Logic is duplicated twice. For example, when a new command is executed, we must remember to update both HistoryManager and UndoRedoStack.
Alternative 2: Use HistoryManager for undo/redo
Pros: We do not need to maintain a separate stack, and just reuse what is already in the codebase.
Cons: Requires dealing with commands that have already been undone: We must remember to skip these commands. Violates Single Responsibility Principle and Separation of Concerns as HistoryManager now needs to do two different things.

5.2. Person Panel

The PersonPanel replaces the previous BrowserPanel, and is a crucial part of MainWindow. It is an event-driven component that displays contact information depending on the user’s actions

5.2.1. Java Implementation

By taking advantage of the java.util.logging package, PersonPanel is able to display all of the details of a contact (name, address, email, contact number, birthday, avatar, tags) selected in PersonCard. This implementation can be seen from the following 2 code snippets:

@Subscribe
private void handlePersonPanelSelectionChangedEvent(PersonPanelSelectionChangedEvent event) {
    loadBlankPersonPage();
    logger.info(LogsCenter.getEventHandlingLogMessage(event));
    selectedPersonCard = event.getNewSelection();
    selectedPerson = selectedPersonCard.person;
    isBlankPage = false;
    loadPersonPage();
}

Code Snippet 1 (handlePersonPanelSelectionChangedEvent()): Whenever a contact is selected, an event will be triggered. The method will respond to the event by obtaining a PersonCard variable and ReadOnlyPerson variable (which contains all the details of the contact), and pass it into loadPersonPage().

private void loadPersonPage() {
    name.setText(selectedPerson.getName().fullName);
    phone.setText("Phone: " + selectedPerson.getPhone().toString());
    address.setText("Address: " + selectedPerson.getAddress().toString());
    email.setText("Email: " + selectedPerson.getEmail().toString());
    birthday.setText("Birthday: " + selectedPerson.getBirthday().toString());
    avatar.setImage(SwingFXUtils.toFXImage(selectedPerson.getAvatar().getImage(), null));
    selectedPerson.getTags().forEach(tag -> {
        Label tagLabel = new Label(tag.tagName);
        tagLabel.setStyle("-fx-background-color: " + tag.tagColour);
        tags.getChildren().add(tagLabel);
    });
}

Code Snippet 2 (loadPersonPage()): The ReadOnlyPerson variable passed into loadPersonPage can then be used to extract the contact’s details for display; the UI will be updated accordingly to reflect these changes.

ℹ️
Upon opening the application, no contact details will be displayed since no contact has been selected yet.

To better illustrate the code snippets, let us look at the following sequence diagram when a user selects a contact found in PersonListPanel:

SequenceDiagramSelectToPersonPanel

Figure 5.2.1.1: Sequence Diagram for Selection

When a contact in PersonListPanel is selected via command or mouse click, this will result in the EventsCenter returning a ReadOnlyPerson of the selected contact for display in the PersonPanel.

5.2.2. Layout Implementation

The layout for PersonPanel is specified in PersonPanel.fxml. Visually, it can be broken down into 2 parts as shown:

PersonPanelLayout

Figure 5.2.2.1: Visual Breakdown of PersonPanel

Part 1 (primaryDetails): This is subdivided into parts A and B. Part A contains the avatar picture of the contact. Clicking on the avatar picture will cause a new AvatarWindow to be generated, which allows users to change the current avatar picture of the selected contact. More information about the implementation of Avatar can be found in the next section.

Part B contains the Name and Tags of the contact, which we found to be important in recognising a displayed contact quickly. Hence, they are in a larger font in order to stand out.

Part 2 (secondaryDetails): This displays the Address, Email, Contact Number and Birthday of the contact. As these details are less important than the Name and Tags, they are placed below and are in a smaller font. At the moment, this section appears simple but empty. We plan to implement more features for v2.0, such as a "Notes about Contact" and "Birthday Countdown".

5.2.3. Design Considerations

Aspect: Display of Contact’s Details.
Alternative 1 (current choice): Replace BrowserPanel with PersonPanel, which displays all of the contacts details. Remove all details but Name and Tags from PersonCard.
Pros: We can build upon PersonPanel and add more features to it, that the BrowserPanel could not achieve.
Cons: PersonPanel will not be able to display personal web pages (e.g. Contact’s Social Media page).
Alternative 2: Keep BrowserPanel and use HTML files to display contact details instead.
Pros: No need to modify existing code; instead just figure out a way to edit and display HTML files that show the contact’s details.
Cons: May take too long to implement since we are not familiar with how we can do so.


Aspect: Display of Tags In PersonPanel (and PersonCard).
Alternative 1 (current choice): Randomly colourise tags to make them distinct.
Pros: Quick to implement and makes it easier for user to differentiate between tags.
Cons: Tags are always changing colour for each new instance of the application; may seem confusing.
Alternative 2: Keep the previous blue colour for all tags.
Pros: Consistent and simple; no work is needed to be done.
Cons: Takes users a longer time to differentiate between tags.


Aspect: Addition of Icons for secondaryDetails.
Alternative 1 (current choice): Place icons on the left of each contact detail.
Pros: Quick to implement and makes it easier for user to differentiate between each contact detail. Icons can be easily taken from Google’s Material Design (or any other design website).
Cons: If we were to include more contact details (e.g. social media links) in the future, then we would have to keep adding more icons; relevant icons may not be found on Google’s Material Design.
Alternative 2: Use different colours for each contact detail.
Pros: Even more quick to implement since it only involves CSS changes.
Cons: Bad idea design-wise because it violates the Triadic Colour Scheme. It could make the application look less professional and unattractive.

5.3. Avatar

The Avatar class is a new contact detail that displays a display picture obtained via a valid Uniform Resource Locator (URL) or a local directory path in your computer. If no display picture is assigned to the contact, then creates a default placeholder image.

In order for the user to upload an Avatar for the contact, he/she can use the Add/Edit command or Graphical User Interface (GUI) upload (AvatarWindow) to do so.

5.3.1. Java Implementation: Avatar

The display and storage of Avatar can be seen in the following code snippets:

public Avatar() {
    AvatarUtil placeholder = new AvatarUtil();
    image = placeholder.getPlaceholderAvatar();
    value = DEFAULT_VALUE;
}

Code Snippet 1 (Avatar()): The default constructor for Avatar is used when no display picture is assigned to a newly added contact. The default placeholder Avatar is generated with the dependant class AvatarUtil.

public Avatar(String url) throws IllegalValueException {
    try {
        if (url.isEmpty() || DEFAULT_VALUE.equals(url)) {
            AvatarUtil placeholder = new AvatarUtil();
            image = placeholder.getPlaceholderAvatar();
            value = DEFAULT_VALUE;
        } else {
            File defaultAvatar = new File(url);

            if (isValidUrl(url)) {
                this.url = new URL(url);
            } else {
                this.url = defaultAvatar.toURI().toURL();
            }
            this.image = ImageIO.read(this.url);
			// Code Continued in Snippet 2B

Code Snippet 2A (Avatar(String url)): If a URL or local directory path string is provided, then the parameterized constructor will check if the provided string is valid or not. If the string is valid (i.e. non-empty and existing), then it will parse the URL and read the image given.

            if (!isSavedInData(url)) {
                String outputName = "/data/" + this.image.hashCode() + ".png";
                File outputImage = new File(System.getProperty("user.dir") + outputName);

                File parentDirectory = outputImage.getParentFile();
                if (!parentDirectory.exists()) {
                    parentDirectory.mkdirs();
                }

                ImageIO.write(this.image, "png", outputImage);
                this.url = outputImage.toURI().toURL();
            }
            this.value = this.url.toString();
        }
    } catch (Exception e) {
        throw new IllegalValueException(MESSAGE_AVATAR_CONSTRAINTS);
    }
}

Code Snippet 2B (Avatar(String url)): If the image given has not yet been stored in the data directory, it will generate a new file and save it.

To better illustrate the code snippets, here is a sequence diagram when a user changes a person’s avatar with a new image (that is not currently stored in the data folder):

SequenceDiagramEditContactAvatar

Figure 5.3.1.1: Sequence Diagram for Changing Avatar

ℹ️
Changing the Avatar of a contact will result in an immediate display change to the user.

5.3.2. Dependant Class: AvatarUtil

AvatarUtil generates a default placeholder image if the contact is not given a display picture. It relies on the java.awt library to generate a picture drawn by the methods found in Graphics2D and Color.

ℹ️
You can find the full implementation of AvatarUtil in the folder java/seedu/address/commons/util.

5.3.3. Dependant Class: AvatarWindow

AvatarWindow is a UI component and is triggered when the current avatar of a contact is clicked on as such:

UiPersonPanel

Figure 5.3.3.1: Avatar Window Pop-up

A window will appear for the user to load and save a picture from the computer. If a valid picture is loaded and saved, it will create an Edit command and raise a new event; this event is the same as typing an actual valid edit command, and can also be undone or redone as per normal.

ℹ️
You can find the full implementation of AvatarWindow in the folder java/seedu/address/ui.

5.3.4. Design Considerations

Aspect: Additional ways of changing Avatar.
Alternative 1: Only allow user to use the Add and Edit commands to change the Avatar of the contact.
Pros: No time is spent to make new ways of changing Avatar.
Cons: May be stifling for users who want to have a GUI option.
Alternative 2 (current choice): Create a GUI alternative for users to change the Avatar of the contact.
Pros: Makes the application more user friendly; easier for users to point and click instead of copying the picture’s directory path, which may take a longer time.
Cons: Some classes (e.g. PersonCard, PersonPanel, etc.) have to be modified in order to implement this option.


Aspect: Saving of pictures.
Alternative 1: Save the pictures within the .jar file.
Pros: Users will not be able to modify the pictures downloaded; lesser chances of error exceptions occuring due to missing files.
Cons: May be inconvenient for a user who would want to manipulate these pictures directly for any purpose.
Alternative 2 (current choice): Save the pictures outside of the .jar file, inside the data/ folder.
Pros: Users will be able to directly access downloaded pictures and manipulate them as they please.
Cons: If users are not careful (e.g. permanently.delete a picture by mistake), the contact’s avatar may get replaced by the default placeholder.


5.4. Import & Export Mechanism

The import and export mechanism is implemented using the XmlAddressBookStorage class. It allows for the manual saving and loading of the address book data, aside from the default initial loading and automatic saving, by using the export and import command.

Import only changes the address book’s data, and does not change the user preference. Notably, it does not change the default file which the application automatically saves to, which is addressbook.xml. Instead, it overwrites the current data with the data in the given filepath, provided it is a valid address book data.

The following shows the dependencies of both ImportCommand and ExportCommand:

ImportExportAssocDiagram

Figure 5.4.1: Import/Export Association Diagram

ℹ️
If the XmlAddressBookStorage fails to read or write to file, an Exception will be thrown. The same will happen if the file contains persons with illegal values. (e.g. empty name)

Suppose the user has just initialized the application, and the data folder is empty.

The user makes some changes to the data (e.g. using clear and add to clear away the sample and add their own contacts) which saves the data in the data folder as addressbook.xml automatically.

Then, without altering the data any further, the user decides to save a manual backup using a export backup.xml command. This will create a backup.xml file in the data folder, which at this point is equivalent to the addressbook.xml.

As the user continues to alter the data, the addressbook.xml file will keep changing, and will be different than the backup.xml file.
The user then decides that they want to return the addressbook to their backup version using a import data/backup.xml command.
This will overwrite the current data with the data in backup.xml, making it once again equivalent to addressbook.xml

5.4.1. Sequence Diagram

The sequence diagram for the command export a.xml is the following:

ImportExportSequenceDiagram

Figure 5.4.1.1: Sequence Diagram for Export Command

An import command would be similar, except that it creates a XmlAddressBookStorage object first, calls the readAddressBook method, and then calls the resetData method from the Model object.

5.4.2. Design Considerations

Aspect: How import works
Alternative 1(current choice): Overwrite the current address book with the data from the given file.
Pros: Easier implementation, data in the given file is preserved.
Cons: Loses the current address book data, which cannot be recovered if the address book is closed. (Can still be recovered with undo if it has not been closed since the import yet)
Alternative 2: Switch the file that the address book uses to the given file, and save all changes to that file
Pros: Allows for easier use of multiple saved files. The data in the current (before import) file is preserved.
Cons: Harder implementation, does not provide an easy way to import backups, since all changes are saved to the given file.


Aspect: Scope of data to export and import
Alternative 1(current choice): Exports and imports only the address book data, excluding pictures and user preferences.
Pros: Can use the current XmlAddressBookStorage class to read and write. Only read and write a single file.
Cons: Excluding pictures means user have to manually backup pictures, otherwise address book is incomplete (no avatar).
Alternative 2: Include pictures and/or user preferences
Pros: More complete data storage.
Cons: Higher probability of accidentally overwriting files if the user is not careful.

5.5. Sorting viewable list

The sort command and auto-sorting of the find command is facilitated by a SortedFilteredList inside the ModelManager class. This list is created on top of the FilteredList that is used to filter the contact list. A Comparator called defaultSortOrder was also created to as a comparator to reset the sorted list to its default order. ModelManager was also modified to support updating the sorted list only, and to always reset to default order whenever the filtered list is updated.

Commands that changes the viewable list without any sorting are implemented as:

public class ListCommand extends Command {
    @Override
    public CommandResult execure() {
        // ... some logic ...
        model.updateFilteredList(some predicate);
        // ... more logic ...
    }

Whereas commands with sorting (e.g. find) is implemented as:

public class FindCommand extends Command {
    @Override
    public CommandResult execute() {
        // ... some logic ...
        model.updateFilteredList(some predicate);
        model.updateSortedFilteredList(some comparator);
        // ... more logic ...
    }

5.5.1. Auto-Sort of Find Command

The find command, when matching by name, sorts its resulting list by the earliest position of a match with a given keyword.
This is implemented by creating a earliestIndexOf method in the StringUtil class, which takes in a sentence and a list of keywords, and returns the earliest starting index where a part of the sentence matches a keyword, or -1 if there are no match.

As an example, suppose the address book contains three people:

SortListUnsorted

Figure 5.5.1.1: 3 People Listed in the PersonList

When a find n/ i command is executed, it will match all three of them (since all their names contains i), and show them in this order:

SortListSorted

Figure 5.5.1.1: 3 People Listed in the New Order

as Michael has i in the 2nd position, Alicia has an i in the 3rd position, and Daniel has an i in the 4th position.

5.5.2. Design Considerations

Aspect: Implementation of sorting
Alternative 1 (current choice): Sort the viewable list only by creating a SortedFilteredList
Pros: Preserves the original order without additional changes, sort without worrying about changing the data.
Cons: Harder to implement a permanent sort functionality.
Alternative 2: Sort the data directly
Pros: Easier to make a permanent sort. Cons: Cannot go back to original order without additional changes. Harder to sort temporarily.


Aspect: Type of sorting in find
Alternative 1 (current choice): Sort the resulting list of find by the earliest matching index.
Pros: Make searching with short keywords more effective since the desired result is likely at the top.
Cons: Can be unintuitive, especially since it’s only done when finding by name.
Alternative 2: Show the resulting list in the default sort order.
Pros: More intuitive. No need for extra code.
Cons: Harder to find desired person when the result list is long.


Aspect: Mechanism to create comparators
Alternative 1 (current choice): sort and find uses separate method to create Comparator
Pros: More flexible since each command can do a different kind of sorting.
Cons: A lot of similar logic is copied to implement the methods individually.
Alternative 2: Create a class or method that returns Comparators.
Pros: Only implement once. Each sorting method now just need to call this method.
Cons: Take longer to code. Need to allow access to private variables in predicate classes.

5.6. Birthday

The Birthday class enables users to store their contact’s birthday details in K.N.S. Address Book. Birthday details should be inputted in dd/mm/yyyy form, and will be stored in the address book in the same form. If the user chooses not to enter their contact’s birthday details, the address book will automatically store the default value for empty birthday, which is -, and the value that will appear in the Person Panel for birthday field would be the default value.

5.6.1. Java Implementation

The Birthday class is implemented in similar way like other fields, such as address, phone, and email.

    public Birthday(String birthday) throws IllegalValueException {
        requireNonNull(birthday);
        String trimmedBirthday = birthday.trim();
        if (!isValidBirthday(trimmedBirthday)) {
            throw new IllegalValueException(MESSAGE_BIRTHDAY_CONSTRAINTS);
        }
        this.value = trimmedBirthday;
    }

5.6.2. Design Considerations

Aspect: Birthday Input Format
Alternative 1 (current choice): Using dd/mm/yyyy as the input format
Pros: It is easier for users to remember the format, as it is the format that is commonly used.
Cons: For some people who use mm/dd/yyyy format instead of dd/mm/yyyy format, they might need some time to adjust to K.N.S Address Book’s date format. K.N.S. Address Book assumes that the date entered is in dd/mm/yyyy format and will not send an error message when the number is valid. These users might enter 03/05/2017 which they intend to be 5 March, but the address book will interpret it as 3 May.
Alternative 2: Using ddmmyyyy as the input format
Pros: Faster to type as it consists of only numbers and no other characters.
Cons: The format is not easy to read and is similar to phone number format.

5.7. Partial Find

The partial matching of the Find command is implemented by creating a method in the StringUtil class with the help of the regionMatches method from the java String class. It replaces the method for matching in all predicate classes that is used by the command.

ℹ️
The Find command now only use partial matching and has lost the full matching functionality

Previously, the method used for matching was implemented as such:

public static boolean containsWordIgnoreCase(String sentence, String word) {
        // ...check and prepare arguments..
        for (String wordInSentence: wordsInPreppedSentence) {
            if (wordInSentence.equalsIgnoreCase(preppedWord)) {
                return true;
            }
        }
        return false;
    }

By using the equalsIgnoreCase method, the query word has to exactly match, ignoring case, the sentence word for the method to return true.

A slightly modified version of the previous method is created to allow for partial matching as such:

 public static boolean containsWordPartialIgnoreCase(String sentence, String word) {
        //..check and prepare arguments..
        return preppedSentence.contains(preppedWord);
    }

By using the contains method, the query word can now be a substring of the sentence word. It also shortens the method, as there is no more need to check through word-by-word.

Afterwards, the use of the previous method in the Predicate classes in model (e.g. NameContainsKeywordsPredicate) is replaced with the new method so that the Find command actually uses partial matching.

5.7.1. Design Considerations

Aspect: Exclusive use of partial matching.
Alternative 1 (current choice): Find command exclusively uses partial matching.
Pros: Simple implementation, doesn’t affect complexity from user’s perspective and easier for users to utilize Find command.
Cons: Users lose the ability to do full matching when it would be useful (e.g. a lot of people with similiar names).
Alternative 2: Give the option to toggle/use either partial matching or full matching
Pros: More flexible and powerful.
Cons: Requires more complicated syntax which can be confusing to new users, most use cases are already covered by partial matching.


Aspect: Type of partial matching
Alternative 1 (current choice): Matches can be from anywhere in the word
Pros: More intuitive way of searching, simpler to understand.
Cons: Search results become less relevant for short keywords. (Mitigated by sorting the results based on match position)
Alternative 2: Matches are required to be from the start of each word.
Pros: Restricts the scope of search which increases relevancy but still giving enough flexibility for users.
Cons: Can be unintuitive, less powerful.

5.8. Find by All Field

The find by all field feature is implemented by adding one argument, prefix of field that want to be searched, to the find command parameter. If the user does not specify the prefix, the address book will automatically search the query in the name field. The FindCommandParser will parse the input given by the user. The mechanism to find by each field is implemented in <field name>ContainsKeywordPredicate class (i.e. NameContainsKeywordPredicate, AddressContainsKeywordPredicate) inside Model component.

5.8.1. Java Implementation

The FindCommandParser is now able to parse the additional prefix argument, as shown in the code snippet below:

public FindCommand parse(String args) throws ParseException {
        // make sure that the argument is valid
        // store the prefix inside String 'toSearch'
        // store the search query inside array of string 'keyword'

        if (toSearch.equals(PREFIX_TAG.getPrefix())) {
            return new FindCommand(new TagListContainsKeywordsPredicate(Arrays.asList(keywords)));
        } else if (toSearch.equals(PREFIX_PHONE.getPrefix())) {
            return new FindCommand(new PhoneContainsKeywordsPredicate(Arrays.asList(keywords)));
        } else if (toSearch.equals(PREFIX_EMAIL.getPrefix())) {
            return new FindCommand(new EmailContainsKeywordsPredicate(Arrays.asList(keywords)));
        } else if (toSearch.equals(PREFIX_ADDRESS.getPrefix())) {
            return new FindCommand(new AddressContainsKeywordsPredicate(Arrays.asList(keywords)));
        } else if (toSearch.equals(PREFIX_BIRTHDAY.getPrefix())) {
            return new FindCommand(new BirthdayContainsKeywordsPredicate(Arrays.asList(keywords)));
        } else {
            return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(keywords)));
        }
    }

After FindCommandParser parse the arguments, it will call the <field name>ContainsKeywordsPredicate class for each respective field.

All contacts with partial matches will appear on the search result, implemented in the method below for phone field. The method is similar for other field.

public boolean test(ReadOnlyPerson person) {
        return keywords.stream().anyMatch(keyword -> StringUtil
                .containsWordPartialIgnoreCase(person.getPhone().value, keyword));
    }

5.8.2. Design Considerations

Aspect: Implementation of find by all field
Alternative 1 (current choice): Enables user to find by all field (name, phone, email, address, birthday, and tag).
Pros: Easier for user to find their contacts when the user does not remember their contact’s name, instead they remember the contacts' details (such as address or birthday). This feature is useful for a broader range of purpose, for example when the user wants to send a birthday wishes to their contacts, the user can easily find by using birthday field.
Cons: Need to type the prefix of the field that want to be searched.
Alternative 2: Find by name only.
Pros: Some people only remember their contact’s name, and find by all field feature might not be useful for them as they don’t remember their contact’s details.
Cons: User could not find their contact details when they do not remember their contact’s name.


Aspect: Find result upon executing find command.
Alternative 1 (current choice): All contacts with partial match with the find query will appear.
Pros: With less restrictive requirement, users can find a broad range of contacts when they are searching using a global keyword. For example, a user can find all their contacts who lived in "Clementi" when using this alternative.
Cons: More contacts will appear on the find result, some of them might not be the target contact that the user wants to find.
Alternative 2: Only contacts with exact match will appear.
Pros: Less contacts will appear on the find result, easier to find the exact person while searching for a single person.
Cons: It will be hard for a forgetful user to find their contacts as they may remember their contact details' partially. This alternative is also more cumbersome when applied to find by address, as user need to type the full address of their contact.

5.9. Add Tag and Delete Tag

Add tag and delete tag mechanism is facilitated by the addtag command and deletetag command, or their equivalent aliases at and dt, which is useful for adding and deleting tags in a person’s tag list. On previous versions before Add and Delete tag feature was introduced, users are able to change a person’s tag list by using edit command. Using edit command to add and delete tags is quite cumbersome as users need to retype all the current tags that they didn’t want to edit. addtag command and deletetag command enables user to add and delete tags using only a single command, without retyping all the current tags.

5.9.1. Java Implementation

As addtag and deletetag are commands, their implementations are a part of Logic component in the address book. The implementation of add tag and delete tag can be found in AddTagCommand and DeleteTagCommand. AddTagCommand and DeleteTagCommand inherits UndoableCommands, as they modify the state of the address book (adding and deleting a person’s tag in the address book). Therefore, users can undo/redo their previously entered addtag and deletetag command.

addtag command is implemented in this way:

public class AddTagCommand extends UndoableCommand {
    @Override
    public CommandResult executeUndoableCommand() throws CommandException {
        // ... list logic ...
    }
}

addtag command can be used by calling the method with an index and a string of tag name that will be added, shown by this code snippet:

public AddTagCommand(Index index, Set<Tag> addedTag) {
    requireNonNull(index);
    requireNonNull(addedTag);

    this.index = index;
    this.addedTag = addedTag;
}

Similar to addtag command, deletetag command is implemented in this way:

public class DeleteTagCommand extends UndoableCommand {
    @Override
    public CommandResult executeUndoableCommand() throws CommandException {
        // ... list logic ...
    }
}

deletetag command can be used by calling the method with an index and a string of tag name that will be added, just like addtag command, shown by the following code snippet:

public DeleteTagCommand(Index index, Set<Tag> deletedTag) {
    requireNonNull(index);
    requireNonNull(deletedTag);

    this.index = index;
    this.addedTag = deletedTag;
}

5.9.2. Design Considerations

Aspect: Implementation of AddTagCommand and DeleteTagCommand
Alternative 1 (current choice): Implementing a new command addtag and deletetag instead of using the existing Edit command.
Pros: Users can add and delete a single tag only by typing the new tag that they want to assign or remove from a contact. addtag and deletetag supports adding and deleting several tags at a single execution.
Cons: More commands to remember.
Alternative 2: Use existing Edit command to add or delete tags from a person in the address book.
Pros: Less commands to remember.
Cons: Users need to retype all existing tags they want to keep when they are using edit command. Users might mistype existing tags or not typing a complete set of existing tags while using edit command.

5.10. Theme Switcher

The Theme Switcher allows users to change the appearance of the application via the command theme or the drop-down selection in the MenuBar. There are 5 themes for users to choose from: Light, Dark, Red, Blue, and Green. Here is what the application looks like in each theme:

UiAllThemes

Figure 5.10.1: All 5 Themes of the Application

5.10.1. Java Implementation

The Theme Switcher mainly spans over the Logic and UI components.

@Override
public CommandResult execute() throws CommandException {
    if (!isValidTheme(this.theme)) {
        throw new CommandException(Messages.MESSAGE_INVALID_THEME);
    }
    if ((MainWindow.getCurrentTheme()).contains(this.theme)) {
        throw new CommandException("Theme is already set to " + this.theme + "!");
    }
    EventsCenter.getInstance().post(new ChangeThemeRequestEvent(this.theme));
    return new CommandResult(String.format(MESSAGE_THEME_SUCCESS, this.theme));
}

Code Snippet 1A (execute()): When the theme command is executed, it checks for two conditions:

  1. Theme Validity → It checks the input arguments for the 5 preset themes. If an invalid theme is given, then a CommandException is thrown to inform the user that their input theme does not exist.

  2. Current Theme Displayed → In order to prevent the user from setting the current theme to the same exact theme, it checks whether the valid argument is currently the displayed theme. If it is, then a CommandException is thrown to inform the user of their error.

If the two conditions are fulfilled, then the theme command will generate a new ChangeThemeRequestEvent for MainWindow to accept, and return a successful CommandResult.

@Subscribe
public void handleChangeThemeRequestEvent(ChangeThemeRequestEvent event) throws CommandException, ParseException {
    logger.info(LogsCenter.getEventHandlingLogMessage(event));
    mainWindow.getStylesheets().remove(currentTheme);
    prefs.setAddressBookTheme(event.theme + "Theme.css");
    currentTheme = "view/" + prefs.getAddressBookTheme();
    mainWindow.getStylesheets().add(currentTheme);
}

Code Snippet 1B (handleChangeThemeRequestEvent(ChangeThemeRequestEvent event)): The event generated by the theme command is received in MainWindow. It takes removes the CSS for the current theme, and replaces it with the input theme’s CSS. The change in appearance is reflected immediately.

To better illustrate code snippets 1A and 1B, the following sequence diagram demonstrates a user entering a valid theme command input:

SequenceDiagramValidThemeCommand

Figure 5.10.1.1: Sequence Diagram for Valid Theme Command

ℹ️
MainWindow does not need to check for the theme validity or whether the same theme is being set, because the theme command has already accounted for these conditions.
@FXML
private void setToVALIDTheme() {
    if (checkSameTheme("VALID")) {
        return;
    }
    mainWindow.getStylesheets().remove(currentTheme);
    prefs.setAddressBookTheme("VALIDTheme.css");
    currentTheme = "view/" + prefs.getAddressBookTheme();
    mainWindow.getStylesheets().add(currentTheme);
    EventsCenter.getInstance().post(new ChangeThemeRequestEvent("VALID"));
    raise(new NewResultAvailableEvent("Theme updated to: VALID", false));
}

Code Snippet 2 (setToVALIDTheme()): Where "VALID" is Light, Dark, Red, Blue or Green. If the user uses the MenuBar to change the current theme, then it will call the appropriate method to change the current theme to it. Also, the method ensures that if the user selects the theme that is already being displayed, it will raise a NewResultAvailableEvent to inform the user of their error.

ℹ️
MainWindow needs to check whether the same theme is being set since no theme command is being executed.

5.10.2. Design Considerations

Aspect: Providing a GUI option for switching themes
Alternative 1 (current choice): Provide a GUI option via the MenuBar.
Pros: Simple to implement and users can change themes seamlessly.
Cons: Users will not know what the colour scheme of the theme looks like unless they change to it.
Alternative 2: Provide a GUI option via coloured buttons.
Pros: Users will be able to see the colour scheme of the theme before changing to it.
Cons: Users may get confused as to what these buttons are for.

5.11. Logging

We are using java.util.logging package for logging. The LogsCenter class is used to manage the logging levels and logging destinations.

  • The logging level can be controlled using the logLevel setting in the configuration file (See Configuration).

  • The Logger for a class can be obtained using LogsCenter.getLogger(Class) which will log messages according to the specified logging level.

  • Currently log messages are output through: Console and to a .log file.

Logging Levels

  • SEVERE: Critical problem detected which may possibly cause the termination of the application.

  • WARNING: Can continue, but with caution.

  • INFO: Information showing the noteworthy actions by the application.

  • FINE: Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size.

5.12. Configuration

Certain properties of the application can be controlled (e.g App name, logging level) through the configuration file (default: config.json).

6. Documentation

We use asciidoc for writing documentation.

ℹ️
We chose asciidoc over Markdown because asciidoc, although a bit more complex than Markdown, provides more flexibility in formatting.

6.1. Editing Documentation

See UsingGradle.adoc to learn how to render .adoc files locally to preview the end result of your edits. Alternatively, you can download the AsciiDoc plugin for IntelliJ, which allows you to preview the changes you have made to your .adoc files in real-time.

6.2. Publishing Documentation

See UsingTravis.adoc to learn how to deploy GitHub Pages using Travis.

6.3. Converting Documentation to PDF format

We use Google Chrome for converting documentation to PDF format, as Chrome’s PDF engine preserves hyperlinks used in webpages.

Here are the steps to convert the project documentation files to PDF format.

  1. Follow the instructions in UsingGradle.adoc to convert the AsciiDoc files in the docs/ directory to HTML format.

  2. Go to your generated HTML files in the build/docs folder, right click on them and select Open withGoogle Chrome.

  3. Within Chrome, click on the Print option in Chrome’s menu.

  4. Set the destination to Save as PDF, then click Save to save a copy of the file in PDF format. For best results, use the settings indicated in the screenshot below.

chrome save as pdf

Figure 6.3.1: Saving documentation as PDF files in Chrome

7. Testing

7.1. Running Tests

There are three ways to run tests.

💡
The most reliable way to run tests is the 3rd one. The first two methods might fail some GUI tests due to platform/resolution-specific idiosyncrasies.

Method 1: Using IntelliJ JUnit test runner

  • To run all tests, right-click on the src/test/java folder and choose Run 'All Tests'

  • To run a subset of tests, you can right-click on a test package, test class, or a test and choose Run 'ABC'

Method 2: Using Gradle

  • Open a console and run the command gradlew clean allTests (Mac/Linux: ./gradlew clean allTests)

ℹ️
See UsingGradle.adoc for more info on how to run tests using Gradle.

Method 3: Using Gradle (headless)

Thanks to the TestFX library we use, our GUI tests can be run in the headless mode. In the headless mode, GUI tests do not show up on the screen. That means the developer can do other things on the Computer while the tests are running.

To run tests in headless mode, open a console and run the command gradlew clean headless allTests (Mac/Linux: ./gradlew clean headless allTests)

7.2. Types of tests

We have two types of tests:

  1. GUI Tests - These are tests involving the GUI. They include,

    1. System Tests that test the entire App by simulating user actions on the GUI. These are in the systemtests package.

    2. Unit tests that test the individual components. These are in seedu.address.ui package.

  2. Non-GUI Tests - These are tests not involving the GUI. They include,

    1. Unit tests targeting the lowest level methods/classes.
      e.g. seedu.address.commons.StringUtilTest

    2. Integration tests that are checking the integration of multiple code units (those code units are assumed to be working).
      e.g. seedu.address.storage.StorageManagerTest

    3. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
      e.g. seedu.address.logic.LogicManagerTest

7.3. Troubleshooting Testing

Problem: HelpWindowTest fails with a NullPointerException.

  • Reason: One of its dependencies, UserGuide.html in src/main/resources/docs is missing.

  • Solution: Execute Gradle task processResources.

8. Dev Ops

8.1. Build Automation

See UsingGradle.adoc to learn how to use Gradle for build automation.

8.2. Continuous Integration

We use Travis CI and AppVeyor to perform Continuous Integration on our projects. See UsingTravis.adoc and UsingAppVeyor.adoc for more details.

8.3. Making a Release

Here are the steps to create a new release.

  1. Update the version number in MainApp.java.

  2. Generate a JAR file using Gradle.

  3. Tag the repo with the version number. e.g. v0.1

  4. Create a new release using GitHub and upload the JAR file you created.

8.4. Managing Dependencies

A project often depends on third-party libraries. For example, Address Book depends on the Jackson library for XML parsing. Managing these dependencies can be automated using Gradle. For example, Gradle can download the dependencies automatically, which is better than these alternatives.
a. Include those libraries in the repo (this bloats the repo size)
b. Require developers to download those libraries manually (this creates extra work for developers)

Appendix A: 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

* * *

normal user

add a new person

fill my address book with contacts

* * *

normal user

edit contact details

keep entries updated

* * *

normal user

delete a person

remove entries that I no longer need

* * *

normal user

find a person by name

locate details of persons without having to go through the entire list

* * *

normal user

find a person based on tags

find my contacts with the same tag group easily

* * *

normal user

undo command

correct my mistake

* * *

normal user

redo command

correct my undo easily

* * *

normal user

store multiple details for contact (e.g multiple phones)

store details thoroughly

* * *

normal user

set favorite contacts

look them up more quickly

* * *

normal user

find contact based on phone number

know who calls me when unknown number calls/text message me

* * *

student/worker

assign groups/tags

categorise and sort my contacts as needed

* * *

forgetful user

find with partial matches

search for contacts that I only partially remember the name of

* * *

user with multiple address book

import contact details

copy contacts to another address book easily

* * *

careful/paranoid user

back up my contacts' details

Restore the contacts in case the original storage file is deleted or corrupted

* * *

careful/paranoid user

export my contacts' details

restore them in another computer if needed

* *

user

hide private contact details by default

minimize chance of someone else seeing them by accident

* *

expert user

use the product seamlessly

start using the product immediately and easily

* *

expert user

set aliases for commands

easily remember the command

* *

elderly person

adjust the product’s font-size

view my contacts' details with ease

* *

shared computer user

enable a PIN/password

no one else can view my contacts

* *

forgetful user

see my last command

know the last change that I made

* *

user

see recently accessed contact

easily find the person’s details without searching

* *

user

input case-insensitive command

input command easily

* *

user

add a picture to contact

remember my contact better

* *

user

get suggestion for command correction

input correct command easily after I input wrongly

* *

user

save my contact’s birthday

remember my contact’s birthday

* *

user

get reminded of a contact’s birthday

wish him/her happy birthday

* *

user multiple devices

set multiple instances of app to be in sync

use address book across multiple devices seamlessly

* *

picky user

change the colour appearance

view my address book in my favourite colour scheme

*

user with many persons in the address book

sort persons by name

locate a person easily

*

picky user

change font type

make my address book as fancy/simple as I like

*

picky user

add font colour to my contact’s name

make the address book colourful

*

picky user

have an address book with sound effects

my address book is "cool"

Appendix B: Use Cases

For all use cases below, the System is the AddressBook and the Actor is the user, unless specified otherwise.

Use case: Delete person

MSS

  1. User requests to list persons

  2. AddressBook shows a list of persons

  3. User requests to delete a specific person in the list

  4. AddressBook deletes the person

    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.

Use case: Edit person’s details

MSS

  1. User requests to list persons

  2. AddressBook shows a list of persons

  3. User requests to edit a specific person in the list’s details to something else

  4. AddressBook edits the details and shows the updated 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.

  • 3b. The given detail field or value is invalid

    • 3b1. AddressBook shows an error message.

      Use case resumes at step 2.

Use case: Assign tag to a person

MSS

  1. User requests to list persons

  2. AddressBook shows a list of persons

  3. User requests to to add a given tag to a specific person

  4. AddressBook adds the tag to person’s details

    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.

  • 3b. The given tag is invalid

    • 3b1. AddressBook shows an error message.

      Use case resumes at step 2.

{More to be added}

Appendix C: Non Functional Requirements

  1. Should work on any mainstream OS as long as it has Java 1.8.0_60 or higher installed.

  2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.

  3. Should be available to use as long as the computer has sufficient power.

  4. Should be available to use with or without internet access.

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

  6. A user with little to no experience with a command line interface should be able to have easy access to the user guide, and be able to familiarise himself/herself with the commands.

  7. A user should only be able to use the supported commands; unsupported commands should be handled gracefully.

  8. A user is able to back up and restore all contacts in the event of the application breaking.

  9. The response to any user action should become visible within 5 seconds at maximum contacts capacity.

  10. The application should be offered as a free software available for download.

  11. The functionality of the application should be able to be increased or extended even after deployment.

  12. The source code should be open source.

Appendix D: Glossary

  • Avatar: A picture representing a particular person in the address book.

  • Command Line Interface (CLI): It is an interface which users respond to a visual prompt by typing in a command on a specified line, receive a response back from the system, and then enter another command. This goes on back and forth.

  • Graphical User Interface (GUI): It is a graphical (rather than purely textual) user interface to a computer.

  • Java: It is a general-purpose computer programming language that is used in many products today. To learn more, click here.

  • Mainstream OS: Examples include Windows, Linux, Unix, Mac OS X.

  • Open Source: Software for which the original source code is made freely available and may be redistributed and modified.

  • Private Contact Detail: A contact detail that is not meant to be shared with others.

  • Sequence Diagram: A sequence diagram is an interaction diagram that shows how objects operate with one another and in what order.

  • Triadic Colour Scheme: A colour scheme that uses only three colors that are equally spaced around the color wheel.