PROJECT: SaveIt


1. Overview

This document outlines the contributions that Tan Zheng Wei has made to the SaveIt project.

SaveIt is targetted at programmer, and aims to help the user store and keep track of problems and solutions that they encounter. The application is meant to replace traditional use of bookmarks on browsers, and is customized especially for programmers, to make it easier for them to search for previously found solutions online. SaveIt is a desktop application that uses the Command Line Interface (CLI) as a mode to interact with it.

SaveIt is a project that is built over the course of CS2103T with a group of five members including myself.

2. Summary of contributions

  • Major enhancement: Suggestion Logic Subcomponent

    • What it does: This is a component of the codebase that allows the application to make suggestions to the user as they type.

    • Justification: This assists the user to type in commands, by automatically suggesting some possible values that the user might want to enter, it allows the user to complete the command much faster and with less effort.

    • Highlights: The architecture is built in a way that allows easy additions of other types of suggestions by future developers.

  • Other enhancements:

    • Feature: Search for Issues - #60.

    • Feature: Searching Issues by Tags - #89.

    • Feature: Suggestion to copy existing fields to the command line - #120.

    • Architecture: Designed and implemented the overall architecture of the Suggestion Component - #167.

  • Code contributed: [Functional code]

  • Other contributions:

    • Project management:

      • Manage milestones and issues on the repository

    • Testing

      • Additional tests written that covers the new features and Suggestion component - #178, #230, #242, #245

    • Documentation:

      • Update images of diagrams for User Guide and Developer Guide, as well as documentation for new features - #73, #74, #127, #254

    • Community:

      • Reported bugs and suggestions for other teams in the class - #84, #91, #95

3. Contributions to the User Guide

_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users.

3.1. Locating issues by statement: (f)find

Finds issues whose statement contain any of the given search queries.

Format: find [KEYWORDS…​]

  • The issues matching the keywords (based on their statements or descriptions) will be displayed on the issue list.

  • There can be more than 1 keywords

Examples:

  • find python

  • find python java

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

  • The order of the keywords does not matter. e.g. ‘kill port’ will match ‘port kill’

  • Searches through the issue statement.

  • The keywords can partially match the statement.

  • Issues matching at least one keyword will be returned.

  • Searching the keyword will increment the search frequency of the issue (so that it can be ordered accordingly later on).

  • This command can only be executed in the home directory.

3.2. Locating issues by tags: (ft)findtag

Finds issues that contain the tags entered in the search queries.

  • The issues with matching tags (given the keywords) will be displayed on the issue list.

  • There can be more than 1 keywords representing multiple tags

Examples:

  • findtag t/java

  • findtag t/cplusplus t/segmentationFault

  • The search is case-sensitive

  • The keywords must match exactly to the tag names of the issue’s tags

  • If multiple tags are searched, only issues containing all the searched tags will be displayed

  • A matched issue can have other tags (aside from the ones searched). e.g issue[cplusplus][segmentation] will match with a single searched tag [cplusplus]

  • This command can only be executed in the home directory.

3.3. Autosuggesting existing tags in findByTag command

To prevent the user from creating many similar tags / duplicates, whenever the user creates a record with a tag, or modifies a record’s tag, the application searches for similar tags in the system and prompts the user with a list of similar tags.

Example:

autosuggest tag
Figure 1. Autosuggesting of existing tags

3.4. Suggesting to copy existing values

When editing fields in an issue (e.g. Editing an issue statement), if only slight modifications are required, the user will have to copy paste the existing issue statement and modify it, or type it out again. To make things more convenient, after the application prompts the user if they want to copy the existing value onto the command line.

Example:

copy existing
Figure 2. Copying existing fields

4. Contributions to the Developer Guide

_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project.

4.1. Logic component

LogicClassDiagram
Figure 3. Structure of the Logic Component

The Logic Component can be split into two subcomponents:

1. Command Logic Subcomponent

The command logic subcomponent encapsulates the execution of all commands. Each command is represented its own class (e.g. FindCommand.java, AddCommand.java) which all inherit from an abstract Command.java class.

API : Logic.java

  1. Logic uses the SaveItParser class to parse the user command.

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

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

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

2. Suggestion Logic Subcomponent

The suggestion logic subcomponent encapsulates the evaluation of user inputs as it is keyed into the command line, differing from the command logic in that command logic is only executed upon entering the command, whereas suggestion logic is called whenever the user input changes (without the need to enter).

  1. SuggestionLogic parses user inputs whenever it changes.

  2. This determines which Suggestion object is created by the SuggestionLogicManager.

  3. The evaluation of the Suggestion object reads data from the Model (e.g. finds a specific Issue).

  4. The result of the evaluation is encapsulated as a SuggestionResult object which is passed back to the Ui.

Given below is the Sequence Diagram for interactions within the Logic component when an API call is made. The Sequence Diagram is split into two branches of logic:

  1. Command logic: Shows an example command execution of the delete 1 Command, which calls the DeleteCommand.

  2. Suggestion logic: Shows an example suggestion evaluation of the user input edit 1 i/, which calls the CopyExistingSuggestion.

SDexamplesForLogic
Figure 4. Example Interactions Inside the Logic Component for the command and suggestion logic subcomponents.

4.1.1. Design Consideration

Aspect 1: Architecture Design of the Suggestion Logic subcomponent

The architecture behind the Suggestion Logic subcomponent is designed by mimicking the original Logic component that handles the command logic. This is because the required implementation behind both is actually similar, needing to access both the Ui and the Model components. Hence, the SuggestionLogic and Suggestion are built similar to the original Logic component which has the corresponding classes Logic and Command that serves as interfaces to the Ui and Model components respectively. However, the internal implementation of the internal classes of the Suggesetion Logic subcomponent is different (i.e. Parser).

  • Alternative 1 (current choice): Create parser methods all within SuggestionLogicManager that parse the user input directly - first parsing them based the commands (handled in SuggestionLogicManager#parseUserInput) and subsequently parsing based on the Prefix that the text cursor is at.

    • Pros: Keeps the code DRY.

    • Cons: Deviates from the original internal structure of the Command Logic subcomponent.

  • Alternative 2: Create parser classes for each command (e.g. EditSuggestionParser) that handles the further parsing of the Prefix and creates the relevant Suggestion object.

    • Pros: Code is more modularized since the parsing is now handled by classes.

    • Cons: Code is repeated in each SuggestionParser class.

We use the first alternative as it best handles the issue of having duplicate logic. The internal parsing of the user inputs for suggestions differs from the Command Logic in that, Command Logic only has one level of parsing (parsing commands), whereas Suggestion Logic requires two levels (parsing commands and parsing prefixes). In order to ensure the same way of handling the same prefix in different commands (e.g. findtag t/ and edit 1 t/), methods are created to do so (e.g. SuggestionLogicManager#handleTagNameSuggestion). This keeps the code DRY and reduces repeats logic in the code.

Further abstractions can be made to this in the future as more Suggestion(s) added, by moving the suggestion handlers (SuggestionLogicManager#handleTagNameSuggestion) into a new Suggestion class (instead of an interface) that the specific XYZSuggestion(s) inherit from.

Aspect 2: Deciding on how to parse the user input

We have two options to parse the user input and find the prefixes and their values.

  • Alternative 1 (current choice): Use the existing ArgumentTokenizer and ArgumentMultimap to parse the user input

    • Pros: The logic for separating the prefixes in ArgumentTokenizer and storing them in ArgumentMultimap is similar to what we need to parse our user input. By reusing the code, it prevents us from needing to rewrite code with duplicated logic elsewhere in our code base, which keeps our code DRY.

    • Cons: ArgumentTokenizer and ArgumentMultimap is also used by the command logic subcomponent, by reusing this code in the suggestion logic subcomponent, it increases the coupling between the implementation of the two subcomponents which can result in new issues arising (aspects 3 and 4).

  • Alternative 2: Parse the input by using a separate piece of code (duplicated logic from the ArgumentTokenizer and ArgumentMultimap). Slight modifications will need to be made to the duplicated logic due to differences in implementation.

    • Pros: Reduces the coupling between the two command and suggestion logic subcomponents. Although the logic behind the implementation will be similar to ArgumentTokenizer, having it as a duplicate piece of code means that any changes will not affect and cause bugs in the other subcomponent.

    • Cons: Having it duplicated logic might affect future development, especially when this duplicated logic needs to be modified, then the implementation for both pieces of code will have to be modified as well. If not documented properly, this could cause bugs if only one piece of code was modified.

Aspect 3: Data structure of Prefix

The original implementation of Prefix only stored the value of the prefix string (e.g. /t or /i), however since the implementation of the suggestion logic subcomponent uses ArgumentMultimap, it requires that the position of the Prefix in the user input string be known as well.

  • Alternative 1 (current choice): Store an additional field position in Prefix

    • Pros: Since the prefix and its location are related, this keeps all the information pertaining to the prefix encapsulated in a single class, which helps with the modularity of the code.

    • Cons: The command logic subcomponent does not require the position field in Prefix, which results in it being unused. This might be confusing for future developers. This also resulted in having another aspect to consider later on (aspect 4).

  • Alternative 2: Move the implementation and checks for the prefix position out of Prefix

    • Pros: This does not affect the code implemented in the Command logic subcomponent, and we will have less to fix.

    • Cons: Related pieces of code will be separated (i.e. since the position is stored separately, it is not properly encapsulated).

Aspect 4: Distinguishing each Prefix in ArgumentMultimap

The original hashcode used by Prefix only hashes the prefix string, as such, adding a position field will not make a difference since two same prefixes with different positions (e.g. /t …​ /t) will still hash to the same key. The previous implementation handles this by having the value in the HashMap within ArgumentMultimap store a List<String> which will append all values with the same prefix.

  • Alternative 1 (current choice): Set the hashcode of Prefix to use both the prefix string and the position.

    • Pros: The prefixes now hash differently if they have the same prefix string but different position, this allows us to have finer control needed for the Suggestion logic subcomponent when handling the prefixes in ArgumentMultimap.

    • Cons: The Command logic subcomponent does not make use of the position field. As such, when attempting to extract out the values of each Prefix at the Parser, it cannot find the values as they are now hashed with the position as this is unknown to the Parser (an indirect result of the coupling due to the chosen implementation in aspect 2).

  • Alternative 2: Use the original implementation

    • Pros: Code does not affect the Command logic subcomponent, which already takes up a significant portion of the code base, hence less changes are needed.

    • Cons: The code and logic are not be well encapsulated (for the Prefix) and we will have repeated logic in the codebase.

We eventually chose Alternative 1 as it provides a better structure and encapsulation for our code overall. We also came up with a workaround for Alternative 1’s cons, by modifying the method ArgumentMultimap#getValue.