Project: iContacts

iContacts is an address book application targeted to university students for managing contacts. iContacts is a Command Line Interface (CLI) application where most of the interactions are through typing commands on a keyboard. It has a Graphical User Interface (GUI) created with JavaFX, and is written in Java with about 12 kLoC. iContacts allow users to manage reminders, keep track of upcoming contacts' birthday, as well as share the contact list using the application’s import and export mechanism.

This portfolio highlights the features and enhancements I have contributed to the development of iContacts, lists my enhancement proposal for the upcoming features, and also includes other contributions I have made. It also showcases the application of software engineering skills I have learned during the development.

Code contributed: [Functional code] [Test code]

Enhancement Added: Nickname

External behavior


Start of Extract [from: User Guide]

Adding a nickname to a person : nickname

Adds a nickname to an existing person in the address book.
Format: nickname INDEX [NICKNAME]

  • Adds a nickname to the person at the specified INDEX. The index refers to the index number shown in the last person listing. The index must be a positive integer 1, 2, 3, …​

  • Existing values will be updated to the input values.

  • You can remove the person’s nickname without specifying anything after the INDEX.

Examples:

  • nickname 1 Eddie
    Adds a nickname Eddie to the 1st person.

  • nickname 1
    Removes the nickname from the the 1st person.

End of Extract


Justification

  • Users may want to add nickname to contacts in order to identify them easily, especially if the users have many contacts.

  • The nickname serves as an additional field that can be searched using the find command.


Enhancement Added: Filter

External behavior


Start of Extract [from: User Guide]

Finding all people by name and associated with one or more tags: filter

Finds persons whose names and/or tag(s) contain any of the given keywords.
Format: filter [n/NAME] [t/TAG]

  • To search by name, type the keywords after the n/.

  • To search by tag, type the keywords after the t/.

  • The search is case insensitive. e.g hans will match Hans

  • Only full words will be matched e.g. Han will not match Hans

  • Persons matching all keywords will be returned (i.e. AND search). e.g. Hans Bo will return Hans Bo but not Hans Yang

Examples:

  • filter n/John
    Returns john and John Doe

  • filter n/John n/Doe or
    filter n/John Doe
    Returns any person with both John and Doe in his name.

  • filter t/friends
    Returns any persons with the tag friends.

  • filter t/friends t/colleagues or
    filter t/friends colleagues
    Returns any person with the tag friends and colleagues.

  • filter n/John t/friends
    Returns any person having the name John and with the tag friends.

End of Extract


Justification

  • Users may need a more precise search.

  • The current find command lists the contacts that match any of the keywords (i.e. an OR search).

  • The filter command is much more restrictive as it lists the contacts that match all the keywords (i.e. an AND search).

  • The name and tags are used as the field for the filter command as they are most likely to be used as constraints in the search.

Implementation


Start of Extract [from: Developer Guide]

Filter mechanism

The FilterCommand uses the NameAndTagsContainsKeywordsPredicate to filter the persons with matching name and/or tags. It accepts the List<String> nameKeywords and List<String> tagKeywords as parameters that are parsed by the FilterCommandParser. The code snippet below is the constructor for the class:

public class NameAndTagsContainsKeywordsPredicate {
    public NameAndTagsContainsKeywordsPredicate(List<String> nameKeywords, List<String> tagKeywords) {
        this.nameKeywords = nameKeywords;
        this.tagKeywords = tagKeywords;
    }
}

The method test(ReadOnlyPerson person) iterates through the nameKeywords and the tagKeywords to find a match of every person from the address book.

Below is an extract of the method test(ReadOnlyPerson person). The method countTagMatches(person) counts and returns the number of matches between the tags of the person and the tags in the tagKeywords. If the tagsMatchedCount is equal to the size of the tagKeywords, this means all the keywords in the tagKeywords match. The hasTag will then be set to true.

public class NameAndTagsContainsKeywordsPredicate {
    @Override
    public boolean test(ReadOnlyPerson person) {
        boolean hasTag = false;

        int numTagKeywords = tagKeywords.size();
        int tagsMatchedCount = 0;
        if (!tagKeywords.isEmpty()) {
            tagsMatchedCount = countTagMatches(person);
        }

        if (tagsMatchedCount == numTagKeywords) {
            hasTag = true;
        }
}

Below is an extract of the same method for name. Each keywords in the nameKeywords will be compared against the name list retrieved from the getName() method of the Person class. If all the keywords match, the hasName will be set to true.

public class NameAndTagsContainsKeywordsPredicate {
    @Override
    public boolean test(ReadOnlyPerson person) {
        boolean hasName = false;
            if (!nameKeywords.isEmpty()) {
                hasName = nameKeywords.stream().allMatch(nameKeywords -> StringUtil
                .containsWordIgnoreCase(person.getName().fullName, nameKeywords));
        }
    }
}

For the FilterCommand to work properly, either the nameKeywords or the tagKeywords must be non-empty.

Design Considerations

Aspect: Should similar search commands be separated
Alternative 1 (current choice): Create a separate command to search for contacts that match all keywords.
Pros: Users will not be confused between the functionality of the find and filter commands.
Cons: Users have to remember an additional command.
Alternative 2: Modify the FindCommand to allow users the option to search contacts that either match any keywords or match all keywords.
Pros: Lesser command for the users to remember when searching for contacts.
Cons: Implementation can be more complicated as there is a need to address both OR search and AND search on the same command.

End of Extract


Enhancement Added: Theme

External behavior


Start of Extract [from: User Guide]

Changing the theme : theme

Changes the theme of the address book to a specific theme.
Format: theme THEME

  • The search is case insensitive. e.g night will match Night.

  • Only full theme names will be matched e.g. night will not match nigh.

Examples:

  • theme day
    Changes the theme to day (Refer to Figure 11).

themeDisplay

Figure 11: Theme changed to day.

End of Extract


Justification

  • Users can have more choices of theme to set for iContacts.

  • Users may find it easier to use iContacts if they are working on their preferred theme.

Implementation


Start of Extract [from: Developer Guide]

Theme-changing mechanism

themeChangingMechanism1

Figure 39 : Component interactions for the theme-changing mechanism

The theme-changing mechanism is an event-driven mechanism. The above diagram (Refer to Figure 39) shows the high-level overview of the component interactions for the theme-changing mechanism.

themeChangingMechanism2

Figure 40 : Sequence diagram for the first part of the theme-changing mechanism

As shown from the sequence diagram above (Refer to Figure 40), after the user entered the command theme day, a new object ThemeCommand will be created. The LogicManager will then execute ThemeCommand, and the event ChangeThemeRequestEvent will be posted by EventsCenter. The code snippet below shows the execute() method of ThemeCommand:

public class ThemeCommand extends Command {
    @Override
    public CommandResult execute() {
        EventsCenter.getInstance().post(new ChangeThemeRequestEvent(theme));
        return new CommandResult(String.format(MESSAGE_SET_THEME_SUCCESS, theme.getTheme()));
    }
}
themeChangingMechanism3

Figure 41 : Sequence diagram for the second part of the theme-changing mechanism

As shown from the sequence diagram above (Refer to Figure 41), the method handleChangeThemeEvent() in MainWindow will handle the event and change the theme of the address book through the method changeTheme() accordingly.

Design Considerations

Aspect: Implementation of ThemeCommand
Alternative 1 (current choice): Utilize ChangeEventRequestEvent that allow MainWindow to handle the event to change the theme.
Pros: ThemeCommand can be more efficient since it does not directly change the theme.
Cons: New developers may find it difficult to understand the event-driven nature of the application.
Alternative 2: Allow ThemeCommand to set the theme directly by allowing it access to MainWindow.
Pros: Easier for new developers to understand.
Cons: Violates the architectural style as ThemeCommand belongs to the Logic component that should not be able to access the UI component where MainWindow belongs to.

End of Extract


Enhancement Proposed: Enhance the command filter

  • Users will be able to perform an even more precise search using more fields as constraints (e.g. nickname, birthday).

  • Users will also able to perform search for keywords that are incomplete (e.g. filtering Ben will also match Benson).

Enhancement Proposed: Enhance the command theme to allow customised theme

  • Users will be able to customise the theme of their choice.

  • Users will be able to import their own CSS file to update the theme.

Enhancement Proposed: Add a new command listtag

  • Users will be able to list all tags they have used when adding or editing the contacts.

  • This allow the users to keep track of all the tags used.

Enhancement Proposed: Merge the command nickname to the command add and edit

  • Users will be able to add, edit or remove the nickname of contact through the add and edit command.

  • Since the nickname is just another field for the contact, it would be better to add and edit the nickname using the add and edit command respectively without the nickname command.

Other contributions

  • Conducted acceptance testing on another team project and uncovered bugs (see issue #48, #49, #51, #52).

  • Wrote additional tests to increase coverage slightly (see pull requests #93, #180).

  • Wrote descriptions of iContacts and target audience in the README.adoc (see pull request #250).

  • Wrote part of the TestScript.adoc (see pull requests #232, #233).

  • Corrected the grammatical errors in the User Guide and Developer Guide (see pull requests #202, #214).

  • Corrected my teammates' grammatical errors in the Developer Guide (see pull requests #263, #265).

  • Highlighted actions to be made to improve the quality of the User Guide and Developer Guide (see issue #169).