Developer Guide
Table of Contents
1. Introduction
1.1 Setting Up
1.2 Project Management & Development Practices
2. Design & Implementation
2.1 Architecture Overview
2.2 NotUS
2.3 Parser & ParserManager
2.4 Commands
2.5 Notebook
2.6 Timetable
2.7 Tags
2.8 Storage
2.9 User Interface
2.10 System Exception
2.11 Usage of External Libraries
3. Product Scope
3.1 Target User Persona
3.2 Target User Profile
3.3 Value Proposition
4. User Stories
5. Non-Functional Requirements
6. Glossary
7. Instructions for Manual Testing
8. Appendix
8.1 List of ParseCommand Classes
8.2 List of Command Classes
1. Introduction
NotUS is a quick and simple, Command Line Interface (CLI) based, note-taking application for keyboard-inclined users. NotUS allows for users to categorize notes by tagging as well as pinning the more important notes. NotUS is also designed to assist in planning timetables to highlight possible clashes.
This document describes the design, implementation and architecture of NotUS. The aim of this developer guide is to get developers and potential contributors familiarised with the design and implementation of NotUS. It is assumed that the reader has some basic understanding of UML Notations. If you do not possess such knowledge, this document is probably not meant for you. Please access the User Guide instead.
1.1 Setting Up
Prerequisites:
- JDK 11
- IntelliJ IDE
Fork this repo and clone it onto your local machine. Import the project as a Gradle project. Ensures that you are using the correct JDK version (For this project we are using JDK 11).
For a more detailed set of instructions, please refer to the following guide.
1.2 Project Management and Development Practices
Please refer to the Development Practices Guide for the Software Development practices used in the project. The document serves to inform on the Project Management frameworks used in the project.
2. Design & Implementation
This section seeks to explain the high-level design of the application. Given below is a quick overview of each component and the explanation of the design architecture in greater detail. NotUS is the main class of the application, and handles the initializing and execution of the appropriate classes.
Diagrams found in our documentation were generated using PlantUML and references were made to addressbook-level2 for the structure of the classes and packages. The structures have been modified to meet the needs of our application.
2.1 Architecture Overview
Figure 1
Figure 1 depicts the architecture design of NotUS. The main components of NotUS are:
InterfaceManager
: Manages the user input as well as the message output from application.ParserManager
: Creates a suitable parser, based on the command, to make sense of user message. The respective parsers then make sense of the information and calls the respective commands.Command
: Executes the necessary tasks, depending on the respective command calls.TagManager
: Stores and manages the creation and deletion of tags and other tag-related functionality.Timetable
: Stores and manages the creation and deletion of events and other event-related functionality.Notebook
: Stores and manages the creation and deletion of notes and other note-related functionality.StorageManager
: Manages the loading of existing saved files and exporting of data to human-editable files.
2.2 NotUS
NotUS manages the flow of the application. On launch, it will create the necessary components, as listed above and then attempts to load any existing saved files into the application. Subsequently, it will accept and interpret the user input and execute the commands accordingly. Figure 2 below depicts the main flow of the application.
Figure 2
💡 The lifeline for Parser and Command should end at destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. This applies to the rest of the sequence diagrams in the document.
💡 Due to a limitation of PlantUML, some of the sequence diagrams may have the destroy marker (X) immediately at the end of the activation bar when there should be a gap before the object is deleted.
2.3 Parser & ParserManager
The ParserManager manages the creation of specific parser objects based on the type of command. The parser then makes sense of the user input and calls the respective commands into action. The class diagram is as follows.
Figure 3
💡 Note that variables and methods in the Command class is empty as it will be covered under Commands.
💡 For a full list of ParseXYZCommand, see Appendix 8.1 List of ParseCommand Classes
- The ParserManager receives the user input message as a whole.
- Interprets the type of command and creates the respective parser for each command.
- The parser then splits the message to identify all the parameters provided.
- Creates and returns the Command class respectively.
The sequence diagram is as follows.
Figure 4
💡 Note that the alternate paths in the sequence diagram above are not exhaustive. There is an alternate path for each unique command. As there are various paths, they are omitted from the diagram. The Command object in the diagram is used to represent a generic Command object that is created through the Parser. Refer to Figure 5 for more details.
Figure 5
Based on the user input, the Parser handles and creates the corresponding Command object.
2.4 Commands
The Command classes update the Notebook and Timetable accordingly. The class diagram is as follows.
Figure 6
💡 Different XYZCommand has different additional variables which are omitted in Figure 6.
💡 For a full list of XYZCommand classes, see Appendix 8.2 List of Command Classes
The following are some examples of the different type of Command Classes and its flow.
AddNoteCommand
Command used to add notes into the notebook.
- Created by the ParseAddNoteCommand with the note object and all the information.
- Obtain content input into note.
- Add the note into the notebook.
- Save the note’s tag(s) into TagManager.
- Save the data into a text file.
- Returns the formatted result string to be displayed.
Figure 7
PinCommand
Command used to pin/unpin notes.
- Created by the ParsePinCommand.
- Gets the note that is referenced either by title or index.
- Toggles the pinned status of the specified note.
- Returns the formatted the title as well as the pinned status of the note to be displayed.
Figure 8
AddEventCommand
- Created by the ParseAddEventCommand with the event object and all the information.
- Checks if the event’s information is valid. If it is not, return the error.
- Gets all events that clashes with this.
- Saves the event into a text file.
- Returns the result of the operation including warnings like clashes and duplicates.
Figure 9
RemindCommand
- Created by the ParseRemindCommand.
- Calls the getReminders from Timetable.
- Timetable then gets all events that is occurring 1 month from now.
- Timetable then generates all reminders for all the events from the previous step.
- Timetable returns all reminders that are to occur today.
- Formatter formats the reminders.
- Returns the result of the formatting.
Figure 10
2.5 Notebook
The notebook component stores a catalogue of notes. On launch, an empty notebook will be created. The note will be created by the user. Notebook handles adding, deleting, editing, finding, sorting, pinning and archiving of notes.
A single note holds information such as title, contents, tags, if its pinned and if its archived. Tag helps to sort user’s notes as the program allows user to retrieve notes by tags. Figure 11 below is a class diagram of the relationship between the Notebook, Note and Tags.
Figure 11
There are multiple overloaded methods. The uses are given below:
- getNote(): is used to retrieve note by integer or note title. getNote is also used to retrieve archived note.
- deleteNote(): is used to delete note by integer or note title.
- getPinnedNotes(): is used to retrieve all pinned notes from the notebook or all pinned notes from a specific notebook.
- getUnpinnedNotes(): is used to retrieve all unpinned notes from the notebook or all unpinned notes from a specific notebook.
- getSortedList(): is used to sort the notebook alphabetically or sort specified notebook alphabetically.
- archiveNotes(): is used to archive note by integer or note title.
- unarchiveNotes(): is used to unarchive note by integer or note title.
The rationale for overloading such methods are given below:
1. getNote()
- There are a total of 3 getNote() methods.
- First one takes in an
int
index as an argument. - Second takes in a
String
of the note title. - The third takes in a
String
of the note title, and aboolean
isArchive.
- First one takes in an
- getNote(
int
) returns the Note that is in the position of specified index within the default list of notes.
- getNote(
String
) checks if the note of the specified title exists in the default list of notes and returns aboolean
value. - getNote(
String
,boolean
) checks if the note of the specified title exists and returns aboolean
value. Theboolean
acts as a flag to determine which of the list of notes (default/archived), will be streamed and filtered.
2. getPinnedNotes()
- There are a total of 2 getPinnedNotes() methods.
- First one takes no arguments.
- Second takes in an
ArrayList<Note>
representing the ArrayList to be filtered.
- getPinnedNotes() returns an
ArrayList<Note>
containing all the pinned notes found in the default list of notes. - getPinnedNotes(
ArrayList<Note>
) returns anArrayList<Note>
containing all the pinned notes found in theArrayList<Note>
parameter that was passed in. Used when the user wants to filter the list-n search with tags. The ArrayList would only contain notes with the specific tags.
3. getUnpinnedNotes()
- There are a total of 2 getUnpinnedNotes() methods.
- First one takes no arguments.
- Second takes in an
ArrayList<Note>
representing the ArrayList to be filtered.
- getUnpinnedNotes() returns an
ArrayList<Note>
containing all the unpinned notes found in the default list of notes. - getUnpinnedNotes(
ArrayList<Note>
) returns anArrayList<Note>
containing all the unpinned notes found in theArrayList<Note>
parameter that was passed in. Used when the user wants to filter the list-n search with tags. The ArrayList would only contain notes with the specific tags.
4. getSortedList()
- There are a total of 2 getSortedList() methods.
- First one takes in 2
Boolean
parameters specifying if only pinned notes from the default list of notes are to be filtered, as well as a flag for the sort order. - Second takes in 2
Boolean
parameters specifying if only pinned notes are to be filtered, as well as a flag for the sort order. A thirdArrayList<Note>
parameter representing the ArrayList to be filtered is also taken in.
- First one takes in 2
- getSortedList(
Boolean
,Boolean
) returns anArrayList<Note>
containing all notes, or just pinned notes found in the default list of notes, in the specified sort order.
- getSortedList(
Boolean
,Boolean
,ArrayList<Note>
) returns anArrayList<Note>
containing all notes, or just pinned notes found in theArrayList<Note>
parameter that was passed, in the specified sort order. Used when the user wants to filter the list-n search with tags. The ArrayList would only contain notes with the specific tags.
5. archiveNotes()
- There are a total of 2 archiveNotes() methods.
- First one takes in an
int
index as an argument. - Second takes in a
String
of the note title.
- First one takes in an
- archiveNotes(
int
) returns a String value of the note title that is in the position of specified index, and that is being archived. - archiveNotes(
String
) checks if the note of the specified title exists in the default list of notes and returns aboolean
value. If the note exists, it will be archived.
6. unarchiveNotes()
- There are a total of 2 unarchiveNotes() methods.
- First one takes in an
int
index as an argument. - Second takes in a
String
of the note title.
- First one takes in an
- unarchiveNotes(
int
) returns a String value of the note title that is in the position of specified index, and that is being unarchived. - unarchiveNotes(
String
) checks if the note of the specified title exists in the archived list of notes and returns aboolean
value. If the note exists, it will be unarchived.
2.6 Timetable
Timetable handles adding, deleting and getting all instances of stored events in a given time period. All scheduling, retrieving and processing of events are done here
The timetable component stores an array of all events and 5 different arrays of recurring events split by frequency of re-occurrence. On launch, an empty timetable will be created. All stored events will be loaded via the StorageManger.
Figure 12
Key Methods Provided:
- getNonRecurringEvents(): Gets all non-recurring events that occurs between the start and end date parameters.
- getAllRecurringEvents(): Gets all recurring events that occurs between the start and end date parameters. Includes repeated events for those that re-occurs.
- getEventSetReminder(): Gets all reminders from a provided set of events.
- getReminders(): Gets all reminders to occur today.
- getTimetable(): Gets all events to occur between the start and end date parameters. Returns a hashmap mapping the month to nested hashmap. The nested hashmap acts as a standard calendar where the keys are the day of the month and values are ArrayList of events occurring on that day.
- getMonthTimetable(): Wrapper around getTimetable for a specific month.
- getYearTimetable(): Wrapper around getMonthTimetable for a specific year.
- getClashingEvents(): Checks the input event to all other events in the timetable to check if the timing clashes. Uses getTimetable on that date of the input event.
An event holds information about all information about start date time, end date time, name, reminders set and how often to re-occur.
On re-occurrence, a new event will be instantiated for display on the timetable.
Figure 13
Event Class Key Methods Provided:
- getReminderDates(): Get an ArrayList of all reminders that this Event will generate.
- occursDuringEvent(): Returns true if another event is occurring during this event’s time duration.
- equals(): Checks if the title of another event is the same and has a clashing time period. If both conditions are satisfied, return true.
RecurringEvent Class Key Methods Provided:
- timeStep(): Returns a LocalDateTime with one time step depending on what the event is.
- toReoccur(): Checks if this event is to reoccur on this date.
- getRecurrences(): Get an ArrayList of Events of all instances this event will reoccur during the specified time period.
- checkAfterEndRecurrences(): Checks if this Event should still reoccur as of this date.
2.7 Tags
Figure 14 below denotes the class diagram for the TagManager and the Taggable Objects (Notes and Events).
Figure 14
💡 As the focus of this diagram is on Tag, TaggableObject and TagManager, the variables and methods of Notes and Events are omitted.
Notes and Events inherit from the abstract class, TaggableObject, and TagManager contains a map of individual unique tags to an ArrayList of TaggableObjects. The TagManager also handles the creation, deletion as well as the tagging and untagging of tags from notes or events.
2.8 Storage
The StorageManager saves and loads data to text files. On launch, the storage manager checks for existing directories that may contain previously saved data, otherwise it creates the necessary directories. Following that, it will load the previously saved notes and events from the text files into NotUS.Below is the class diagram representing the relationship between the StorageManager, Timetable, Notebook, TagManager and ParserManager.
Figure 15
While loading information is passed to the parser manager to prepare the information to be added. Following that, the respective Add Command will be called to add the event/note to the program. Below is the sequence for loading the notes and events when the program first starts up.
Figure 16
During the program, as changes are made to the data, the storageManager saves the data to the hard disk. In case the user force closes the program, the updated data is saved. The sequence diagram below is an example of a note being archived.
Figure 17
When exiting the program, the storageManager saves all the data to the hard disk, in case the user tampers with the txt files while the program is ongoing. Hence, saving the latest version of the data. Below is the sequence diagram of the final saving procedure.
Figure 18
2.9 User Interface
The InterfaceManger receives the input from the user which is then processed by ParserManager, as well as printing the output. The class diagram is as follow.
Figure 19
The Formatter class handles the formatting of the Note(s), Event(s) and message(s) into a String which is then passed to InterfaceManager to be printed out through NotUS. Any changes to the layout or information to display will be done in this class. This class only contains static methods to eliminate the need of a Formatter object.
Figure 20
There are few overloaded functions such as formatNotes, formatTimetable and formatString. These functions are overloaded due to the different format that is to be printed for the different Commands.
A notable function is the encloseRow(String)
which is a recursive function. It takes in the string to be formatted and split the string if it exceeds the maximum character display length, which is then recursively formatted. One additional consideration to take note of is the ANSI escape code for color as they have to be accounted when splitting the string as well as adding spaces to fill up the gap.
2.10 System Exception
The System Exception Enumeration contains all the possible types of exception with specific messages.
Figure 21
Figure 22
💡 As there are various types of exception, the diagram is split into two.
2.11 Usage of External Libraries
This application uses 2 color libraries, JColor and Jansi, to print colored messages on the terminals using ANSI escape codes. While JColor itself is sufficient to colorize the strings, Windows 10 terminal, by default, does NOT support ANSI escape code. Hence, there was a need for the Jansi library to support ANSI escape codes on Windows.
Note on usage of JColor library:
IntelliJ’s ‘Dracula’ and ‘High Contrast’ themes print white fonts as black and vice versa. Developers using either of the themes will have to change the white and black console color to reflect the correct color that is being printed. Instructions to do so are given below.
- Go under Settings -> Editor -> Color Scheme -> Console Colors -> ANSI colors -> Change the Foreground color for Black and White to the correct RGB value.
Figure 23 below illustrates what you should see on your screen.
Figure 23
Note on usage of Jansi library:
While Jansi provides support for Windows terminal to print colored texts, it does not work within IntelliJ IDEA console. Therefore, when running on IntelliJ console, comment out the following lines in NotUS.java main function:
AnsiConsole.systemInstall();
AnsiConsole.systemUninstall();
Remember to uncomment them when building jar files for release.
3. Product Scope
3.1 Target User Persona
Jane Doe is a NUS undergraduate student who is in SOC/FOE and is having a hard time managing her responsibilities and extra curricular activities. She wants to have a convenient platform to take notes and categorize them according to her modules. She also wants to plan her time so she is more aware of her module schedule.
She also wants to be able to export the information so she is able to share them with whomever easily.
3.2 Target User Profile
- NUS students, specifically SOC and CEG students (herein referred to as students) who are comfortable and adept at using CLI.
- Students who want to take notes and categorize them so they are not all over the place.
- Students who are comfortable with CLI.
3.3 Value Proposition
A all-in-one solution for note-taking and managing your schedule. NotUS solves the following problems.
- Lack of access to organizing schedule
- Lack of access to organizing notes
4. User Stories
Version | Target User | Function/Feature | User’s Benefit |
---|---|---|---|
v1.0 | As a … | I want to … | So that I can … |
v1.0 | CEG student | Keep track of my notes | Be organized and find notes easily |
v1.0 | Meticulous student | Categorize my notes by level of importance | Focus on the important topics |
v1.0 | Student who is overloading | Categorize my notes by module | Be more aware of which notes are necessary for the upcoming lessons |
v1.0 | Student who is more visual | Categorize my notes by colour (visible on CLI) | Be able to easily identify which module is which |
v1.0 | Meticulous student | Create daily and weekly task lists | Keep track of my work progress |
v1.0 | Forgetful student | To obtain reminders about my schedule for the day | So I do not forget what I have on for the day |
v1.0 | Busy student | Pin important notes, events and todo list | Focus on the important information |
v1.0 | CEG student | Be able to edit my notes | Update missing information |
Version | Target User | Function/Feature | User’s Benefit |
---|---|---|---|
v1.0 | CEG student | Be able to read my notes | To revise before exams |
v1.0 | CEG student | Be able to delete my notes | To clear up space and keep it more organized |
v2.0 | Outgoing student | Able to add my social events to the timetable | View all my upcoming events and classes |
v2.0 | Busy student | Be alerted if there are clashes in between my events and classes | Reschedule my plan |
v2.0 | Student leader | Be able to share certain events with others | Can get people to join events more conveniently |
v2.0 | CEG student | Archive old notes | Keep dashboard neat while allowing me to refer to old notes when necessary |
v2.0 | CEG student | Be able to import my notes | To make edits |
v2.0 | CEG student | Be able to export my notes | To share my notes with my peers |
v2.0 | CEG student | Be able to import my timetable/events | To make adjustments to my schedule |
v2.0 | CEG student | Be able to export my timetable/events | To share my schedule with my peers |
5. Non-Functional Requirements
Requirement Type | Description |
---|---|
Constraint | Single user product |
Performance | Software should not be dependent on a remote server |
Performance | Software should not exceed 100Mb for JAR file and 15MB per PDF file |
Quality | Users should prefer CLI/Typing |
Technical | Must have Java 11 installed |
Technical | No DBMS, all data to be stored locally |
Technical | Data stored must be in human-editable files |
Technical | Programme should be platform independent |
Technical | Programme should work without an installer |
6. Glossary
- CLI - Command Line Interface
- DBMS - Database Management System
- UML - Unified Modelling Language
- PERT - Program Evaluation Review Technique
- IntelliJ - An Integrated Development Environment (IDE) developed by JetBrains for developing computer software.
- SOC - School of Computing
- FOE - Faculty of Engineering
- CEG - Computer Engineering
7. Instructions for Manual Testing
- Download the jar file and copy it into an empty folder.
- Open a new terminal window and navigate to the same directory where the notus.jar is located. As a shortcut if you are on windows, you can open the folder where the notus.jar is located > click on the address bar > type
cmd
> press enter on your keyboard. - Enter the command
java -jar notus.jar
into the terminal window to launch the application. The application should now be running. - Enter the command
help
to get a list of all available commands and its usages. - For a detailed list on the command features, refer to the user guide.
- Simply enter
exit
to terminate and exit the application.
8. Appendix
8.1 List of ParseCommand Classes
ParseXYZCommands | Functions |
---|---|
ParseAddNoteCommand | Creates a AddNoteCommand |
ParseAddEventCommand | Creates a AddEventCommand |
ParseEditNoteCommand | Creates a EditNoteCommand |
ParseEditEventCommand | Creates a EditEventCommand |
ParseDeleteNoteCommand | Creates a DeleteNoteCommand |
ParseDeleteEventCommand | Creates a DeleteEventCommand |
ParseListNoteCommand | Creates a ListNoteCommand |
ParseListEventCommand | Creates a ListEventCommand |
ParseFindCommand | Creates a FindCommand |
ParsePinCommand | Creates a PinCommand |
ParseViewNoteCommand | Creates a ViewNoteCommand |
ParseArchiveOrUnarchiveNoteCommand | Creates either ArchiveNoteCommand or UnarchiveNoteCommand |
ParseCreateOrDeleteTagCommand | Creates a CreateTagCommand or DeleteTagCommand |
ParseTagCommand | Creates a TagNoteCommand or TagEventCommand |
8.2 List of Command Classes
The list of Command classes is as follow:
- AddNoteCommand
- AddEventCommand
- EditNoteCommand
- EditEventCommand
- DeleteNoteCommand
- DeleteEventCommand
- ListNoteCommand
- ListEventCommand
- PinCommand
- FindCommand
- ArchiveNoteCommand
- UnarchiveNoteCommand
- ViewNoteCommand
- RemindCommand
- CreateTagCommand
- DeleteTagCommand
- ListTagCommand
- TagNoteCommand
- TagEventCommand
- HelpCommand
- ExitCommand
- IncorrectCommand