PROJECT: Mark

1. Overview

This project portfolio showcases my contributions to my team’s group Software Engineering project, Mark. My team consisting 5 NUS Year 2 Computer Science students were tasked with enhancing a basic desktop application, AB-3, to solve a problem for a specific target group.

Given 8 weeks to conceptualise and implement the project in Java, we chose to morph AB-3 into a bookmark manager application we call Mark. Mark aims to improve computing students' productivity by helping them organise their web browsing and note-taking activities.

Mark has a visually appealing graphical user interface (GUI) to visually guide the users around the application. This is what Mark looks like when you open the application:

450
Figure 1. Dashboard tab view of Mark.

My role was to design and implement the annotation feature so users can conveniently make notes on important web pages instantly while reading them. This feature uses commands that begin with annotate and works on the offline version of the web pages converted by Mark.

The following sections illustrate my contributions in more detail.

2. Summary of contributions

This section gives a summary of the features I implemented, documentation I did and other parts I contributed to my team project.

2.1. Major enhancement: Added the annotation feature

I added the ability to annotate the offline copy of the web page referred to by a bookmark. Users can not only read their web pages offline, but they can also make notes on them directly.

  • What it does: Adds, deletes and edits annotations.

  • Justification: Users can take notes directly on the web page. This makes it convenient for users to review their notes.

  • Highlights: This enhancement works with undo and redo` commands to provide a sense of security when annotating. The implementation was rather challenging because to support annotations, the offline document has to take into account different kinds of paragraphs, each having a unique set of restrictions. Nevertheless, the completed annotation feature works well and is aesthetically pleasing on the application.

  • Credits: I used the JSoup library to extract the paragraph contents of the web pages. Colours of the highlight are provided by the javafx css extension.

2.2. Major enhancement: Added the web browsing feature

I added the ability to browse web pages on the application.

  • What it does: Allows users to search the web and browse web pages.

  • Justification: Users no longer need to open a separate web browser window to view their bookmarks, which improves navigability within the application.

  • Highlights: This enhancement allows users to browse directly on the application and more conveniently bookmark the tab they are viewing on Mark.

  • Credits: I referred to the web view embedded in AB-4, which was lacking a navigation bar.

2.3. Minor enhancement: Added tab view

I created tabs and added the ability to switch views from tab to tab.

  • What it does: Organises the GUI and allow users to switch between tabs to view the various components with ease.

  • Justification: An organised interface will help users get familiarised fast and improve their quality of use of the application. Hence, tabs are necessary to declutter the GUI so each component has sufficient space for users to view them in comfort.

  • Highlights: This enhancement leaves much room for customization and personalization for future work.

2.4. Code contributed

To view the code I wrote, please click on the following links: Functional code Test code Reposense overview

2.5. Other contributions

  • Project management: managed release v1.2 on GitHub

  • Enhancements to existing features: created layout of the GUI (Pull requests #63, #74) and designed the GUI color scheme to make it more attractive (Pull request #246)

  • Community: reviewed Pull Requests (with non-trivial review comments): #64, #96, #128, #129 and provided suggestions for another team.

3. Contributions to the User Guide

Given below is one of the sections that I contributed to our User Guide for Mark. It showcases my ability to write documentation targeting end-users.

Start of extract from Mark User Guide

3.1. Making annotations on an offline copy: annotate

If you want to add a new annotation on the offline copy of a bookmark, you can do so using the annotate command. With this command, you can highlight a paragraph on the offline document and optionally attach a supplementary note to said paragraph. You can add notes to explain the highlight or as content-relevant notes to refer to in future.

If you are looking to add a general note not pertaining to any specific paragraph, you can also use this command to add it to the bottom of the page, hereby known as the General notes section.

Upon annotating, the your view will be switched to the offline tab showing the results of your command.

A general note is not attached to any paragraph of the original web page. Instead, when adding general notes, they are attached to invisible at the bottom of the document. General notes are found at the bottom of the page, referred to as the General notes section.

You can choose to use this command to overwrite any existing note. However, note that when you highlight the paragraph of a general note, Mark remembers the hidden highlight colour, but no highlight is reflected on the application.

Paragraphs are identified using a numbered identifier that starts with either P or G. You can refer to the numbered identifier in the leftmost column of the offline document to check out the respective identifier for each paragraph. Paragraphs of general notes have identifiers that begin with G, while paragraphs from the original web page have identifiers that begin with P.

Format: annotate INDEX p/PARA_NUM [n/NOTES] [h/HIGHLIGHT_COLOUR=yellow]

For example:

  • Input annotate 24 p/p1 n/summary of paragraph h/orange into the command box.

100
  • Observe that paragraph P1 is now highlighted orange and a note with content “summary of paragraph” is attached to it.

300

Parameter constraints:

  • INDEX is the bookmark that you want to annotate offline version of. If INDEX is invalid, a warning message will be displayed.

  • PARA_NUM is the numbered identifier of the paragraph to be marked. PARA_NUM must be NULL or it must begin with P or G (e.g. P3). PARA_NUM is case-insensitive. If PARA_NUM is invalid, a warning message will be displayed.

  • NOTES is the content of notes to add.

  • HIGHLIGHT_COLOUR is either orange, pink, green or yellow. This selects the highlight colour to mark out paragraphs. If no colour is specified, the colour is set to yellow by default. If the colour provided is invalid, a warning message will be displayed.

Other examples:

  • annotate 1 p/p2
    This highlights paragraph P2 yellow in the offline copy of bookmark 1.

  • annotate 1 p/p2 h/pink
    This overwrites any existing highlight of paragraph P2 with pink in the offline copy of bookmark 1. Any notes attached remain attached.

  • annotate 1 p/p2 n/change or add note content
    This overwrites any note attached to paragraph P2 to a note with content “change or add note content” in the offline copy of bookmark 1. Highlight colour remains the same. Otherwise, paragraph P2 will be highlighted yellow and a new note with content "change or add note content" will be added to the paragraph.

  • annotate 1 p/null n/adding a general note
    This adds a note with content "adding a general note" to the General notes section in the offline copy of bookmark 1.

End of extract from Mark User Guide

4. Contributions to the Developer Guide

This section will refer to this class diagram drawn by Dorcas:

Annotation Package Class Diagram
Figure 2. Structure of the Annotation Package

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

Start of extract from Mark Developer Guide

4.1. Annotation feature

4.1.1. Implementation

The annotation feature builds upon the structure of an offline document used to represent a Readability4J-derived cache. Such an offline document is represented by OfflineDocument, which contains at least one Paragraph. Each annotation is attached to an entire Paragraph. The annotation will be internally stored as an instance of Annotation. You can view the class structure of the annotation feature here.

The annotation feature supports adding, deleting and editing annotations. Command words annotate, annotate-delete and annotate-edit activate the respective functionality. These command words make up the family of annotation commands. When activated, each calls the respective command parser to create the corresponding AnnotationCommand. Subsequently, when necessary the appropriate Paragraph is retrieved, and the annotation is handled according to the command. You can study the hierarchy of the family of annotation commands here:

400
Figure 3. Structure of the family of annotation commands.

The following sequence diagram illustrates how a command of the family of annotation commands operates:

400
Figure 4. Sequence diagram showing how annotation commands generally work. cmdStr is the string representing the command input by the user. arguments is cmdStr without the command word.
The lifeline for AddAnnotationCommandParser and AddAnnotationCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.

In the figure above, the specifics of how Parser#parse and AnnotationCommand#execute are abstracted out because each command is handled differently. The reference frame may refer to the following sequence diagrams for adding, deleting and editing annotations respectively:

350
Figure 5. Sequence diagram referenced by the general sequence diagram for annotations when cmdStr given is annotate 1 p/p2 n/example note h/orange. ARGS include pid, highlight and note.

The above example adds an annotation to a :TrueParagraph. Alternatively, if the user chooses to add a general note instead, OfflineDocument#addPhantom is called instead, creating an annotated :PhantomParagraph.

A :TrueParagraph, whose content is from the cache, must be highlighted in order to have a note attached. A :PhantomParagraph is a temporary holding place for a general note and has no content.
350
Figure 6. Sequence diagram referenced by the general sequence diagram for annotations when cmdStr given is annotate-delete 1 p/p2 n/true. ARGS include pid.

The above example deletes only the highlight from a :TrueParagraph which has a note, and then creates a :PhantomParagraph to keep a reference to the note.

For other annotate-delete functions, instances of other derived classes of DeleteAnnotationCommand is used instead of :DeleteAnnotationHighlightCommand.

If the user requests to remove the entire annotation instead, DeleteAnnotationAllCommand is used and the an:Annotation is simply dereferenced. If the user requests to remove only the annotation note, DeleteAnnotationNoteCommand is used and the AnnotationNote of Annotation is dereferenced. Alternatively, if the user requests to remove annotations from all paragraphs, DeleteAnnotationClearAllCommand is used and the entire :OfflineDocument is dereferenced and a new, unannotated copy is created.

350
Figure 7. Sequence diagram referenced by the general sequence diagram for annotations when cmdStr given is annotate-edit 1 p/g1 to/p1 h/green. ARGS include origPid, targetPid and newHighlight and newNote.

The above example moves general note G1 to paragraph P1 and highlights P1 green. As per the specifications, users are not allowed to move any annotation to :PhantomParagraph using annotate-edit, so targetP must either be null or a :TrueParagraph. However, origP can be anything as long as it is an instance of a derived class of Paragraph.

The reason why we do not allow users to use this command to make notes general is because there is not much value in doing so given we only support highlights and notes as annotations. If you want, you can easily extend the edit function to be able to allow moving annotations to the general section.

You will see why the sequence diagram for the annotate-edit command is much longer than that of annotate-delete in the Design Considerations section.

Summmary

The following activity diagram summarizes what happens when a user attempts to annotate their offline document:

350
Figure 8. Activity diagram for an annotation request.

4.1.2. Design Considerations

The following are a few design considerations made in deciding how to implement the annotation feature:

Aspect: How to structure the offline components to store annotations

An offline document is composed of multiple paragraphs, each having at most 1 annotation. A cached copy has the original cache of the website. There are hence a few alternatives in which we can combine these elements and store them:

  • Alternative 1 (current choice): Let each CachedCopy have an OfflineDocument that consists of paragraphs with content parsed from the original cache. Each paragraph contains a annotation, if any.

    • Pros: Easy to implement.

    • Cons: May have performance issues in terms of memory usage and overheads from layers of abstraction.

  • Alternative 2: Let each cache keep only the original cache of the website. Use another class to store the annotations with the respective paragraph identifiers.

    • Pros: Will use less memory since the content of the website is not duplicated.

    • Cons: Not straightforward to implement. Stray notes will need a different implementation to order and store them. Objects not well abstracted.

I decided to proceed with Alternative 1 as it is easier to implement and more feasible to implement in light of time constraints. This alternative offers accessibility to model components that are highly related in functionality.

Aspect: How to implement command to delete annotations

An annotate-delete command deletes a part or whole of an annotation. In particular, deleting only the highlight of a paragraph with a note will have a side effect where the note becomes general. Considering these variations, there are a few alternatives that we can consider to implement the delete function:

  • Alternative 1: Use a single DeleteAnnotationCommand object to execute the entire delete functionality

    • Pros: Makes it clear as all related functionality are packaged in the same class.

    • Cons: Does not follow the Single Responsibility Principle and Open-close Principle. All checks will have to be performed in the same class, which makes it messy as well.

  • Alternative 2 (current choice): Use a different subtype of DeleteAnnotationCommand to represent and execute each use case.

    • Pros: Easier to implement since delete functionality is rather discrete and each function is mutually exclusive. Less checks will be required for each class implemented. Also easier to extend functionality in future.

    • Cons: Need to write multiple lines of code due to multiple layers of abstraction.

I decided to proceed with Alternative 2 as it is easy and feasible to implement in light of time constraints. Since the individual functions of annotate-delete do not overlap, this alternative makes it less cumbersome to modify if there needed to be a change in delete functionality.

Aspect: How to implement command to edit annotations

An annotate-edit command can edit existing annotations and/or moving annotations from paragraph to paragraph. As the functionality can be mixed, there are a few options we can take to implement the edit functionality:

  • Alternative 1 (current choice): Use a single EditAnnotationCommand object to execute the entire edit functionality

    • Pros: Easier to implement and also makes it clear as all related functionality are packaged in the same class.

    • Cons: Does not abide by the Single Responsibility Principle. All checks will have to be performed in the same class.

  • Alternative 2: Use a different subtype of EditAnnotationCommand to represent and execute each use case.

    • Pros: Easier to extend. Also, less checks on conditions can be performed for each class implemented.

    • Cons: Not straightforward to implement. The functions can overlap, which makes it difficult to segregate into separate responsibilities.

I decided to proceed with Alternative 1 as it is easier to implement. The overlaps in use cases of the annotate-edit command makes it difficult to separate the responsibility. Hence the first alternative is more feasible to implement.

End of extract from Mark Developer Guide