
Welcome to cOOPer’s Developer Guide!
cOOPer is a Command Line Interface (CLI) desktop application developed to simplify administrative processes of tech startups such as communication and finance management.
cOOPer was developed in Java 11 following an Object-Oriented Programming (OOP) paradigm, hence the letters OOP in its name.
This developer guide is for software designers, developers, and software testers of cOOPer. It will be your reference manual if you are looking to:
Throughout this developer guide, you will see text formatted differently from normal text, as well as symbols appearing before another line of text. The table below explains the formatting and symbols in this developer guide.
| Formatting/Symbol | Meaning |
|---|---|
| italics | Text in italics represent terminology specific to using / developing cOOPer. |
| bold | Text in bold emphasizes the text’s importance and indicates that you should pay more attention to the text. |
code |
Short lines of text highlighted as such indicate a class, method, component or user input. It is also used to represent directories / file paths. |
| ℹ️ | The info symbol indicates useful information about diagrams / content in this developer guide. |
| 💡 | The light bulb symbol indicates a useful tip which eases the development of cOOPer. |
This section includes the sources of code, documentation and third-party libraries reused / adapted in developing cOOPer.
Storage component was adapted from one of our member’s CS2113T Individual Project (iP). A few of the methods for file reading and file creation were reused.Util.java was adapted from this website.generate feature was adapted from this website.💡 These are the software / tools used in developing cOOPer. You are recommended to use them too:
- IDE: IntelliJ IDEA (highly recommended)
- JDK: Java 11
- Version control: Git
- Git GUI: Sourcetree
- Build system: Gradle
cooper.Cooper
src/main/java/cooper/Cooper.javaCooper.java and select ‘Run Cooper.main()’. /$$$$$$ /$$$$$$ /$$$$$$$
/$$__ $$ /$$__ $$| $$__ $$
/$$$$$$$| $$ \ $$| $$ \ $$| $$ \ $$ /$$$$$$ /$$$$$$
/$$_____/| $$ | $$| $$ | $$| $$$$$$$//$$__ $$ /$$__ $$
| $$ | $$ | $$| $$ | $$| $$____/| $$$$$$$$| $$ \__/
| $$ | $$ | $$| $$ | $$| $$ | $$_____/| $$
| $$$$$$$| $$$$$$/| $$$$$$/| $$ | $$$$$$$| $$
\_______/ \______/ \______/ |__/ \_______/|__/
=========================================================================
Hello I'm cOOPer! Nice to meet you!
=========================================================================
Log in or register to gain access to my features!
To log in, enter "login [yourUsername] /pw [password] /as [yourRole]".
To register, enter "register [yourUsername] /pw [password] /as [yourRole]".
To exit, enter "exit".
=========================================================================
>> [Logged out]
src/test.test and select ‘Run ‘All tests’ ‘.

.github/workflows folder. CI for cOOPer is automatically run at each push to the ‘master’ branch or whenever a pull request is created.💡 The architecture diagram and UML diagrams in this document were created using draw.io. The
.pngtemplates used to create the diagrams can be found in thedeveloperGuideDiagramsfolder. To create and edit diagrams, access the draw.io website, select ‘Open Existing Diagram’ and open the desired.pngfile. Any changes to the diagram will be saved automatically.
cOOPer consists of two main layers: the verification layer and the features layer as shown in the diagram below. cOOPer recognizes different sets of inputs at each layer.

Upon launching the app, the user starts at the verification layer where they can only log in or register.
Entering valid credentials will then grant the user access to the features layer where they can input commands to use cOOPer’s features.
At this layer, entering the logout command will bring the user back to the verification layer.

The Architecture Diagram above shows the high-level design of cOOPer and how cOOPer’s components are connected.
Cooper contains the main method of the program. Cooper’s responsibilities are as such:
Cooper initializes the components and loads stored user data into the components.Cooper reads user input which is then processed by the components to produce a result.Cooper shuts down the components and performs cleaning up where necessary.Apart from Cooper, the rest of the app consists of these seven components:
Ui: Handles the reading of user input and printing of messages to the terminal.Parser: Interprets and validates user input.Verification: Verifies that the user is signing in to cOOPer with valid credentials.Command: Executes commands which are parsed from user input.Resources: Manages data for cOOPer’s finance, meetings and forum features while the app is running.Storage: Loads data from, and saves data to storage files in the computer hard disk.Util: Provides utility which help with some of cOOPer’s features.ℹ️
userInputrepresents the credentials input by the user for verification. For example,register John /pw 12345 /as admin.

ℹ️
userInputrepresents a command input by the user. For example,meetings.
ℹ️XYZCommandis an object representing a command recognised by cOOPer. For example,MeetingsCommand.

API: cooper.ui

Ui component consists of a parent Ui class and its subclasses as shown by the class diagram above.Ui class contains general constants and methods used across cOOPer’s components which read user input and print recurring messages.ABCUi) contain constants and methods specific to the function of that component.
For example, FinanceUi contains a method printBalanceSheet() which prints a balance sheet formatted with headers containing the entries input by the user.Ui component have static methods so Cooper does not need to contain a reference to Ui.The Ui component:
[Logged out] label indicates that the user is currently logged out of cOOPer.Verification and Cooper mainly for reading user input, while it is used by Resources mainly for printing output messages.API: cooper.parser

Parser component consists of an abstract ParserBase class with its subclasses, CommandParser and SignInDetailsParser.SignInProtocol object while user input at the features layer will be parsed to construct a Command object.SignInProtocol object executes the signing in of the user with details provided while the Command object executes the command input by the user.ParserBase contains a reference to the Parser interface from the dopsun chatbot-cli library used by cOOPer.
More information about cOOPer’s implementation of the library can be found here.The Parsercomponent:
SignInProtocol / Command object with the correct parsed attributes.InvalidCommandFormatException, UnrecognisedCommandException etc. to signal erroneous input.API: cooper.verification

Verification component consists of a Verifier class which contains the list of registered users and methods to verify user credentials.SignInProtocol class is an abstract class representing one of the two sign in protocols, Login or Registration.SignInProtocol class contains a reference to a SignInDetails object which as a whole, represents a sign in attempt by the user using one of the two protocols, with the corresponding SignInDetails.
For example, a Login object containing SignInDetailsX represents the user’s login attempt with the details SignInDetailsX.Login and Registration override SignInProtocol’s abstract method, executeSignIn() as there are different conditions to check depending on whether the user is trying to log in or register. More on the verification process can be found here.The Verification component:
API: Command.java

Command component consists of an abstract Command class and its subclasses as shown in the diagram above.ABCCommand) overrides Command’s abstract method, execute() and has its own unique implementation of the method based on how the command is to be executed.HelloCommand which prints out Hello world! by inheriting from Command and implementing the execute() method as such:public class HelloCommand extends Command {
// constructors and attributes for execution of the command are omitted
public void execute() {
System.out.println("Hello world!");
}
}
ScheduleCommand contains a String representing the meeting name as well as an ArrayList representing the users associated with that meeting.execute() method takes in a SignInDetails object as a parameter. This object represents the sign in details of a user who has successfully signed in to cOOPer. For some of cOOPer’s finance / meetings features which are only accessible by an admin, the UserRole attribute of this SignInDetails object is checked to grant correct access to the feature.The Command component:
Resources component depending on the command.Storage component if there is any change to the data after the command is executed successfully.Ui component to inform the user of the status of command execution.API: cooper.resources

The Resources component contains ResourcesManager which manages access rights to feature managers based on UserRole.
E.g. the following line will only return a valid reference to FinanceManager if userRole == UserRole.ADMIN. Otherwise, null will be returned indicating the user does not have the access right to that module.
FinanceManager financeManager = resourcesManager.getFinanceManager(userRole);
It also implements a give-receive pattern to pass private members of ResourcesManager class to StorageManager safely. This is because StorageManager has “super privilege” to access internal data structures of feature managers and save to/ load from hard disk.
The Resources component:
MeetingManager, FinanceManager or ForumManager based on UserRole of the request body. E.g. Only an admin is able to get FinanceManager successfully.StorageManager safely upon request.API: cooper.finance

Finance component contains the FinanceManager, BalanceSheet, CashFlow, and Projection classes, as well as the FinanceCommand enumeration.FinanceManager constructs the instances of the BalanceSheet, CashFlow and Projection for use, and contains attributes and methods that aid the related functions.FinanceCommamnd enum helps the Parser to understand what Finance function is being used, with three states: CF, BS, and IDLE.Finance component also contains the PdfGenerator class (not shown in the diagram above) for the generate feature. More info on this feature can be found here.The Finance component:
API: cooper.meetings

The Meetings component contains the MeetingManager and Meeting classes.
MeetingManager stores two attributes:
TreeMap<LocalTime, ArrayList<String>> object,ArrayList<Meeting> object.The MeetingManager constructs the instances of Meeting, and stores it as an ArrayList<Meeting> in itself.
The Meetings component:
API: cooper.forum

Forum component contains a ForumManager, ForumPost , ForumComment and ForumPostBase. Both ForumPost and ForumComment are inherited from abstract base class ForumPostBase as they contain the attributes content and username.ForumManager keeps a list of ForumPosts and each ForumPost keeps a list of ForumComments.The Forum component:
username of the request body must match the username field of the post or comment.API: cooper.storage

Storage component consists of a parent Storage class along with its subclasses as shown in the diagram above.Storage class contains a filePath attribute representing the path of the file where the data is to be stored. It also contains methods common to all its subclasses such as getScanner() and createFileInDirectory() which aid in the process of writing to and creating the storage file.Cooper contains a reference to a StorageManager object. This StorageManager object in turn contains references to each of the subclasses of Storage which store cOOPer’s data in the Verification and Resources components.ℹ️We do not put the
StorageManagerclass underResourcesfor the following reasons:
Storageclass is cOOPer’s internal construct for bookkeeping various internal data structures and recovering them at startup. This does not categorise under any features the user can interact with and hence should not be kept underResourcesManager.Storagehas super privileges to access internal data structures of all components inResources. This contradicts the goal ofResourcesManagerwhich is to manage access rights to different features depending on user roles, and hence should be kept separate from it.
The Storage component:
filePath into the Verifier, FinanceManager, MeetingsManager and ForumManager objects upon launching the app.filePath from the Verifier, FinanceManager, MeetingsManager and ForumManager whenever a change is made to the data in these objects.API: Util.java
Util component is a component unrelated to cOOPer and serves mainly as a helper component to make some of cOOPer’s features possible.Util class, namely inputStreamToTmpFile() and inputStreamToString().inputStreamToTmpFile() is used to recreate the dopsun chatbot-cli’s training files (originally located in src/main/resources/parser).
In the process of packaging cOOPer into a JAR application, these training files are converted to bytes which are unable to be read in by the chatbot API. Hence, there is a need to recreate these files for the chatbot to work.inputStreamToString() is used for cOOPer’s generate feature which allows the user to generate a PDF file from data in cOOPer’s balance sheet or cash flow statement.
This method is used to convert the .tex template files (located in src/main/resources/pdf) into a String object which can then be handled easily in the code. More details of the implementation can be found here.cOOPer uses the dopsun chatbot-cli library as its frontend parser that allows you to define any arbitrary input schema under src/main/resources/parser/command-data.properties
such as
login = login ${username-hint} /pw ${password-hint} /as ${role-hint}
The chatbot’s Parser library automatically parses the place-holders defined with $ leaders to strings. For example, login Yvonne /pw 12345 /as admin will be parsed
into the following fields:
{ "username-hint" : Yvonne,
"password-hint" : 12345,
"role-hint" : admin }
This gives great flexibility and extensibility to the Parser component as you do not need to worry about writing new parsing schemes for every command and adding new commands to cOOPer for new features become trivial.
The Verifier class facilitates the verification of the credentials of a user registering or logging in to cOOPer.
Different conditions are checked depending on whether a user is trying to log in or register. For example, if a user is trying to register, cOOPer will check if the username is already registered and asks the user to log in if they are not registered yet. On the other hand, if an unregistered user is trying to log in, cOOPer will ask the user to register first.
For a registered user trying to log in, cOOPer will first check if the entered password is correct. This is done with the help of the PasswordHasher class which hashes the entered password with the user’s salt stored by cOOPer. The hash obtained will then be compared to the user’s stored hash to determine if the entered password is correct.
If the password is correct, the user’s role will then be checked to determine if they are logging in with the role they registered with.
The following sequence diagram shows the detailed process of registering a user.
ℹ️
userInputisregister John /pw 123 /as admin.
ℹ️TheexecuteSignIn()method actually takes in arawPasswordas its parameters but is omitted in this sequence diagram as the registration process does not require the raw password of the user.

The SignInDetailsParser constructs a SignInDetails object parsed from the arguments in userInput. This SignInDetails object is then used to construct a Registration object which executes the registration of the user. This process is shown by the sequence diagram below.

Assuming that the above registration has taken place successfully, the following sequence diagram shows the login process of the user.
ℹ️
userInputislogin John /pw 123 /as admin.
ℹ️The process of parsinguserInputtakes place similar to when a user is registering. The reference frame is omitted in this sequence diagram for simplicity.

The Password Based Key Derivation Function (PBKDF2) hashing algorithm is used for hashing user passwords. This algorithm is used together with a 64-bit salt text for each password before it is hashed to improve security and decrease susceptibility to rainbow-table attacks, where duplicate user passwords are still stored securely.
This algorithm is recommended by the National Institute of Standards and Technology (NIST) for password storage and our implementation also adheres to NIST specifications:
The Resources component manages the access rights to other manager components like the FinanceManager, MeetingManager and ForumManager. The following sequence diagram shows the two main operations of ResourcesManager:
FinanceManager, the user needs to pass in their UserRole. ResourcesManager will check if the user has the right access to the feature and returns the requested object if so, and null otherwise.StorageManager class has “super privilege” to access internal data structure of FinanceManager, MeetingManager and ForumManager. Private members are passed safely using the give-receive pattern, instead of universal getters.

The Finance component provides features such as adding and listing of financial statements, i.e. the balance sheet and cash flow statement as well as compounded projection of Free Cash Flow growth.
The sequence diagram below illustrates the process of adding to a given financial statement, in this case the balance sheet.

When the user wants to add an entry to a financial statement, FinanceManager will first determine if the amount should reflect as positive or negative in the financial statement, as well as which section of the financial statement the entry belongs to. FinanceManager will then add the entry to the respective financial statement and its section’s net amount.
When the user wants to view a financial statement with list, FinanceManager will run a check that the net amounts of each section of the financial statement are calculated correctly before the statement is displayed to the output.
When the user wants to project free cash flow, FinanceManager will first help to calculate free cash flow by subtracting the CapEx (Capital Expenditure: a field of the cash flow statement) from the total cash from Operating Activities. Subsequently FinanceManager will compare this value to the previous year’s value, and calculate the percentage increase. This percentage increase will then be used in a recursive periodic compound interest formula to calculate the following year’s free cash flow, at the same percentage increase.
The PdfGenerator abstract class is responsible for the generation of the financial statement as a PDF via the generate command. It is inherited by the subclasses, BalanceSheetGenerator and CashFlowStatementGenerator, with each subclass containing different methods to add different sections to the PDF.
The PDF is generated with the help of an online LaTeX compiler. The LaTeX (.tex) templates for the PDF can be found under src/main/resources/pdf. The PdfGenerator class employs the use of the inputStreamToString() method of the Util component to convert the contents of these LaTeX templates into a String object. The LaTeX template, which is now a String is then manipulated by calling Java String methods like replace() and append().
Certain identifiers (in the form of LaTeX comments ‘%’) in the LaTeX template will be replaced by the actual values of cOOPer’s financial statement.
The example below shows the template of an entry in the financial statement:
\centering
% {Description}
& \centering
& \centering
& \centering
& % {Amount}
\\[3ex]
Calling replace("% {Description}", "Depreciation and Amortisation") and replace("% {Amount}", 1500) on the template above will result in the following:
\centering
Depreciation and Amortisation
& \centering
& \centering
& \centering
& 1500
\\[3ex]
When compiled, the LaTeX code above will correspond to an entry ‘Depreciation and Amortisation’ on the PDF with the amount $1500. This technique can be used on the header and summary templates which will format the header and summary of a particular section in the financial statement.
The methods createHeader(), createEntry() and createSummary() in PdfGenerator perform the text replacement as shown above. The diagram below shows how these methods correspond to the different parts of the ‘Operating Activities’ section in the cash flow statement PDF.

createHeader(), createEntry() and createSummary() also add the template to an ArrayList after performing the text replacement on the template. Iterating through the ArrayList, these templates are then appended together using append().
This forms a long String which is then sent to the online LaTeX compiler via a POST request. The reply data obtained from the request is used to construct the PDF via the write() method of Java’s FileOutputStream class.
The MeetingManager class facilitates the storing of availability in cOOPer.
When the user declares an availability, the addAvailability function in MeetingManager performs some checks before successfully storing their availability.
addAvailability looks at the format of the [date] and [time] entered, which will be checked using the isValidDateTimeFormat function.addAvailability checks if the time entered is at the start of the hour, using the isStartOfHour function.addAvailability checks if the user has already entered their availability under the same date and time, by checking all the names under the specified date and time in the availability TreeMap.The following sequence diagram shows the detailed process of declaring an availability. username is Sebastian and userInput is available 11-08-2021 14:00.

The MeetingManager class facilitates the scheduling of meetings.
When the user schedules a meeting ScheduleCommand checks if the [date] and [time] parameter is entered and calls manualScheduleMeeting in MeetingManager if it is and autoScheduleMeeting if it isn’t.
The following sequence diagram shows the process of automatically scheduling a meeting. username of the user scheduling is Sebastian and userInput is schedule Project Meeting /with Eugene.

The following sequence diagram shows the process of manually scheduling a meeting. username of the user scheduling is Sebastian and userInput is schedule Project Meeting /with Eugene /at 11-08-2021 14:00.

The following sequence diagram shows three operations with the forum, addPost, commentPost and deletePost.
For adding a post, ForumManager will create a new ForumPost object and store its username and content.
For commenting on a post, ForumManager will first check if the postId specified is a valid index. If it is not, an exception will be thrown. Otherwise, it will get the ForumPost by its postId and add a new ForumComment to it.
For deleting a post, ForumManager will again check the postId and delete the post only if the postId is valid.
ℹ️ In the actual implementation,
ForumManagerwill also ensure the username of the user requesting for thedeletePostoperation matches the owner of the forum post. This checking is omitted in this sequence diagram for simplicity and represented as the method call toremove(postId).

ℹ️Due to the way the
Storagecomponent is implemented, the classes and methods used for storage have names which are quite similar. In order to generalize the explanations in this section for how data is saved and loaded, the termXYZwill be used as a placeholder whereXYZissignInDetails,balanceSheet,cashFlowStatement,availability,meetingsandforum.
The StorageManager class facilitates the saving and loading of cOOPer’s data to and from its storage files. This data includes the sign in details of registered users (from the Verification component) , entries of the balance sheet and cash flow statement, list of availabilities, scheduled meetings, and forum posts (all from the Resources component).
cOOPer’s data is stored separately in multiple text files named XYZ.txt located in the ‘cooperData’ folder in the home folder.
Certain commands, when executed successfully, can change the data in the Verification and Resources components. (e.g. register, add, available, etc.) Whenever the data in these components change, the command that made the change will call the saveXYZ() method of the StorageManager to update the storage file with the change.
For example, when a new availability is added successfully, the method saveAvailability() is called by AvailableCommand and the storage file is updated with the new list of availabilities.
The following sequence diagram shows the general procedure of saving data to the storage file whenever a change is made.

Data is loaded from cOOPer’s storage files into the Verification and Resources component upon launching the app. The StorageManager constructor is first called and each subclass XYZStorage is initialized with the file paths of their storage files, XYZ.txt.
The loadAllData() method of StorageManager is then called and this method in turn calls the loadXYZ() methods of the XYZStorage subclasses. If the storage files are not present upon launching cOOPer, the storage files will be created and any error in file creation will be made known to the user.
Since data in the storage files are of a specific format, any change to the storage format will throw an InvalidFileDataException and a message will be printed to specify the file containing invalid data.
The following sequence diagram shows the general procedure of loading data from the storage file upon launching cOOPer.

Current choice: Individual subclasses which store the sign in details of registered users, entries of the balance sheet and cash flow statement, list of availabilities, scheduled meetings, and forum posts in separate storage files.
saveXYZ(), loadXYZ(), etc.)The target user profile of cOOPer consists of all levels of administration in a tech startup, namely from the employee level of Secretary up to the management level of CEO.
Example Users:
cOOper’s value proposition: Manage company financials faster than typical human accounting means & manage company communication more reliably than a typical GUI driven app.
💡 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 ‘help’ instructions | refer to them when I forget how to use the application |
*** |
new user | register an account | login and return to my saved work at any point later on |
*** |
user | see a list of roles at login | login to the specific role I need to carry out a task |
*** |
user | have a password encrypted login | have my saved work be protected from any external tampering |
*** |
finance admin | create the company’s financial statements | assess the company’s current financial health accurately and quickly |
*** |
secretary employee | see all company personnel’s daily availability | schedule meetings between all available members easily |
** |
finance admin | automatically generate projections on the company’s yearly profitability | assess the company’s potential future growth |
** |
finance admin | generate the company’s financial statements as a PDF document | view and share a neat version of the financial statement with my colleagues |
** |
employee | make posts on a company forum | discuss difficulties or interesting developments in the company |
** |
employee | add my available timings for meetings | ease the scheduling of meetings with my colleagues |
** |
secretary employee | automatically schedule a meeting without having to know other person’s availability | save time on finding an appropriate time to meet |
* |
user in a hurry | customise shortcut keys in the app | save time on retrieving the data I desire |
java -version. Ensure that you are using Java 11 or above.java -jar cOOPer.jar. exit.[Logged out].register [username] /pw [password] /as [role] where [username] is your username, [password] is your password and [role] is one of ‘admin’ or ‘employee’.[Logged out].login [username], /pw [password] /as [role] where [username], [password] and [role] are the username, password and role you registered with.[Logged out] label at the command prompt is no longer present.help.bs to initiate the balance sheet function.[Balance Sheet].add [amount] to add the first entry. list upon completion of the balance sheet.bs to initiate the balance sheet function.[Balance Sheet].list to view the current balance sheet. cf to initiate the cash flow statement function.[Cash Flow].add [amount] to add the first entry. list upon completion of the cash flow statement.cf to initiate the cash flow statement function.[Cash Flow].list to view the current cash flow statement. cf → add.proj [years] to project up to your specified number of years. The generate command works regardless of whether the prompt label is showing [Balance Sheet], [Cash Flow] or is not even present.
bs → add.generate bs.cf → add.generate cf.available [date in dd-MM-yyyy] [time in HH:mm].availability.schedule [meetingName] /with [username]. [username] is a user(not you) who is available in at least one common date and time as you.schedule [meetingName] /with [username] /at [date] [time]. [username] and yourself must be both available at this date and time.meetings.post add hello world.post comment hello world 2 /on 1. post delete 1. post list all.logout.
Expected output: A message informing you that you have logged out of cOOPer is shown along with the instructions on how to log in, register or exit. The label at the command prompt now shows [Logged out].