diff --git a/.gitignore b/.gitignore
index f69985ef1f..cc576e104d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,8 @@ bin/
/text-ui-test/ACTUAL.txt
text-ui-test/EXPECTED-UNIX.TXT
+
+logger.log
+logger.log.lck
+
+data/
diff --git a/README.md b/README.md
index 4eef34421f..559a26377f 100644
--- a/README.md
+++ b/README.md
@@ -63,3 +63,4 @@ Steps for publishing documentation to the public:
1. Scroll down to the `GitHub Pages` section.
1. Set the `source` as `master branch /docs folder`.
1. Optionally, use the `choose a theme` button to choose a theme for your documentation.
+
diff --git a/build.gradle b/build.gradle
index b0c5528fb5..48f2155d13 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,7 +29,7 @@ test {
}
application {
- mainClassName = "seedu.duke.Duke"
+ mainClassName = "seedu.mtracker.MTracker"
}
shadowJar {
@@ -43,4 +43,5 @@ checkstyle {
run{
standardInput = System.in
+ enableAssertions = true
}
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 0f072953ea..97219313ec 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -1,9 +1,9 @@
# About us
-Display | Name | Github Profile | Portfolio
+Display | Name | Github Profile | Product Portfolio Page
--------|:----:|:--------------:|:---------:
- | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
+
| Kum Wing Ho | [Github](https://github.com/kum-wh) |[PPP](team/kum-wh.md)
+
| Kwok Xiu Sheng Theodore | [Github](https://github.com/theodorekwok) | [PPP](team/theodorekwok.md)
+
| Lam Junyu William | [Github](https://github.com/williamlamjy) | [PPP](team/williamlamjy.md)
+
| Kumaravel Vignesh | [Github](https://github.com/KVignesh122) | [PPP](team/kvignesh122.md)
+
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 64e1f0ed2b..b7dd7ced25 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -2,37 +2,589 @@
## Acknowledgements
-{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+* Referenced the AB3 developer guide [here](https://se-education.org/addressbook-level3/DeveloperGuide.html).
-## Design & implementation
+## Setting up and getting started
+First fork the mTracker repo from [here](https://github.com/AY2122S1-CS2113T-T12-1/tp) and clone the fork into your computer.
-{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
+Do read through this developer guide to understand the project software architecture.
+Writing code:
+ * Before starting, please familiarise yourself with the Java code style guidelines [here](https://se-education.org/guides/conventions/java/index.html).
+ * After coding if you would like to create a pull request, please ensure that your code passes the github checks before
+ asking for a reviewer.
+
+## Design
+
+> Tip: The diagrams in this guide were designed using PlantUML.
+> Their original .puml files can be found in the diagrams folder [here](https://github.com/AY2122S1-CS2113T-T12-1/tp/tree/master/docs/diagrams).
+
+### Architecture
+
+The following diagram denotes the high-level design of the mTracker
+program:
+
+
+
+Major components of the app:
+* `main` contains the `MTracker` class which contains methods responsible for launching and
+running the app. It first initializes the required components
+ and executes the overall program.
+* `ui` holds the `TextUi` class, which is responsible for displaying various greetings,
+instructions for user input, and other display texts. The class contains both
+ strings of commonly used display texts like the console input prompter, and
+ methods that print these strings out, thus ensuring satisfactory user interface and
+ communication with user.
+* `console` is a collection of closely-related parser classes that take in the user input, analyse them
+to understand the various commands the user would like to execute through the console.
+* `commands` is another collection of closely-related classes that deal with
+executing particular commands determined by the necessary parser classes in console.
+* `model` contains two types of classes:
+ * `InstrumentManager` singleton class that manages access to the arraylist containing
+ all the instruments created by user during the session.
+ * `subinstrument` is a collection of the different instrument classes: `Crypto`,
+ `Etf`, `Forex`, and `Stock`. The primary role of these classes is to initialize instrument
+ objects of their said type containing their necessary financial information recorded from the user.
+* `filemanager` is responsible for saving the session's instruments data to local file, updating
+them during runtime, and restoring data from previous session when the program is relaunched.
+* `commons` contains classes which are utilised by the other components to execute their functionality:
+ * The `Validate` class is responsible for doing various checks on the user inputs and the file data.
+ * The `error` package contains different exception classes that displays user specific error messages to guide the
+ user in the usage of the program.
+
+The subsequent sections will elaborate on the more technical design and implementation details of
+the architectural components briefly explained in this section.
+
+### Console component
+
+The main parent class in `console` package is the `InputParser` class which is defined in `InputParser.java`.
+The figure below represents the class diagram of how all the parser classes interact with classes outside the `console`
+package:
+
+
+
+How the `InputParser` class works:
+1. When the user enters a command along with the relevant parameters if any, the
+ `getCommandComponents(commandInput)` method in `InputParser` separates the user's command by spaces to return a string array.
+2. The command is then determined by using the `filterByCommandType(componentComponents, instruments)` method which would return the corresponding
+ command type. Examples of different command types are `AddInstrumentCommand`, `DeleteCommand`, `ListCommand` etc.
+
+#### Design considerations for parsing inputs for add functionality
+Given the different types of financial instruments supported by mTracker, an abstract class `AddInstrumentParser`
+which inherits from `InputParser` is implemented. Multiple `AddXYZParser` (`XYZ` is
+a placeholder for the different instrument types, for example `AddStockParser`) child classes of
+`AddInstrumentParser` support the parsing of different instruments and their parameters.
+This implementation provides greater extensibility to the add functionality to support more instrument types.
+
+Two alternatives to get the instrument information from the user were considered. The first alternative was to
+get the user to add in all the information in a single line with separators
+(for example: `stock TSLA; 909.68; negative; To buy`). This was not implemented as it is likely
+for the user to enter the parameters in the wrong order. This becomes especially problematic if there are multiple
+parameters that require the same type to represent different attributes of the instrument (for example: The entry and
+exit price attributes in Forex instrument).
+
+The second alternative was to get the user to indicate which attribute the parameter would belong to
+(for example: `stock n/TSLA p/909.68 s/negative r/To buy`). This way there are distinct markers to define which
+parameter belongs to which attribute. However, this was not implemented as given that some instruments have as many as
+7 different attributes, it requires the user to recall all the attributes needed to add an instrument which is not
+user-friendly.
+
+Therefore, the current implementation prompts the user on the information required to add a particular instrument.
+This helps to support the user through the process of adding a new instrument.
+
+#### Design considerations for parsing inputs for edit functionality
+Despite currently supporting 4 types of financial instruments, the parsing of inputs for the edit functionality does not require
+4 edit classes for each instrument. This is because the edit functionality is done on an existing instrument which
+contains information on what parameters can be edited on. Therefore, only a single `EditInstrumentParser`
+is needed to filter out all the other parameters that are irrelevant to the instrument.
+
+In addition, the current design is able to parse multiple input parameters and display the relevant instructions to
+users in editing those parameters for a particular instrument. This allows the user to edit multiple parameters of a
+instrument at once which increases its user-friendliness.
+
+### Model Component
+
+The `model` package contains the `InstrumentManager` class and `Instrument` class. It is defined
+in `InstrumentManager.java` and `Instrument.java` respectively. This figure below represents the class diagram of
+how the different class work together:
+
+
+
+The `Model` component:
+
+* Stores the instrument data through `Instrument` objects which are contained and managed by the `InstrumentManager`
+* Contains an abstract parent `Instrument` class. The 4 child sub-instrument classes `Crypto`, `Etf`, `Forex` and
+`Stock` implements the Overridden methods (e.g. `textFileFormatting()`).
+* Contains the `InstrumentManager` class which manages the list of instruments (e.g. add a new instrument to
+the list). `InstrumentManager` is implemented as a singleton class to ensure that only one instrument list exists.
+This ensures the user only edits one list and prevents possible data corruption (e.g. adding a new instrument to
+different lists).
+* Does not have any dependencies on any of the other components, as the `Model` component is meant to be responsible
+solely for the data representation and modification of instruments.
+
+### Command Component
+
+The Command component contains all the commands classes, where its respective class is instantiated when a valid command is entered by the user.
+
+Some of the key command classes include:
+```
+1) AddCrytoCommand
+2) AddEtfCommand
+3) AddForexCommand
+3) AddStockCommand
+4) DeleteCommand
+5) DoneCommand
+6) EditInstrumentCommand
+7) FindCommand
+6) ListCommand
+7) ViewCommand
+8) InvalidCommand
+9) ExitCommand
+```
+This figure below shows the class diagram of all the commands classes:
+
+
+Command component:
+
+* All commands are child classes of the abstract parent `Command` class.
+* Each command class is responsible for carrying out its respective function where each command will execute different actions.
+ In addition, most of these command classes interact with `TextUi` to ensure that the user sees the correct responses from the program based on their input.
+* All Command classes have a method `execute()` that does the actions required according to the user's input.
+* Commands component also contains a parent `AddInstrumentCommand` class that all commands related to adding an instrument inherits from.
+* Other than ExitCommand and InvalidCommand, the other command classes are dependent of on the InstrumentManager and its various methods in order to execute the required actions on the stored instruments.
+* The command classes are dependent on the `TextUi` class. This allows the command class to display its execution results to the user.
+
+### Ui Component
+
+The ui component only contains the `TextUi.java` file and its API can be found
+[here](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/src/main/java/seedu/mtracker/ui/TextUi.java).
+
+It is a basic java class containing string attributes and helper methods for displaying the different features, texts and
+instructions to the user. Hence, **under the single-responsibility principle (SRP), its only responsibility is to act as the primary interaction platform
+between the user and the rest of the program**.
+
+As detailed by the UML diagrams in the Architecture sections above, **many other parser and command classes utilize
+the methods contained in `TextUi`** to display instructions on the console for required user input. Hence, **most other
+classes of this program are dependent on the methods of this `TextUi` class** for their proper interaction with the user.
+
+Thus, the **`TextUi` class has high cohesion** as it contains all the user text display methods for the various classes
+in itself. This **enhances maintainability** as only this class **has to be modified to achieve a small change in
+the desired texted or instruction to be displayed by various classes**, and **increases reusability of the module**
+as all aspects of texts or instruments to be displayed on the console **have been localized**.
+
+On the other hand, the `TextUi` class itself **has a dependency only on an Instrument class** whenever
+the user wishes to `list` out all the instruments in the watchlist or if s/he wants to `view`
+one such instrument in detail. The following sequence diagram explains `TextUi`'s interaction with an `Instrument` class when
+`ListCommand#execute()` calls the `displayAllInstruments(instruments)` method when the user wishes to list out all instruments in the watchlist:
+
+
+
+Hence, in this scenario, `TextUi` relies on the particular `Instrument` class's `getGeneralParams()` method to retrieve
+all the general financial information recorded for that instrument like the instrument's name,
+current price, and sentiment. Through this sequence process, `TextUi` displays this information in an appropriate format to
+the user.
+
+A similar approach is also taken when the user wishes to `view` a particular instrument. However,
+instead of a loop being iterated over in the `displayInstruments()` method, the `getAllParams()` method is called instead
+which fetches all the financial information of that particular instrument back to `TextUi` for display:
+
+
+
+### FileManager Component
+The `filemanager` package contains the `Storage`, `InstrumentEncoder` and `InstrumentDecoder` classes. It is defined in
+the `Storage.java`, `InstrumentEncoder.java` and `InstrumentDecoder.java` respectively. This figure below represents the class diagram of
+how the different class work together:
+
+
+
+The FileManager Component:
+
+* Contains the `Storage` class that loads data from any pre-existing text file. If the file does not exist, it creates
+a new text file to store the data. It updates the file by calling the `writeFile(instruments, writeToFile)` method in the `InstrumentEncoder` class.
+* Contains the `InstrumentEncoder` class which encodes the instrument data into a text file format for decoding.
+* Contains the `InstrumentDecoder` parent class which decodes the text file. The 4 sub-decoder classes `CryptoDecoder`,
+`EtfDecoder`, `ForexDecoder` and `StockDecoder` adds the respective instruments with their decoded attributes into the
+`InstrumentManager` enabling the program to load pre-existing data.
+* Has some dependencies on the `Model` component as it saves and retrieves data from `Model` objects.
+
+#### Design considerations for decoding functionality
+Given the different types of financial instruments supported by mTracker, the `InstrumentDecoder` class is implemented.
+Multiple `XYZDecoder` (`XYZ` is a placeholder for the different instrument types, for example `EtfDecoder`) child classes of
+`InstrumentDecoder` support the decoding of different instruments and their parameters.
+This implementation provides greater extensibility and code re-usability to the decoding functionality to support more
+instrument types. Greater cohesion is achieved by separating the classes to give more focus on each instrument type and
+a higher level of abstraction.
+
+## Implementation
+
+### Add instrument feature
+The add instrument functionality is mainly handled by the `console` and `commands` components. Within the `console`
+component, the `InputParser` class implements the method `InputParser#getAddInstrumentParameters()`. This method calls
+`AddInstrumentParser#filterByInstrumentType(componentComponents)` which will then guide the user through the process of adding a new
+instrument. `AddInstrumentParser#filterByInstrumentType(componentComponents)` will throw an `InvalidInstrumentError` if the
+user provides an instrument type that is invalid.
+
+The figure below represents the sequence diagram when the user wants to add a stock:
+
+
+
+More details about the reference frame for obtaining the stock details and creating the AddStockCommand object are shown
+below.
+
+
+
+The process for adding the other instruments follow a similar process to the sequence above. The main difference would
+be the type of instrument parser called, the parameters collected from the user and the command type returned. For
+example instead of calling `AddStockParser#getStockSpecificParameters()`, its equivalent for adding a crypto is
+`AddCryptoParser#getCryptoSpecificParameters()`.
+
+From the notes in the sequence diagram above, for every attribute in the instrument, there would be an instructional
+prompt to get user to provide information for that attribute. This is done through a series of methods in
+the `TextUi` class.
+
+After getting the stock details from the user, the `AddStockCommand#execute()` will be called. This creates a new stock
+adds it to the list of instruments. Here below we have a sequence diagram detailing the process.
+
+
+
+For other instrument types a different command will be executed. For example if the user is adding a new crypto,
+the equivalent command used would be the `AddCryptoCommand`.
+
+### Edit instrument feature
+
+The edit instrument functionality mainly involves the `console`, `commands` and `model` components. Within the `console`
+component, the `InputParser` class implements the method `InputParser#getEditInstrumentCommand(comandComponents, instruments)`. This method calls
+`InputParser#getParametersToEdit(validAttributes)` which will prompt the users to input which parameters of the instrument to edit
+and check if the parameters entered are valid. Invalid inputs will not be processed.
+
+The process of writing the new values of the parameters to be edited is handled by the `EditInstrumentParser` class.
+The method `EditInstrumentParser#createEditCommand(parametersToEdit, instrumentToEdit, instrumentNumber)` calls `EditInstrumentParser#getEditedParameters(parametersToEdit, instrumentToEdit)` which
+calls multiple individual methods that check if its parameters is being edited and to enter a new value for the
+parameters.
+
+The execution of setting the new values of the parameters is handled by the `EditInstrumentCommand` class.
+
+The figure below represents the sequence diagram when the user wants to edit the name a stock:
+
+
+
+More details about the reference frame for getting the new edited parameters from the user is given below:
+
+
+
+From the note in the reference diagram above, each parameter the user wants to edit,
+there would be an instructional prompt to guide the user to give a valid input. This is done through the `TextUi` class.
+
+Below is the sequence diagram detailing the command execution of setting the stock with the new values (in this case is setting the name parameter to new name):
+
+
+
+More details about checking if parameters exist in HashMap and to edit the parameters if it exists is shown below:
+
+
+
+The process for editing other instruments or other parameters follow a similar process to the sequence above.
+The main difference would be the parameters collected from the user and the parameters allowed to be edited.
+For example the user can edit the expiry parameter in Crypto but not in Stock.
+
+### Mark an instrument as done feature
+
+The done instrument functionality mainly involves the `console`, `commands` and `model` components. Within the `console`
+component, the `InputParser` class implements the method `InputParser#getDoneInstrumentCommand(commandComponents, instruments)`, which processes the index of instrument
+and check if the instrument has been previously marked as done.
+
+The execution of marking the instrument as done is handled by the `DoneCommand`class.
+
+The figure below represents the sequence diagram when the user executes a done command. In this scenario the user
+gave the command "done 1". Here "done" is the command keyword and "1" represents the current position of the instrument
+in the list of instruments:
+
+
+
+More details about the reference frame for executing the done command is shown below:
+
+
+
+### Loading pre-existing data
+The loading of pre-existing data is mainly handled by the `filemanager` and `model` components. The main method calls
+`Storage#loadFileData(instrumentManager)` which uses `InstrumentDecoder#readFile(instrumentManager, fileLines)`. This method calls
+`InstrumentDecoder#addSavedInstrumentToList(instrumentManager, textSegment)` for each pre-existing instrument which will add the
+corresponding instrument in the `InstrumentManager` through calling the `XYZDecoder#addXYZToList(textSegment, instrumentManager)`.
+In the event the instrument is not one of the 4 types of instruments, the `InstrumentDecoder` will throw a new `InvalidInstrumentInFileError`
+and display the corresponding error message.
+
+The figures below represents the sequence diagrams when the user loads a pre-existing crypto:
+
+
+
+More details about the reference frame for decoding and updating the `InstrumentManager` is shown below:
+
+
+
+More details about the reference frame for adding the decoded instrument into the `InstrumentManager` is shown below:
+
+
+
+The process for loading other pre-existing instruments follow a similar process to the sequence above. The main difference
+would be the type of instrument decoder called, the different instrument specific decoded parameters and the type of instrument
+added to the `InstrumentManager`. For example when loading a stock instead of calling `CryptoDecoder#addCryptoToList(textSegment, instrumentManager)`
+it will call `StockDecoder#addStockToList(textSegement, instrumentManager)`.
+
+If loading the file data has any error, it will throw the corresponding file error. This file error will display the
+appropriate message through the `TextUi` class.
+
+### Storing current data
+The storing of current data is mainly handled by the `filemanager` and `model` components. The main method calls
+the `Storage#updateFileData(instruments)` which implements the `InstrumentEncoder#writeFile(instruments, writeToFile)` method.
+This method calls the `Instrument#textFileFormatting()` method for every instrument that is being stored. The formatted
+instrument details are then written to the `MTracker` text file.
+
+The figure below represents the sequence diagram when the user stores current data:
+
+
+If storing the file data has any error, it will throw the corresponding file error. This file error will display the
+appropriate message through the `TextUi` class.
## Product scope
### Target user profile
-
-{Describe the target user profile}
+ * Busy individuals that need a convenient way to record financial information on the go.
+ * Familiar with using the terminal and command-line interface applications.
+ * Individuals that consistently keep up to date with financial news and events.
### Value proposition
-{Describe the value proposition: what problem does it solve?}
+Financial information is rapidly evolving and growing beyond what the standard brokerages
+and traditional financial news provide. The information is readily and easily accessible to any individual with an internet connection
+via social media and public forums. Therefore, mTracker aims to empower individuals with the capability to note and organise
+such information in a quick and easy way.
## User Stories
-|Version| As a ... | I want to ... | So that I can ...|
-|--------|----------|---------------|------------------|
-|v1.0|new user|see usage instructions|refer to them when I forget how to use the application|
-|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list|
+|Priority|Version| As a ... | I want to ... | So that I can ...|
+|----|---|---|------|------------|
+|***|v1.0|user|add a stock|record details of the stock|
+|***|v1.0|user|add a cryptocurrency|record details of the cryptocurrency|
+|***|v1.0|user|add a forex|record details of the forex|
+|***|v1.0|user|add an etf|record details of the etf|
+|***|v1.0|user|see my recorded instruments|refer to all of my instruments with their corresponding details|
+|**|v1.0|user|add additional information about an instrument|keep track of information other than the instrument's traits|
+|***|v2.0|user|see my previously recorded instruments|continue adding to my list of instruments for my day to day trading|
+|**|v2.0|user|have a clear and concise list of my instruments|easily look through the list without having too many details|
+|*|v2.0|user|view further details of my instruments|view excessive details of each instrument without cluttering the list|
+|**|v2.0|user|edit an instrument|update certain details of an instrument when their traits change|
+|**|v2.0|user|mark instruments|so that I can have a checklist of instruments to prioritise|
+|**|v2.0|user|find an instrument|locate an instrument without having to go through the entire list|
+|*|v2.1|user|abort an add/edit process|cancel adding/editing an instrument if my mind changes during the process.|
## Non-Functional Requirements
-{Give non-functional requirements}
+1. The program should work on operating systems with `Java 11` installed.
+2. The program should allow for persistent data storage of instruments.
+3. The program should be able to store 1000 instruments in the storage text file and manage them during run-time.
+4. The program should be usable for a novice user who is starting to learn about the financial markets.
+5. The program should handle any corruption of storage text file data.
+6. The program should have high extensibility for supporting more instrument types in the future.
+7. The price information stored in the program should never be negative.
+8. The program should not crash regardless of user's inputs.
## Glossary
-* *glossary item* - Definition
+* *Instrument* - Represents assets that can be traded. Most common examples are stocks and foreign currency.
+* *Etf* - Known as Exchange Traded Funds, they are a type of instrument that tracks the performance of a particular asset.
+* *Forex* - Foreign exchange market for trading currencies. An example included is the USDSGD exchange rate.
+* *Crypto* - Digital currencies that are secured by cryptography methods.
+* *Stock* - Shares of a company that provide the owner a certain level of ownership of said company.
## Instructions for manual testing
-{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing}
+In this section are some instructions for getting started with manual testing of the program.
+Feel free to come up with more test cases to try for yourself.
+
+**Launch and start up**
+
+1) Ensure that you have `Java 11` installed.
+
+2) Download the latest jar file [here](https://github.com/AY2122S1-CS2113T-T12-1/tp/releases).
+
+3) In your terminal under the directory where the jar file is saved type `java -jar mTracker.jar`.
+ * If it is successful you should see a mTracker greet message. If you get an error message please create a new issue
+ [here](https://github.com/AY2122S1-CS2113T-T12-1/tp/issues) along with a description of the error.
+
+4) Refer to the [userguide](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/UserGuide.md) to understand how
+to use the program.
+
+
+**Add functionality Testing**
+
+To test the add functionality, there are a few test cases you can try:
+1. Testcase: Adding a stock with an empty name.
+
+```
+mTracker$main> add
+ Please key in the type of instrument:
+mTracker$add> stock
+ Name of stock:
+```
+
+Expected: An error message that says name cannot be empty.
+
+```
+Sorry stock cannot have an empty name!
+ Name of stock:
+mTracker$add>
+```
+
+2. Testcase: Adding a crypto with an empty current price.
+
+```
+mTracker$main> add
+ Please key in the type of instrument:
+mTracker$add> crypto
+ Name of crypto:
+mTracker$add> bitcoin
+ Current Price:
+mTracker$add>
+```
+
+Expected: An error message that says current price cannot be empty.
+
+```
+Sorry price cannot be empty.
+ Current Price:
+mTracker$add>
+```
+
+3. Testcase: Adding an etf with a past return of -150.
+
+```
+mTracker$main> add
+ Please key in the type of instrument:
+mTracker$add> etf
+ Name of etf:
+mTracker$add> SPY
+ Current Price:
+mTracker$add> 468.53
+ Sentiment for instrument:
+mTracker$add> neutral
+ Past Returns (optional):
+mTracker$add> -150
+```
+
+Expected: An error message that says past returns cannot be less than -100 and input will be ignored.
+
+```
+Sorry, past return inserted cannot be lesser than -100. Input value will be ignored.
+ Remarks (optional):
+mTracker$add>
+```
+
+**Edit functionality Testing**
+
+To test the edit functionality, there are a few test cases you can try:
+1. Testcase: Edit an instrument at an index that is out of range. For example if you have less than 100 instruments in
+your list, you can try the example below.
+
+```
+mTracker$main> edit 100
+```
+
+Expected: An error message that says instrument does not exist at that index.
+
+```
+Oops, instrument does not exist at that index.
+```
+
+2. Testcase: Enter parameters that are not supported by stock type.
+
+```
+mTracker$main> edit 7
+ Please enter one or more Stock parameters to edit separated by a single space only.
+ done-status, name, current-price, sentiment, remarks
+mTracker$edit> entry-price
+```
+
+Expected: An error message that says the parameter is invalid and will be ignored.
+
+```
+entry-price is an invalid attribute of this instrument and will be ignored.
+```
+
+**Delete functionality Testing**
+
+To test the delete functionality, there are a few test cases you can try:
+1. Testcase: Delete an instrument at an index that is out of range. For example if you have less than 100 instruments in
+your list, you can try the example below.
+
+```
+mTracker$main> delete 100
+```
+
+Expected: An error message that says instrument does not exist at that index.
+
+```
+Oops, instrument does not exist at that index.
+```
+
+**Done functionality Testing**
+
+To test the done functionality, there are a few test cases you can try:
+1. Testcase: Set an already done instrument as done.
+
+```
+mTracker$main> done 7
+ Nice! I have marked this instrument as completed:
+ [S][X] IBM; 144.61; positive
+mTracker$main> done 7
+```
+
+Expected: An error message that says instrument is already done.
+
+```
+Instrument at provided index has already been marked as completed!
+```
+
+**Find functionality Testing**
+
+To test the find functionality, there are a few test cases you can try:
+1. Testcase: Try the find command without any search string.
+
+```
+mTracker$main> find
+```
+
+Expected: An error message that says please enter a search string.
+
+```
+Oops, please input a search string after 'find' command.
+```
+
+**List functionality Testing**
+
+To test the list functionality, there are a few test cases you can try:
+1. Testcase: Listing instruments with extraneous parameters.
+
+```
+mTracker$main> list extraneous parameters
+```
+
+Expected: It should perform the list action ignoring the additional words.
+
+**View functionality Testing**
+
+To test the view functionality, there are a few test cases you can try:
+1. Testcase: Viewing an instrument with extraneous parameters.
+
+```
+mTracker$main> view 8 10
+```
+
+Expected: It should return only the 8th instrument in the list ignoring the value `10`.
+
+**Loading storage file testing**
+
+To test the program against corruption of saved file data, there are a few test cases you can try:
+1. Testcase: In the saved file on a newline write `This is a fake instrument`.
+
+ Expected: It should say that incorrect instrument type is provided and that instrument would be ignored.
+
+
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..64b6ae822b 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,6 +1,13 @@
-# Duke
+# mTracker
-{Give product intro here}
+
+
+mTracker is a **command-line based trading journal interface** that **allows
+investors and traders to store and view important trading related information** on their
+shortlisted financial instruments for **reference and decision-making**. It **summarises
+key financial details** into an **easy-to-read format and provides convenient lookups for trade setups**
+for busy individuals. The instruments currently supported by the product are Stocks, ETFs,
+Forex, and Cryptocurrencies.
Useful links:
* [User Guide](UserGuide.md)
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index abd9fbe891..4c82665bd2 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,42 +1,585 @@
-# User Guide
+# mTracker User Guide
+
## Introduction
-{Give a product intro}
+mTracker is a **command-line based trading journal interface** that **allows
+investors and traders to store and view important trading related information** on their
+shortlisted financial instruments for **reference and decision-making**. It **summarises
+key financial details** into an **easy-to-read format and provides convenient lookups for trade setups**
+for busy individuals. The instruments currently supported by the product are Stocks, ETFs,
+ Forex, and Cryptocurrencies.
-## Quick Start
+This user guide explains the key features of mTracker and includes examples of
+their usages. The contents table below includes clickable hyperlinks for you to
+navigate directly to a section of your preference.
-{Give steps to get started quickly}
+The _**Quick Start**_ section provides you with the instructions to download the product, and the necessary software specifications
+needed so that you can start using mTracker on the go! The _**FAQ**_ section towards the
+end of this guide answers possible queries that you may have of this product, and the
+eventual _**Command Summary**_ section gives a brief overview of the different types of
+command-line commands mTracker intakes to execute the various functionalities.
-1. Ensure that you have Java 11 or above installed.
-1. Down the latest version of `Duke` from [here](http://link.to/duke).
+## _Contents_
+* [1.0 Quick Start](#10-quick-start)
+* [2.0 Usage](#20-usage)
+ * [Notes on command format](#notes-on-command-format)
+ * [2.1.0 Add a new instrument: `add`](#210-adding-a-new-instrument-add)
+ * [2.1.1 Add a new stock: `stock`](#211-adding-a-new-stock)
+ * [2.1.2 Add a new crypto: `crypto`](#212-add-a-new-crypto)
+ * [2.1.3 Add a new etf: `etf`](#213-adding-a-new-etf)
+ * [2.1.4 Add a new forex: `forex`](#214-adding-a-new-forex)
+ * [2.2.0 List all instruments: `list`](#220-displaying-general-info-of-all-instruments-added-list)
+ * [2.3.0 View instrument info: `view`](#230-viewing-more-info-recorded-for-an-instrument-view)
+ * [2.4.0 Mark an instrument done: `done`](#240-marking-a-setup-as-acted-upon-done)
+ * [2.5.0 Edit an instrument: `edit`](#250-editing-an-instrument-edit)
+ * [2.6.0 Abort an operation: `abort`](#260-aborting-an-operation-abort)
+ * [2.7.0 Remove an instrument: `delete`](#270-removing-an-instrument-record-delete)
+ * [2.8.0 Search for an instrument: `find`](#280-search-for-instruments-in-watchlist-find)
+ * [2.9.0 Exit the application : `bye`](#290-exiting-the-bot-bye)
+* [3.0 FAQ](#30-faq)
+* [4.0 Command Summary](#40-command-summary)
-## Features
-{Give detailed description of each feature}
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
+## 1.0 Quick Start
-Format: `todo n/TODO_NAME d/DEADLINE`
+1) Ensure that you have Java `11` installed on your computer.
+2) Download the latest version of `mTracker` from [here](https://github.com/AY2122S1-CS2113T-T12-1/tp/releases).
+3) Copy the jar file to a folder that you want to run mTracker from.
+4) At the folder where you copied the jar file run the command `java -jar mTracker.jar` in terminal.
+5) If mTracker starts successfully, you should see the following greeting:
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+```
+________________________________________________________________________________
+ _________ __
+ | _ _ | [ | _
+ _ .--..--.|_/ | | \_| .--. ,--. .---. | | / ] .---. _ .--.
+[ `.-. .-. | | | [ `/'`\]`'_\ : / /'`\] | '' < / /__\\[ `/'`\]
+ | | | | | | _| |_ | | /| | |,| \__. | |`\ \| \__., | |
+[___||__||__]|_____|[___] \'-;__/'.___.'[__| \_]'.__.'[___]
-Example of usage:
+Hello! I am mTracker, your personal assistant bot that
+helps you keep track of the markets.
+What should I do for you now?
+________________________________________________________________________________
+```
-`todo n/Write the rest of the User Guide d/next week`
+## 2.0 Usage
+### *Notes on command format*
+> * Words in `UPPER_CASE` represent the parameters to be supplied by user. This is the list of parameters
+>that would often be referred to throughout this user guide.
+> * `TYPE` represents the type of the instrument. mTracker currently
+ supports 4 different types of instruments: `stock`, `etf`, `crypto` and `forex`.
+> * `INDEX` represents position index at which the instrument appears in the displayed list.
+> * For example, the first instrument in the list would have a position index of 1 while
+> the 3rd instrument in the list would have a
+>position index of 3.
+> * `SEARCH_STRING` represents the phrase or word user would like to search for in the watchlist with the `find` command.
+>* Extraneous parameters for all commands would be ignored.
+> * For example, the command `bye 123`
+> would be interpreted as `bye`.
+> * The command `view 2 5` will be interpreted as `view 2`, so financial details of the second item in the watchlist
+> will be printed out.
+>* Instrument attributes that require fixed inputs and all commands are case-insensitive.
+ > * For example, the command `ADd` would be interpreted as `add`.
+ > * The `sentiment` input `POsiTive` would be accepted as `positive`.
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
+### 2.1.0 Adding a new instrument: `add`
+Adds a new instrument to the watchlist of instruments. If at any point in time you want to terminate the `add`
+process, type in `abort` without any additional characters and mTracker will bring you back to the main workspace.
-## FAQ
+**Example usage**
+```
+mTracker$main> add
+```
+
+**Expected outcome**
+
+Upon entering the `add` command, mTracker prompts for the type of
+instrument to be added:
+
+```
+mTracker$main> add
+ Please key in the type of instrument:
+mTracker$add> TYPE
+```
+
+After the desired instrument type is input, instruction prompts would be displayed to guide you through the process of
+adding the new instrument. They are explained for the respective instrument types in the following sections below.
+
+### *2.1.1 Adding a new `stock`*
+After keying in `stock` as the type of instrument, mTracker prompts for the following parameters:
+* `Name` Name of the stock. Empty name is not allowed.
+* `Current price` Current price of the stock. Requires a positive number.
+* `Sentiment` Sentiment of user towards the stock. Only accepts `positive`, `neutral` and `negative`.
+* `Remarks` Any additional optional remarks about the stock that the user would like to record.
+
+**Example usage**
+```
+mTracker$main> add
+ Please key in the type of instrument:
+mTracker$add> stock
+ Name of stock:
+mTracker$add> IBM
+ Current Price:
+mTracker$add> 144.61
+ Sentiment for instrument:
+mTracker$add> positive
+ Remarks (optional):
+mTracker$add>
+```
+
+**Expected outcome**
+
+By following the instructions above, a new stock would be added, and an acknowledgement message would appear.
+Following the usage example above would produce the following message:
+```
+ [S][ ] IBM; 144.61; positive - has been added to list.
+```
+
+_**Note: If any of the non-optional parameters `Name`, `Current price` and `Sentiment` are provided with invalid
+inputs, mTracker would prompt you once again to give a valid input.**_
+
+### *2.1.2 Add a new `crypto`*
+After keying in `crypto` as the type of instrument, mTracker prompts for the following parameters:
+* `Name` Name of the crypto. Empty name is not allowed.
+* `Current Price` Current price of the crypto. Requires a positive number.
+* `Sentiment` Sentiment of the crypto. Only accepts `positive`, `neutral` and `negative`.
+* `Expiry` The date by which this trade setup should be executed. Date only in the `YYYY-MM-DD` format is allowed, and it should be a date in the future.
+* `Remarks` Any additional optional remarks about the cryptocurrency the user would like record.
+
+**Example usage**
+
+```
+mTracker$main> add
+ Please key in the type of instrument:
+mTracker$add> crypto
+ Name of crypto:
+mTracker$add> bitcoin
+ Current Price:
+mTracker$add> 14442.22
+ Sentiment for instrument:
+mTracker$add> positive
+ Expiry (YYYY-MM-DD):
+mTracker$add> 2021-12-14
+ Remarks (optional):
+mTracker$add>
+```
+
+**Expected outcome**
+
+By following the instructions above, a new crypto would be added and an acknowledgement message would appear.
+Following the usage example above we would see the following message:
+
+```
+ [C][ ] bitcoin; 14442.22; positive - has been added to list.
+```
+
+_**Note: If any of the non-optional parameters `Name`, `Current price`, `Sentiment` and `Expiry` are provided with invalid
+inputs, you would be prompted to give a valid input.**_
+
+### *2.1.3 Adding a new `etf`*
+
+An exchange-traded fund (etf) is a security that tracks an index, sector, commodity, or any
+other asset. After keying in `etf` as the type of instrument, mTracker prompts for the following parameters:
+* `Name` Name of the etf. Empty name is not allowed.
+* `Current price` Current price of the etf. Requires a positive number.
+* `Sentiment` Sentiment of user towards the etf. Only accepts `positive`, `neutral` and `negative`.
+* `Past returns` Optional input for past returns of the etf.
+* `Remarks` Any additional optional remarks about the etf that the user would like to record.
+
+**Example usage**
+```
+mTracker$main> add
+ Please key in the type of instrument:
+mTracker$add> etf
+ Name of etf:
+mTracker$add> SPY
+ Current Price:
+mTracker$add> 445.87
+ Sentiment for instrument:
+mTracker$add> positive
+ Past Returns (optional):
+mTracker$add> 1200
+ Remarks (optional):
+mTracker$add> Prices will plateau out in a few days.
+```
+**Expected outcome**
+
+When the above ETF is added successfully, the following acknowledgement will be printed
+out:
+```
+ [E][ ] SPY; 445.87; positive - has been added to list.
+```
+
+_**Note: If any of the non-optional parameters `Name`, `Current price` and `Sentiment` are provided with invalid
+inputs, you would be prompted to give a valid input.**_
+
+### *2.1.4 Adding a new `forex`*
+Bilateral currency pairs, known as forex pairs, are traded in the currency market
+and mTracker provides the ability to add forex pairs to its watchlist.
+After keying in `forex` as the type of instrument, mTracker prompts for the
+following parameters:
+* `Name` Name of forex. Empty name is not allowed and forex pairs' names should be
+ 6 letters long. (Eg. AUDUSD instead of AUD USD, or Australian dollar-US dollar, or AUD/USD)
+* `Current Price` Current price of the etf. Requires a positive number.
+* `Sentiment` Sentiment of user towards the etf. Only accepts `positive`, `neutral` and `negative`.
+* `Entry Price` Price at which to open an order for the forex pair.
+* `Exit Price` Price at which to close the order.
+* `Expiry` The date by which this trade setup should be executed. Dates only in the `YYYY-MM-DD` format and in the future are allowed.
+* `Remarks` Any additional optional remarks about the forex that the user would like to record.
+ (Eg. trade deficits between countries, FOMC meeting dates,
+ interest rates outlook in currency's home country, etc.)
+
+**Example usage**
+
+```
+mTracker$main> add
+ Please key in the type of instrument:
+mTracker$add> forex
+ Name of forex:
+mTracker$add> USDJPY
+ Current Price:
+mTracker$add> 114.289
+ Sentiment for instrument:
+mTracker$add> negative
+ Entry Price:
+mTracker$add> 114.20
+ Exit Price:
+mTracker$add> 110.0
+ Expiry (YYYY-MM-DD):
+mTracker$add> 2021-12-13
+ Remarks (optional):
+mTracker$add> USD is losing momentum. Technical levels are holding firm.
+```
+**Expected outcome**
+
+When the above forex pair is added successfully, the following acknowledgement will be printed
+out:
+```
+ [F][ ] USDJPY; 114.289; negative - has been added to list.
+```
+
+_**Note: If any of the non-optional parameters `Name`, `Current price`, `Sentiment`, `Entry Price`, `Exit Price`,
+and `Expiry` are provided with invalid
+inputs, you would be prompted to give a valid input.**_
+
+### 2.2.0 Displaying general info of all instruments added: `list`
+
+Displays the 3 general parameters of `Name`, `Current Price`,
+and `Sentiment` for all instruments in an easy-to-view format, alongside
+their execution status marked by an `[X]`.
+
+**Example usage**
+
+```
+mTracker$main> list
+```
+
+**Expected outcome**
+
+```
+________________________________________________________________________________
+CURRENT WATCHLIST
+1) [C][X] bitcoin; 14442.22; positive
+2) [E][ ] SPY; 445.87; positive
+3) [F][ ] USDJPY; 114.289; negative
+________________________________________________________________________________
+```
+
+### 2.3.0 Viewing more info recorded for an instrument: `view`
+
+Since there are many different parameters that are recorded for
+various instruments, to view all the recorded information for an
+instrument apart from the general information presented
+by the output of the `list` command, the `view` command can be used.
+
+**Format**
+
+```
+mTracker$main> view INDEX
+```
+
+**Example usage**
+
+```
+mTracker$main> view 2
+```
+
+**Expected outcome**
+
+All the parameters for the instrument at the requested index
+will be printed out:
+
+```
+________________________________________________________________________________
+Type: Etf [ ]
+Name: SPY
+Current Price: 445.87
+Sentiment: positive
+Past Returns: 1200.0
+Remarks: Prices will plateau out in a few days.
+________________________________________________________________________________
+```
+
+### 2.4.0 Marking a setup as acted upon: `done`
+
+Mark a particular record of an instrument in the watchlist as executed or acted upon.
+
+**Format**
+
+```
+mTracker$main> done INDEX
+```
+
+**Example usage**
+```
+mTracker$main> done 1
+```
+
+**Expected output**
+
+Marks the first instrument in watchlist as complete,
+and the following confirmation message will be displayed:
+
+```
+ Nice! I have marked this instrument as completed:
+ [S][X] IBM; 144.61; positive
+```
+
+In this example, the first instrument is `IBM` so it has been
+checked as complete with an 'X'.
+
+### 2.5.0 Editing an instrument: `edit`
+Edit an existing instrument parameters. If at any point in time you want to terminate the `edit`
+process, type in `abort` without any additional characters and mTracker will bring you back to the main workspace.
+
+**Format**
+```
+mTracker$main> edit INDEX
+```
+**Example usage**
+
+```
+mTracker$main> edit 2
+```
+
+**Expected outcome:**
+
+Upon entering the `edit` command, mTracker prompts for the parameters of
+instrument to be edited:
+
+```
+mTracker$main> edit 2
+ Please enter one or more Etf parameters to edit separated by a single space only.
+ done-status, name, current-price, sentiment, past-returns, remarks
+mTracker$edit> name current-price
+ Enter new name:
+mTracker$edit> XLF
+ Enter new Current price:
+mTracker$edit> 148.76
+```
+
+_**Note: If unknown/invalid parameters are input when mTracker prompts for the parameters to edit,
+they will be ignored.**_
+
+_**Note: If any of the non-optional parameters of the instrument being edited are provided with invalid
+inputs, you would be prompted to give a valid input. See adding of the respective instrument for its non-optional parameters.**_
+
+Then, it prints out the changes that have been made:
+
+```
+________________________________________________________________________________
+Before:
+Type: Etf [ ]
+Name: SPY
+Current Price: 445.87
+Sentiment: positive
+Past Returns: 1200.0
+Remarks: Prices will plateau out in a few days.
+________________________________________________________________________________
+Changed To:
+Type: Etf [ ]
+Name: XLF
+Current Price: 148.76
+Sentiment: positive
+Past Returns: 1200.0
+Remarks: Prices will plateau out in a few days.
+________________________________________________________________________________
+```
+
+### 2.6.0 Aborting an operation: `abort`
+
+Abort a current process. If you want to terminate the `add` or `edit` operation at any stage, enter `abort`
+and the operation will be terminated.
+
+**Note:** `abort` is case-insensitive but must be entered without any additional characters. For example,
+`abort/` will be an invalid input.
+
+Aborting an Add Operation:
+
+**Example usage**
+```
+mTracker$add> abort
+```
+**Expected outcome:**
+
+```
+mTracker$main> add
+ Please key in the type of instrument:
+mTracker$add> stock
+ Name of stock:
+mTracker$add> abort
+Addition of new instrument has been aborted! You are in the main workspace now.
+```
+
+Aborting an Edit Operation:
+
+**Example usage**
+
+```
+mTracker$edit> abort
+```
+
+**Expected outcome:**
+
+```
+mTracker$main> edit 1
+ Please enter one or more Stock parameters to edit separated by a single space only.
+ done-status, name, current-price, sentiment, remarks
+mTracker$edit> name current-price
+ Enter new name:
+mTracker$edit> abort
+Edit process has been aborted! You are in the main workspace now.
+```
+
+_**Note: You can use this feature to exit an add or edit process. Calling abort in any other functionality will
+result in an error message being displayed.**_
+
+### 2.7.0 Removing an instrument record: `delete`
+
+You can remove an instrument from the watchlist with
+the index number of the instrument in the watchlist.
+
+**Format**
+
+```
+mTracker$main> delete INDEX
+```
+
+**Example usage**
+
+```
+mTracker$main> delete 4
+```
+
+**Expected outcome**
+
+The 4th instrument in the watchlist is removed,
+and the remaining instruments' index numbers are updated accordingly.
+
+```
+________________________________________________________________________________
+Noted. [F][ ] USDJPY; 114.289; negative - removed from your watchlist
+________________________________________________________________________________
+```
+
+You can key in `list` once again to view the latest watchlist.
+
+### 2.8.0 Search for instruments in watchlist: `find`
+
+You can find specific instruments in the watchlist by searching for them through `find` command.
+
+**Format**
+
+```
+mTracker$main> find SEARCH_STRING
+```
+
+**Example usage**
+
+```
+mTracker$main> find coin
+```
+
+**Expected outcome:**
+
+Any instrument name which contains the SEARCH_STRING will be returned:
+
+```
+________________________________________________________________________________
+1) [C][ ] bitcoin; 14442.22; positive
+2) [C][ ] dogecoin; 21.14; positive
+There were 2 instrument(s) found for keyword, coin.
+________________________________________________________________________________
+```
+
+In the event that there were no instruments found for the input
+SEARCH_STRING, the following message will be printed out:
+
+```
+________________________________________________________________________________
+There were no instruments found for keyword, JPY.
+________________________________________________________________________________
+```
+
+_**Note: SEARCH_STRING is case-sensitive.**_
+
+### 2.9.0 Exiting the bot: `bye`
+When you wish to quit the mTracker program, simply type in `bye`.
+
+**Example usage**
+
+```
+mTracker$main> bye
+```
+
+**Expected outcome**
+
+```
+ ______ _______ _
+( ___ \ |\ /|( ____ \( )
+| ( ) )( \ / )| ( \/| |
+| (__/ / \ (_) / | (__ | |
+| __ ( \ / | __) | |
+| ( \ \ ) ( | ( (_)
+| )___) ) | | | (____/| _
+|/ \___/ \_/ (_______/(_)
+Thank you for using mTracker.
+MAY THE MARKETS BE WITH YOU!!!
+```
+
+_**Note: Once quit, the instruments created during session
+will be stored and retrieved back by mTracker once it is relaunched.**_
+
+## 3.0 FAQ
**Q**: How do I transfer my data to another computer?
-**A**: {your answer here}
+**A**: Locate text file named `mTracker.txt` and transfer the file over to another computer.
+
+**Q**: Can I edit the data without launching the mTracker?
+
+**A**: You can open the `mTracker.txt` file located in the same directory as the jar file
+to edit/add instruments manually. However, please make sure that each line
+contains details of only one instrument.
+
+**Q**: Can I add multiple instruments in one command?
-## Command Summary
+**A**: No, most commands are designed to handle one instrument at a time. As with most commands,
+the add feature only allows adding of 1 instrument at a time.
-{Give a 'cheat sheet' of commands here}
+## 4.0 Command Summary
-* Add todo `todo n/TODO_NAME d/DEADLINE`
+Action | Format | Examples
+ --------- | ------ |------
+Add an instrument | `add` | Read [2.1.0 Add a new instrument: `add`](#210-adding-a-new-instrument-add) for detailed instructions and examples.
+List all instruments in watchlist | `list` | `mTracker$main> list` prints out all instruments in watchlist, and their respective general parameters' information.
+View all info of an instrument | `view INDEX` | `mTracker$main> view 1` prints out all financial details recorded for the first instrument in watchlist.
+Mark an instrument's trade setup as completed | `done INDEX` | `mTracker$main> done 2` marks second instrument in watchlist as acted upon.
+Edit details recorded for an instrument | `edit INDEX` | Read [2.5.0 Edit an instrument: `edit`](#250-editing-an-instrument-edit) for detailed instructions and examples.
+Abort an operation | `abort`| Read [2.6.0 Abort an operation: `abort`](#260-aborting-an-operation-abort) for detailed instructions and examples.
+Delete an instrument from watchlist | `delete INDEX` | `mTracker$main> delete 5` deletes the fifth instrument in watchlist.
+Search for recorded instrument(s) | `find SEARCH_STRING` | `mTracker$main> find USD` returns all financial instruments in watchlist that contain "USD". **(Note: SEARCH_STRING is case-sensitive.)**
+Exit program | `bye` | `mTracker$main> bye` prints out farewell message and program ends.
diff --git a/docs/diagrams/AddStockSequenceDiagram.puml b/docs/diagrams/AddStockSequenceDiagram.puml
new file mode 100644
index 0000000000..deecffdfbf
--- /dev/null
+++ b/docs/diagrams/AddStockSequenceDiagram.puml
@@ -0,0 +1,96 @@
+@startuml
+!include Styles.puml
+
+box console COLOR_CONSOLE_SEGMENT
+participant ":InputParser" as InputParser COLOR_CONSOLE
+participant "<>\nInputParser" as ClassInputParser COLOR_CONSOLE
+participant "<>\nAddInstrumentParser" as AddInstrumentParser COLOR_CONSOLE
+participant ":AddStockParser" as AddStockParser COLOR_CONSOLE
+participant ":AddStockCommand" as AddStockCommand COLOR_COMMANDS
+end box
+
+box commands COLOR_FAINT_GREEN
+participant ":AddStockCommand" as AddStockCommand COLOR_FAINT_GREEN
+end box
+
+box error COLOR_ERROR_SEGMENT
+participant ":InvalidInstrumentError" as InvalidInstrumentError COLOR_ERROR
+end box
+box ui COLOR_LIGHT_BLUE
+participant "<>\nTextUi" as TextUi COLOR_UI
+end box
+
+
+[-> InputParser : filterByCommandType({"add"}, instruments)
+activate InputParser
+
+InputParser -> InputParser : getAddInstrumentParameters()
+activate InputParser
+
+ loop until user enters a valid instrument type or aborts
+ InputParser -> TextUi : displayAddInstrumentFirstInstruction()
+ activate TextUi
+
+ TextUi --> InputParser
+ deactivate TextUi
+ InputParser -> ClassInputParser : getUserInput("add")
+ activate ClassInputParser
+ ClassInputParser --> InputParser : return instrumentType
+ deactivate ClassInputParser
+ end loop
+
+InputParser -> AddInstrumentParser : filterByInstrumentType({"stock"})
+activate AddInstrumentParser
+
+note right
+The parameter "stock" was
+given by the user after
+displayAddInstrumentFirstInstruction().
+end note
+alt Instrument type is stock/crypto/forex/etf
+ create AddStockParser
+ AddInstrumentParser -> AddStockParser : AddStockParser()
+ activate AddStockParser
+
+ AddStockParser --> AddInstrumentParser
+ deactivate AddStockParser
+
+ AddInstrumentParser -> AddStockParser : initParameters()
+ activate AddStockParser
+ AddStockParser --> AddInstrumentParser
+ deactivate AddStockParser
+
+ ref over AddInstrumentParser, AddStockParser, AddStockCommand
+ Get stock details from user and create the AddStockCommand
+ endref
+ note bottom
+ Displaying
+ only the
+ outcome
+ of adding
+ a type
+ stock. This
+ is suppose
+ to be a
+ switch
+ statement
+ amongst
+ the 4
+ different
+ instruments
+ end note
+else
+ create InvalidInstrumentError
+ AddInstrumentParser -> InvalidInstrumentError : InvalidInstrumentError()
+ activate InvalidInstrumentError
+ InvalidInstrumentError --> AddInstrumentParser
+ deactivate InvalidInstrumentError
+end
+AddInstrumentParser --> InputParser : return command
+deactivate AddInstrumentParser
+InputParser --> InputParser : return command
+deactivate InputParser
+[<-- InputParser : return command
+deactivate InputParser
+
+@enduml
diff --git a/docs/diagrams/AddStockSequenceDiagramRef.puml b/docs/diagrams/AddStockSequenceDiagramRef.puml
new file mode 100644
index 0000000000..3ced9bb9da
--- /dev/null
+++ b/docs/diagrams/AddStockSequenceDiagramRef.puml
@@ -0,0 +1,57 @@
+@startuml
+!include Styles.puml
+
+group sd Get stock details from user and create the AddStockCommand
+
+box console COLOR_CONSOLE_SEGMENT
+participant "<>\nAddInstrumentParser" as AddInstrumentParser COLOR_CONSOLE
+participant ":AddStockParser" as AddStockParser COLOR_CONSOLE
+participant "<>\nAddStockParser" as ClassAddStockParser COLOR_CONSOLE
+end box
+
+box command COLOR_FAINT_GREEN
+participant ":AddStockCommand" as AddStockCommand COLOR_COMMANDS
+end box
+activate AddInstrumentParser
+
+AddInstrumentParser-> AddStockParser : getInstrumentParameters()
+activate AddStockParser
+AddStockParser->ClassAddStockParser : getGeneralParameters("stock")
+activate ClassAddStockParser
+ClassAddStockParser--> AddStockParser
+deactivate ClassAddStockParser
+
+
+note right
+Both getGeneralParameters() and
+getStockSpecificParameters()
+would prompt the user to input details for every
+attribute to store as parameters.
+end note
+
+AddStockParser -> AddStockParser : getStockSpecificParameters()
+activate AddStockParser
+AddStockParser --> AddStockParser
+deactivate AddStockParser
+
+create AddStockCommand
+AddStockParser -> AddStockCommand : AddStockCommand()
+activate AddStockCommand
+AddStockCommand --> AddStockParser
+deactivate AddStockCommand
+
+AddInstrumentParser<--AddStockParser : return command
+deactivate AddStockParser
+
+AddInstrumentParser->AddStockParser : getParameters()
+activate AddStockParser
+AddInstrumentParser<--AddStockParser : return parameters
+deactivate AddStockParser
+
+AddInstrumentParser-> AddStockCommand : setParams(parameters)
+activate AddStockCommand
+AddInstrumentParser<--AddStockCommand
+deactivate AddStockCommand
+
+end
+@enduml
diff --git a/docs/diagrams/AddStockSequenceExecuteDiagram.puml b/docs/diagrams/AddStockSequenceExecuteDiagram.puml
new file mode 100644
index 0000000000..5c456d93ae
--- /dev/null
+++ b/docs/diagrams/AddStockSequenceExecuteDiagram.puml
@@ -0,0 +1,43 @@
+@startuml
+!include Styles.puml
+
+box commands COLOR_FAINT_GREEN
+participant ":AddStockCommand" as AddStockCommand COLOR_COMMANDS
+end box
+
+box model COLOR_MODEL_SEGMENT
+participant ":InstrumentManager" as InstrumentManager COLOR_MODEL
+end box
+
+box ui COLOR_LIGHT_BLUE
+participant "<>\nTextUi" as TextUi COLOR_UI
+end box
+
+[-> AddStockCommand : execute()
+activate AddStockCommand
+AddStockCommand -> AddStockCommand : setAddGeneralParameters();
+activate AddStockCommand
+AddStockCommand --> AddStockCommand
+deactivate AddStockCommand
+AddStockCommand -> AddStockCommand : setStockParameters();
+activate AddStockCommand
+AddStockCommand --> AddStockCommand
+deactivate AddStockCommand
+AddStockCommand -> AddStockCommand : createNewStock();
+activate AddStockCommand
+AddStockCommand --> AddStockCommand
+deactivate AddStockCommand
+AddStockCommand -> InstrumentManager : addInstrument(newInstrument);
+activate InstrumentManager
+InstrumentManager --> AddStockCommand
+deactivate InstrumentManager
+
+AddStockCommand -> TextUi : displayInstrumentAdded(newInstrument);
+activate TextUi
+TextUi --> AddStockCommand
+deactivate TextUi
+[<-- AddStockCommand : return "stock"
+deactivate AddStockCommand
+
+
+@enduml
diff --git a/docs/diagrams/ArchitectureDiagram.puml b/docs/diagrams/ArchitectureDiagram.puml
new file mode 100644
index 0000000000..8abdaa473d
--- /dev/null
+++ b/docs/diagrams/ArchitectureDiagram.puml
@@ -0,0 +1,33 @@
+@startuml
+!include Styles.puml
+!include
+
+hide members
+
+Class "<$user>" as User COLOR_USER
+
+Package " "<>{
+ Class ui COLOR_UI
+ Class main COLOR_MAIN
+ Class commands COLOR_COMMANDS
+ Class console COLOR_CONSOLE
+ Class model COLOR_MODEL
+ Class filemanager COLOR_STORAGE
+ Class commons COLOR_COMMONS
+
+
+main -down[COLOR_MAIN]-> filemanager
+main -down[COLOR_MAIN]-> console
+main -down[COLOR_MAIN]-> model
+main .down[COLOR_MAIN]-> commands
+main .down[COLOR_MAIN]-> ui
+filemanager .down[COLOR_STORAGE]->ui
+filemanager .down[COLOR_STORAGE]->model
+console .down[COLOR_CONSOLE]->ui
+console .down[COLOR_CONSOLE]->model
+console .down[COLOR_CONSOLE]->commands
+commands .down[COLOR_COMMANDS]->ui
+commands -down[COLOR_COMMANDS]->model
+ui .down[COLOR_UI]->model
+User ..[COLOR_USER]> ui
+@enduml
diff --git a/docs/diagrams/Colors.puml b/docs/diagrams/Colors.puml
new file mode 100644
index 0000000000..37c9ecdd39
--- /dev/null
+++ b/docs/diagrams/Colors.puml
@@ -0,0 +1,20 @@
+!define COLOR_WHITE #FFFFFF
+!define COLOR_SILVER #C0C0C0
+!define COLOR_GREY #A5A8AC
+!define COLOR_BLACK #000000
+!define COLOR_BROWN #800000
+!define COLOR_RED #FF0000
+!define COLOR_YELLOW #FFFF00
+!define COLOR_BRIGHT_GREEN #00FF00
+!define COLOR_DARK_GREEN #008000
+!define COLOR_LIGHT_BLUE #00FFFF
+!define COLOR_TEAL #008080
+!define COLOR_DARK_BLUE #0000FF
+!define COLOR_PINK #FF00FF
+!define COLOR_PURPLE #800080
+!define COLOR_FAINT_TEAL #BBF1F1
+!define COLOR_FAINT_GREEN #C2F5CC
+!define COLOR_LIGHT_PURPLE #CBC3E3
+!define COLOR_LIGHT_BROWN #C89D7C
+!define COLOR_ORANGE #CF5300
+!define COLOR_FAINT_ORANGE #FFD580
diff --git a/docs/diagrams/CommandsDiagram.puml b/docs/diagrams/CommandsDiagram.puml
new file mode 100644
index 0000000000..d0e3628698
--- /dev/null
+++ b/docs/diagrams/CommandsDiagram.puml
@@ -0,0 +1,91 @@
+@startuml
+!include Styles.puml
+
+Package "Commands " <>{
+ Class "{abstract}\nCommand" as Command COLOR_COMMANDS {
+ {method} + setData(InstrumentManager)
+ {method} + setParams(ArrayList)
+ {method} + execute() ABSTRACT: String
+ }
+ Class ViewCommand COLOR_COMMANDS {
+ {method} + execute(): String
+ }
+ Class EditInstrumentCommand COLOR_COMMANDS {
+ {method} + execute(): String
+ }
+ Class DoneCommand COLOR_COMMANDS {
+ {method} + execute(): String
+ }
+ Class DeleteCommand COLOR_COMMANDS {
+ {method} + execute(): String
+ }
+
+ Class InvalidCommand COLOR_COMMANDS {
+ {method} + execute(): String
+ }
+ Class ABCCommand COLOR_COMMANDS {
+ {method} + execute(): String
+ }
+ Class AddInstrumentCommand COLOR_COMMANDS {
+ {method} + execute(): String
+ }
+ Class "{abstract}\nIndexedCommand" as IndexedCommand COLOR_COMMANDS {
+ {method} + execute() ABSTRACT: String
+ }
+
+ Class AddXYZCommand COLOR_COMMANDS {
+ {method} + execute(): String
+ }
+}
+
+
+Class InstrumentManager COLOR_MODEL {
+ {method} + getSize(): int
+ {method} + getInstruments(): ArrayList
+ {method} + getInstrument(int): Instrument
+ {method} + addInstrument(Instrument): Instrument
+ {method} + findInstruments(String): ArrayList
+ {method} + deleteInstrument(int)
+ {method} + editInstrument(int, Hashmap)
+}
+
+Class TextUi COLOR_UI
+
+show Command members
+show InstrumentManager members
+show AddInstrumentCommand members
+show IndexedCommand members
+show InvalidCommand members
+show DeleteCommand members
+show DoneCommand members
+show EditInstrumentCommand members
+show ViewCommand members
+show ABCCommand members
+show AddXYZCommand members
+
+InvalidCommand -[COLOR_COMMANDS]-|>Command
+Command -left[COLOR_COMMANDS]->"1" InstrumentManager
+
+note right of ABCCommand: ABCCommand = \nExitCommand, \nFindCommand, \nListCommand
+
+ABCCommand -[COLOR_COMMANDS]-|>Command
+AddInstrumentCommand -up[COLOR_COMMANDS]-|>Command
+IndexedCommand -up[COLOR_COMMANDS]-|>Command
+
+AddXYZCommand -up[COLOR_COMMANDS]-|>AddInstrumentCommand
+
+EditInstrumentCommand -left[COLOR_COMMANDS]-|>IndexedCommand
+ViewCommand -[COLOR_COMMANDS]-|>IndexedCommand
+DoneCommand -down[COLOR_COMMANDS]-|>IndexedCommand
+DeleteCommand -up[COLOR_COMMANDS]-|>IndexedCommand
+
+AddXYZCommand -[COLOR_COMMANDS].>TextUi
+ABCCommand -[COLOR_COMMANDS].>TextUi
+
+DeleteCommand -[COLOR_COMMANDS].>TextUi
+DoneCommand -[COLOR_COMMANDS].>TextUi
+EditInstrumentCommand -[COLOR_COMMANDS].>TextUi
+ViewCommand -[COLOR_COMMANDS].>TextUi
+
+
+@enduml
diff --git a/docs/diagrams/ConsoleDiagram.puml b/docs/diagrams/ConsoleDiagram.puml
new file mode 100644
index 0000000000..9099801622
--- /dev/null
+++ b/docs/diagrams/ConsoleDiagram.puml
@@ -0,0 +1,59 @@
+@startuml
+!include Styles.puml
+
+Class Mtracker COLOR_MAIN
+
+Package "console "<>{
+ Class InputParser COLOR_CONSOLE {
+ + filterByCommandType(String[], ArrayList): Command
+ }
+ Class "{abstract}\nAddInstrumentParser" as AddInstrumentParser COLOR_CONSOLE {
+ + {static} filterByInstrumentType(String[]): AddInstrumentCommand
+ }
+ Class AddXYZParser COLOR_CONSOLE {
+ + getInstrumentParameters(): AddXYZCommand
+ }
+ Class EditInstrumentParser COLOR_CONSOLE {
+ + getEditedParameters(HashSet, Instrument)
+ + createEditCommand(HashSet, Instrument, int): EditInstrumentCommand
+ }
+}
+
+show InputParser members
+show AddInstrumentParser members
+show AddXYZParser members
+show EditInstrumentParser members
+
+Class "{abstract}\nCommand" as Command COLOR_COMMANDS
+Class AddInstrumentCommand COLOR_COMMANDS
+Class AddXYZCommand COLOR_COMMANDS
+Class EditInstrumentCommand COLOR_COMMANDS
+Class "{abstract}\nIndexedCommand" as IndexedCommand COLOR_COMMANDS
+Class TextUi COLOR_UI
+Class "{abstract}\nInstrument" as Instrument COLOR_MODEL
+
+Mtracker -down[COLOR_MAIN]->"1" InputParser
+AddInstrumentParser -up[COLOR_CONSOLE]-|> InputParser
+AddXYZParser -up[COLOR_CONSOLE]-|> AddInstrumentParser
+EditInstrumentParser -left[COLOR_CONSOLE]-|> InputParser
+
+InputParser -down[COLOR_CONSOLE].> Command : creates >
+AddInstrumentParser -right[COLOR_CONSOLE].> AddInstrumentCommand : creates >
+AddXYZParser -right[COLOR_CONSOLE].> AddXYZCommand : creates >
+EditInstrumentParser -right[COLOR_CONSOLE].> EditInstrumentCommand : creates >
+InputParser -down[COLOR_CONSOLE].> TextUi
+AddInstrumentParser -left[COLOR_CONSOLE].> TextUi
+AddXYZParser -left[COLOR_CONSOLE].> TextUi
+EditInstrumentParser -down[COLOR_CONSOLE].> TextUi
+
+InputParser -left[COLOR_CONSOLE].> Instrument
+EditInstrumentParser -right[COLOR_CONSOLE].> Instrument
+
+note bottom of AddXYZParser: AddXYZParser = AddCryptoParser, \nAddEtfParser, etc
+
+EditInstrumentCommand -down[COLOR_COMMANDS]-|> IndexedCommand
+IndexedCommand -up[COLOR_COMMANDS]-|> Command
+AddXYZCommand -up[COLOR_COMMANDS]-|> AddInstrumentCommand
+AddInstrumentCommand -up[COLOR_COMMANDS]-|> Command
+note bottom of AddXYZCommand: AddXYZCommand = AddCryptoCommand, \nAddEtfCommand, etc
+@enduml
diff --git a/docs/diagrams/DoneCryptoExecuteDiagram.puml b/docs/diagrams/DoneCryptoExecuteDiagram.puml
new file mode 100644
index 0000000000..0dc8f257f6
--- /dev/null
+++ b/docs/diagrams/DoneCryptoExecuteDiagram.puml
@@ -0,0 +1,47 @@
+@startuml
+!include Styles.puml
+
+group sd Mark instrument as done and displays it in the TextUi
+box command COLOR_FAINT_GREEN
+participant ":DoneCommand" as DoneCommand COLOR_COMMANDS
+end box
+
+box model COLOR_MODEL_SEGMENT
+participant ":InstrumentManager" as InstrumentManager COLOR_MODEL
+participant ":Instrument" as Instrument COLOR_MODEL
+end box
+
+box ui COLOR_LIGHT_BLUE
+participant "<>\nTextUi" as TextUi COLOR_UI
+end box
+
+activate DoneCommand
+DoneCommand-> DoneCommand: getInstrumentAtIndex()
+activate DoneCommand
+
+DoneCommand-> InstrumentManager: getInstrument(1)
+activate InstrumentManager
+InstrumentManager--> DoneCommand: return instrumentToComplete
+deactivate InstrumentManager
+DoneCommand--> DoneCommand: return instrumentToComplete
+deactivate DoneCommand
+
+DoneCommand-> Instrument: markAsDone()
+activate Instrument
+Instrument--> DoneCommand
+deactivate Instrument
+
+DoneCommand-> TextUi: displayDoneInstrument(instrument)
+activate TextUi
+
+TextUi-> Instrument: getGeneralParams()
+activate Instrument
+
+Instrument--> TextUi
+deactivate Instrument
+
+TextUi--> DoneCommand
+deactivate TextUi
+
+end
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/DoneCryptoSequenceDiagram.puml b/docs/diagrams/DoneCryptoSequenceDiagram.puml
new file mode 100644
index 0000000000..1624bf7860
--- /dev/null
+++ b/docs/diagrams/DoneCryptoSequenceDiagram.puml
@@ -0,0 +1,70 @@
+@startuml
+'https://plantuml.com/class-diagram
+!include Styles.puml
+
+box console COLOR_CONSOLE_SEGMENT
+participant ":InputParser" as InputParser COLOR_CONSOLE
+end box
+
+box command COLOR_FAINT_GREEN
+participant ":DoneCommand" as DoneCommand COLOR_COMMANDS
+end box
+
+box model COLOR_MODEL_SEGMENT
+participant ":InstrumentManager" as InstrumentManager COLOR_MODEL
+participant ":Instrument" as Instrument COLOR_MODEL
+end box
+
+box ui COLOR_LIGHT_BLUE
+participant "<>\nTextUi" as TextUi COLOR_UI
+end box
+
+
+[-> InputParser : filterByCommandType({"done", "1"}, instruments)
+activate InputParser
+
+InputParser ->InputParser: getDoneInstrumentCommand({"done", "1"}, instruments)
+activate InputParser
+
+create DoneCommand
+InputParser-> DoneCommand: DoneCommand()
+activate DoneCommand
+
+DoneCommand --> InputParser
+deactivate DoneCommand
+
+InputParser-> InputParser: getAndValidateIndex({"done", "1"}, instruments)
+activate InputParser
+InputParser--> InputParser
+deactivate InputParser
+
+InputParser-> InputParser: getAndValidateDoneStatus({"done", "1"}, instruments)
+activate InputParser
+InputParser--> InputParser
+deactivate InputParser
+
+InputParser->DoneCommand: setIndex(1)
+activate DoneCommand
+
+DoneCommand--> InputParser
+deactivate DoneCommand
+
+InputParser -->InputParser: return doneCommand
+
+deactivate InputParser
+[<--InputParser: return doneCommand
+deactivate InputParser
+
+[->DoneCommand:execute()
+activate DoneCommand
+
+ref over DoneCommand, InstrumentManager, Instrument, TextUi
+
+Mark instrument as done and displays it in the TextUi
+
+endref
+
+[<--DoneCommand : return "done"
+deactivate DoneCommand
+
+@enduml
diff --git a/docs/diagrams/EditExecuteRefrence.puml b/docs/diagrams/EditExecuteRefrence.puml
new file mode 100644
index 0000000000..b59e425efe
--- /dev/null
+++ b/docs/diagrams/EditExecuteRefrence.puml
@@ -0,0 +1,51 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+!include Styles.puml
+
+box model COLOR_MODEL_SEGMENT
+participant ":Stock" as Instrument COLOR_MODEL
+end box
+
+activate Instrument
+group sd check if parameter exist in HashMap and edit
+
+Instrument -> Instrument: editGeneralParameter({"name, new name"})
+activate Instrument
+
+Instrument -> Instrument: editName({"name, new name"})
+note left: editName checks if the key "name" exist in HashMap\nand if it exist, it updates the name parameter
+activate Instrument
+Instrument --> Instrument
+deactivate Instrument
+
+Instrument -> Instrument: editCurrentPrice({"name, new name"})
+activate Instrument
+Instrument --> Instrument
+deactivate Instrument
+
+Instrument -> Instrument: editSentiment({"name, new name"})
+activate Instrument
+Instrument --> Instrument
+deactivate Instrument
+
+note left: The rest of the edit methods are similar to editName.
+
+Instrument -> Instrument: editDoneStatus({"name, new name"})
+activate Instrument
+Instrument --> Instrument
+deactivate Instrument
+
+Instrument --> Instrument
+deactivate Instrument
+
+Instrument -> Instrument: editRemark({"name, new name"})
+activate Instrument
+
+Instrument --> Instrument
+deactivate Instrument
+
+end
+
+deactivate Instrument
+
+@enduml
diff --git a/docs/diagrams/EditExecuteSequenceDiagram.puml b/docs/diagrams/EditExecuteSequenceDiagram.puml
new file mode 100644
index 0000000000..82a6805849
--- /dev/null
+++ b/docs/diagrams/EditExecuteSequenceDiagram.puml
@@ -0,0 +1,74 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+!include Styles.puml
+
+box command COLOR_FAINT_GREEN
+participant ":EditInstrumentCommand" as EditInstrumentCommand COLOR_COMMANDS
+end box
+
+box model COLOR_MODEL_SEGMENT
+participant ":InstrumentManager" as InstrumentManager COLOR_MODEL
+participant ":Stock" as Instrument COLOR_MODEL
+end box
+
+box ui COLOR_LIGHT_BLUE
+participant "<>\n:TextUi" as TextUi COLOR_UI
+end box
+
+[->EditInstrumentCommand:execute()
+activate EditInstrumentCommand
+
+EditInstrumentCommand -> EditInstrumentCommand: getInstrumentAtIndex()
+activate EditInstrumentCommand
+EditInstrumentCommand-> InstrumentManager: getInstrument(1)
+activate InstrumentManager
+InstrumentManager--> EditInstrumentCommand: return instrumentToEdit
+deactivate InstrumentManager
+EditInstrumentCommand--> EditInstrumentCommand: return instrumentToEdit
+deactivate EditInstrumentCommand
+
+
+EditInstrumentCommand -> Instrument: getAllParams()
+activate Instrument
+Instrument --> EditInstrumentCommand: return parameters before edit
+deactivate Instrument
+
+EditInstrumentCommand-> InstrumentManager: editInstrument(1, {"name", "new name"})
+activate InstrumentManager
+
+InstrumentManager -> Instrument: editParameter({"name", "new name"})
+activate Instrument
+
+ref over Instrument
+check if
+stock
+parameters
+exist in
+HashMap
+and if it
+exists,
+update its
+parameters.
+endref
+
+Instrument --> InstrumentManager
+deactivate Instrument
+
+InstrumentManager --> EditInstrumentCommand
+deactivate InstrumentManager
+
+EditInstrumentCommand -> Instrument: getAllParams()
+activate Instrument
+Instrument --> EditInstrumentCommand: return parameters after edit
+deactivate Instrument
+
+EditInstrumentCommand-> TextUi: displayEditBeforeAfter(parameters before edit, parameters after edit)
+activate TextUi
+
+TextUi --> EditInstrumentCommand
+deactivate TextUi
+
+[<--EditInstrumentCommand: return "edit"
+deactivate EditInstrumentCommand
+
+@enduml
diff --git a/docs/diagrams/EditInstrumentSequenceDiagram.puml b/docs/diagrams/EditInstrumentSequenceDiagram.puml
new file mode 100644
index 0000000000..3753804e8c
--- /dev/null
+++ b/docs/diagrams/EditInstrumentSequenceDiagram.puml
@@ -0,0 +1,90 @@
+@startuml
+'https://plantuml.com/class-diagram
+!include Styles.puml
+
+box console COLOR_CONSOLE_SEGMENT
+participant ":InputParser" as InputParser COLOR_CONSOLE
+participant ":EditInstrumentParser" as EditInstrumentParser COLOR_CONSOLE
+end box
+
+box command COLOR_FAINT_GREEN
+participant ":EditInstrumentCommand" as EditInstrumentCommand COLOR_COMMANDS
+end box
+
+box model COLOR_MODEL_SEGMENT
+participant ":Stock" as Instrument COLOR_MODEL
+end box
+
+box ui COLOR_LIGHT_BLUE
+participant "<>\n:TextUi" as TextUi COLOR_UI
+end box
+
+[-> InputParser : filterByCommandType({"edit", "1"},\n instruments)
+activate InputParser
+
+InputParser -> InputParser: getEditInstrumentCommand({"edit", "1"},\n instruments)
+activate InputParser
+
+InputParser -> InputParser: getAndValidateIndexNumber({"edit", "1"}, instruments)
+
+activate InputParser
+InputParser --> InputParser
+deactivate InputParser
+
+InputParser -> TextUi: displayEditInstrumentFirstInstruction(stockToEdit)
+activate TextUi
+TextUi --> InputParser
+deactivate TextUi
+
+InputParser -> Instrument: getValidAttribute()
+activate Instrument
+Instrument --> InputParser: return validAttributes
+deactivate Instrument
+
+InputParser -> InputParser: getParametersToEdit(validAttributes)
+activate InputParser
+
+InputParser -> InputParser: filterInvalidParameters({"name"}, validAttributes)
+note right: Prompt user to enter in parameters and\ncheck if parameters entered are valid.\nError is thrown if there is no input.\nvalidAttributes for Stock are name,\ncurrent price, sentiment and remarks.
+activate InputParser
+InputParser --> InputParser: return filteredParameters
+deactivate InputParser
+InputParser --> InputParser: return filteredParameters
+deactivate InputParser
+
+create EditInstrumentParser
+InputParser -> EditInstrumentParser: EditInstrumentParser()
+activate EditInstrumentParser
+EditInstrumentParser --> InputParser
+deactivate EditInstrumentParser
+
+InputParser -> EditInstrumentParser: createEditCommand({"name"}, stockToEdit, 1);
+activate EditInstrumentParser
+
+ref over EditInstrumentParser
+Get new edited parameters
+from user and create a hashmap
+associating the parameter type
+and its new value
+endref
+
+create EditInstrumentCommand
+EditInstrumentParser -> EditInstrumentCommand: EditInstrumentCommand({"name", "new name"})
+activate EditInstrumentCommand
+EditInstrumentCommand --> EditInstrumentParser
+deactivate EditInstrumentCommand
+
+EditInstrumentParser -> EditInstrumentCommand: setIndex(1)
+activate EditInstrumentCommand
+EditInstrumentCommand --> EditInstrumentParser
+deactivate EditInstrumentCommand
+
+EditInstrumentParser --> InputParser: return editInstrumentCommand
+deactivate EditInstrumentParser
+
+InputParser --> InputParser: return editInstrumentCommand
+deactivate InputParser
+[<--InputParser: return editInstrumentCommand
+deactivate InputParser
+
+@enduml
diff --git a/docs/diagrams/EditRefrence.puml b/docs/diagrams/EditRefrence.puml
new file mode 100644
index 0000000000..adb249f78f
--- /dev/null
+++ b/docs/diagrams/EditRefrence.puml
@@ -0,0 +1,55 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+!include Styles.puml
+
+box console COLOR_CONSOLE_SEGMENT
+participant ":EditInstrumentParser" as EditInstrumentParser COLOR_CONSOLE
+end box
+
+group sd Get new edited parameters from user \nand create a hashmap associating the parameter type and its new value
+
+activate EditInstrumentParser
+
+EditInstrumentParser -> EditInstrumentParser: getEditedParameters({"name"}, stockToEdit)
+activate EditInstrumentParser
+
+EditInstrumentParser -> EditInstrumentParser: editNameParameter("stock", {"name"})
+note left: editNameParameters check if the name is entered \nthen prompts user to provide a new name.\nMethod loop until a valid name is given.
+activate EditInstrumentParser
+
+alt parameters to edit does not include "name"
+else
+loop until valid name is entered or abort is called
+EditInstrumentParser -> EditInstrumentParser: getUserInput()
+activate EditInstrumentParser
+EditInstrumentParser --> EditInstrumentParser: return new name
+deactivate EditInstrumentParser
+end
+end
+
+EditInstrumentParser --> EditInstrumentParser
+deactivate EditInstrumentParser
+
+EditInstrumentParser -> EditInstrumentParser: editXYZParameter({"name"})
+note left: This represents similar methods that does the same\nas the one above but for other parameters.\nXYZ represents all other parameters of\ncurrent price, sentiment, past return, entry price,\nexit price, expiry, remark, status.
+activate EditInstrumentParser
+
+alt parameters to edit does not include parameter "XYZ"
+else
+loop until valid parameter is entered or abort is called
+EditInstrumentParser -> EditInstrumentParser: getUserInput()
+activate EditInstrumentParser
+EditInstrumentParser --> EditInstrumentParser: return new value of parameter
+deactivate EditInstrumentParser
+end
+end
+EditInstrumentParser --> EditInstrumentParser
+deactivate EditInstrumentParser
+
+EditInstrumentParser --> EditInstrumentParser
+deactivate EditInstrumentParser
+
+end
+
+@enduml
+
diff --git a/docs/diagrams/FileManagerDiagram.puml b/docs/diagrams/FileManagerDiagram.puml
new file mode 100644
index 0000000000..68e421b9dc
--- /dev/null
+++ b/docs/diagrams/FileManagerDiagram.puml
@@ -0,0 +1,55 @@
+@startuml
+!include Styles.puml
+'https://plantuml.com/class-diagram
+
+Class Mtracker COLOR_MAIN
+
+Package "filemanager" <> {
+ Class Storage COLOR_STORAGE {
+ + loadFileData(InstrumentManager)
+ + writeFileData(ArrayList)
+ + updateFileData(ArrayList)
+ }
+ Class InstrumentDecoder COLOR_STORAGE {
+ + {static} readFile(InstrumentManager, List)
+ + {static} addSavedInstrumentToList(InstrumentManager, String[])
+ }
+ Class InstrumentEncoder COLOR_STORAGE {
+ + {static} editFile(String, FileWriter)
+ + {static} writeFile(ArrayList, FileWriter)
+ }
+ Class XYZDecoder COLOR_STORAGE {
+ + {static} addXYZToList(String[], InstrumentManager)
+ }
+
+}
+
+show Storage members
+show InstrumentDecoder members
+show InstrumentEncoder members
+show XYZDecoder members
+
+Class TextUi COLOR_UI
+Class "{abstract}\n Instrument" as Instrument COLOR_MODEL
+Class InstrumentManager COLOR_MODEL
+
+Mtracker -down[COLOR_MAIN]->"1" Storage
+
+Class outside COLOR_WHITE
+ Storage -down[COLOR_STORAGE].> InstrumentEncoder
+ Storage -down[COLOR_STORAGE].> InstrumentDecoder
+ Storage -down[COLOR_STORAGE].> Instrument
+ Storage -down[COLOR_STORAGE].> InstrumentManager
+ InstrumentEncoder -down[COLOR_STORAGE].> Instrument
+ InstrumentDecoder -down[COLOR_STORAGE].> InstrumentManager
+ InstrumentDecoder -down[COLOR_STORAGE].> Instrument
+ XYZDecoder -right[COLOR_STORAGE].> Instrument
+ XYZDecoder -down[COLOR_STORAGE].> InstrumentManager
+ XYZDecoder -up[COLOR_STORAGE]-|>InstrumentDecoder
+ Storage -left[COLOR_STORAGE].> TextUi
+ InstrumentDecoder -left[COLOR_STORAGE].> TextUi
+
+
+note bottom of XYZDecoder: XYZDecoder = CryptoDecoder, ForexDecoder, \nEtfDecoder, StockDecoder
+
+@enduml
diff --git a/docs/diagrams/FileManagerEncodingSequenceDiag.puml b/docs/diagrams/FileManagerEncodingSequenceDiag.puml
new file mode 100644
index 0000000000..a423f83663
--- /dev/null
+++ b/docs/diagrams/FileManagerEncodingSequenceDiag.puml
@@ -0,0 +1,68 @@
+@startuml
+!include Styles.puml
+
+'https://plantuml.com/sequence-diagram
+box filemanager COLOR_STORAGE_SEGMENT
+participant ":Storage" as Storage COLOR_STORAGE
+participant "<>\nInstrumentEncoder" as InstrumentEncoder COLOR_STORAGE
+end box
+
+box model COLOR_MODEL_SEGMENT
+participant ":Instrument" as Instrument COLOR_MODEL
+end box
+
+[-> Storage: updateFileData(instruments)
+activate Storage
+
+Storage-> Storage: writeFileData(instruments)
+activate Storage
+
+note bottom
+writeFileData initialises a new
+FileWriter to write into the txt file
+and calls the writeFile method in
+InstrumentEncoder
+end note
+
+Storage-> InstrumentEncoder: writeFile(instruments, writeToFile)
+activate InstrumentEncoder
+
+note right
+writeToFile is of type FileWriter
+to enable a FileWriter to write to
+the text file
+end note
+
+loop each instrument
+ InstrumentEncoder-> Instrument: textFileFormatting()
+ activate Instrument
+ Instrument--> InstrumentEncoder: return InstrumentInFileFormat
+ deactivate Instrument
+ note bottom
+ InstrumentInFileFormat
+ is of type String eg.
+ {"Stock;AAPL;32.0;
+ positive;false;Nil"}
+ end note
+
+ InstrumentEncoder-> InstrumentEncoder: editFile(InstrumentInFileFormat, writeToFile)
+ activate InstrumentEncoder
+ InstrumentEncoder--> InstrumentEncoder
+ deactivate InstrumentEncoder
+
+ note right
+ editFile writes to the text file
+ with the formatted instrument
+ end note
+
+end
+InstrumentEncoder--> Storage
+deactivate InstrumentEncoder
+
+Storage--> Storage
+deactivate Storage
+
+[<-- Storage
+deactivate Storage
+
+@enduml
diff --git a/docs/diagrams/FileManagerRefDiagCryptoDecoder.puml b/docs/diagrams/FileManagerRefDiagCryptoDecoder.puml
new file mode 100644
index 0000000000..0f8433a705
--- /dev/null
+++ b/docs/diagrams/FileManagerRefDiagCryptoDecoder.puml
@@ -0,0 +1,50 @@
+@startuml
+!include Styles.puml
+
+group sd Decode crypto attributes and create and add crypto to InstrumentManager
+box filemanager COLOR_STORAGE_SEGMENT
+participant "<>\nCryptoDecoder" as CryptoDecoder COLOR_STORAGE
+end box
+
+box model COLOR_MODEL_SEGMENT
+participant ":InstrumentManager" as InstrumentManager COLOR_MODEL
+participant ":Crypto" as Crypto COLOR_MODEL
+end box
+
+
+activate CryptoDecoder
+
+CryptoDecoder-> CryptoDecoder: validateAndDecodeGeneralAttributes(textSegment)
+activate CryptoDecoder
+CryptoDecoder--> CryptoDecoder
+deactivate CryptoDecoder
+
+CryptoDecoder-> CryptoDecoder: validateAndDecodeSpecificAttributes(textSegment)
+activate CryptoDecoder
+
+CryptoDecoder--> CryptoDecoder
+deactivate CryptoDecoder
+
+CryptoDecoder-> CryptoDecoder: createDecodedInstrument()
+activate CryptoDecoder
+
+create Crypto
+CryptoDecoder-> Crypto: Crypto("Crypto", "32.0", "positive","true", "2022-12-12", "")
+activate Crypto
+Crypto--> CryptoDecoder: return crypto
+deactivate Crypto
+
+CryptoDecoder--> CryptoDecoder: return crypto
+deactivate CryptoDecoder
+
+CryptoDecoder-> InstrumentManager: addInstrument(crypto)
+activate InstrumentManager
+
+InstrumentManager--> CryptoDecoder
+deactivate InstrumentManager
+
+end
+
+deactivate CryptoDecoder
+
+@enduml
diff --git a/docs/diagrams/FileManagerSeqBetweenStorageAndDecoder.puml b/docs/diagrams/FileManagerSeqBetweenStorageAndDecoder.puml
new file mode 100644
index 0000000000..6c18a3e03a
--- /dev/null
+++ b/docs/diagrams/FileManagerSeqBetweenStorageAndDecoder.puml
@@ -0,0 +1,48 @@
+@startuml
+!include Styles.puml
+
+box filemanager COLOR_STORAGE_SEGMENT
+participant ":Storage" as Storage COLOR_STORAGE
+participant "<>\nInstrumentDecoder" as InstrumentDecoder COLOR_STORAGE
+end box
+
+box ui COLOR_LIGHT_BLUE
+participant "<>\nTextUi" as TextUi COLOR_UI
+end box
+
+[-->Storage: loadFileData(instrumentManager)
+activate Storage
+
+alt File does not exist or is not regular
+ Storage->TextUi: displayCreateFile()
+ activate TextUi
+ TextUi--> Storage
+ deactivate TextUi
+
+ note bottom
+ if file does not exist or is not regular,
+ a new text file will be created
+ end note
+else
+ Storage->TextUi: displayLoadingFile()
+ activate TextUi
+ TextUi--> Storage
+ deactivate TextUi
+
+ Storage-> InstrumentDecoder: readFile(instrumentManager, fileLines)
+ activate InstrumentDecoder
+
+ ref over InstrumentDecoder
+
+ Decodes the text file and
+ updates InstrumentManager
+
+ end ref
+
+ InstrumentDecoder--> Storage
+ deactivate InstrumentDecoder
+ end
+
+[<--Storage
+deactivate Storage
+@enduml
diff --git a/docs/diagrams/FileManagerSequenceDiagram.puml b/docs/diagrams/FileManagerSequenceDiagram.puml
new file mode 100644
index 0000000000..4b3007c9e8
--- /dev/null
+++ b/docs/diagrams/FileManagerSequenceDiagram.puml
@@ -0,0 +1,70 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+!include Styles.puml
+
+group sd Decodes the text file and updates InstrumentManager
+box filemanager COLOR_STORAGE_SEGMENT
+participant "<>\nInstrumentDecoder" as InstrumentDecoder COLOR_STORAGE
+participant "<>\nCryptoDecoder" as CryptoDecoder COLOR_STORAGE
+end box
+
+box model COLOR_MODEL_SEGMENT
+participant ":InstrumentManager" as InstrumentManager COLOR_MODEL
+participant ":Crypto" as Crypto COLOR_MODEL
+end box
+
+box error COLOR_ERROR_SEGMENT
+participant ":InvalidInstrumentInFileError" as InvalidInstrumentInFileError COLOR_ERROR
+end box
+
+activate InstrumentDecoder
+
+ loop each line in the file
+ InstrumentDecoder-> InstrumentDecoder: addSavedInstrumentToList(instrumentManager, textSegment)
+ activate InstrumentDecoder
+
+ note right
+ The parameter textSegment is
+ the array of Crypto parameters:
+ {"Crypto", "32.0", "positive", "2022-01-12", ""}
+ end note
+
+ alt Instrument type is Crypto/Etf/Stock/Forex
+ InstrumentDecoder-> CryptoDecoder: addCryptoToList(textSegment, instrumentManager)
+ activate CryptoDecoder
+
+ ref over CryptoDecoder, InstrumentManager, Crypto
+
+ Decode crypto attributes and create and add crypto to InstrumentManager
+
+ endref
+
+ note bottom
+ Displaying only the outcome of adding
+ a pre-existing Crypto to list. This if
+ statement is supposed to be a switch statement
+ amongst the 4 different instruments
+ end note
+
+ CryptoDecoder--> InstrumentDecoder
+ deactivate CryptoDecoder
+
+ else
+ create InvalidInstrumentInFileError
+ InstrumentDecoder-> InvalidInstrumentInFileError: throw new InvalidInstrumentInFileError()
+ activate InvalidInstrumentInFileError
+ InvalidInstrumentInFileError--> InstrumentDecoder
+ deactivate InvalidInstrumentInFileError
+
+ end
+
+ InstrumentDecoder--> InstrumentDecoder
+ deactivate InstrumentDecoder
+
+ end
+
+ end
+ deactivate InstrumentDecoder
+
+
+@enduml
diff --git a/docs/diagrams/ModelDiagram.puml b/docs/diagrams/ModelDiagram.puml
new file mode 100644
index 0000000000..3ed40c9dea
--- /dev/null
+++ b/docs/diagrams/ModelDiagram.puml
@@ -0,0 +1,30 @@
+@startuml
+!include Styles.puml
+
+Package "Model " <>{
+ Class "{abstract}\nInstrument" as Instrument COLOR_MODEL
+ Class InstrumentManager COLOR_MODEL {
+ + {static} getInstance(): InstrumentManager
+ + addInstrument(Instrument)
+ + findInstruments(String): ArrayList
+ + deleteInstrument(int)
+ + editInstrument(int, HashMap)
+ }
+ Class Crypto COLOR_MODEL
+ Class Stock COLOR_MODEL
+ Class Etf COLOR_MODEL
+ Class Forex COLOR_MODEL
+}
+
+show InstrumentManager members
+
+Class Mtracker COLOR_MAIN
+Mtracker -down[COLOR_MODEL]-> " 1" InstrumentManager
+InstrumentManager -up[COLOR_MODEL]-> "1"InstrumentManager
+Crypto -up[COLOR_MODEL]-|>Instrument
+Stock -up[COLOR_MODEL]-|>Instrument
+Etf -up[COLOR_MODEL]-|>Instrument
+Forex -up[COLOR_MODEL]-|>Instrument
+InstrumentManager -down[COLOR_MODEL]-> " *" Instrument
+
+@enduml
diff --git a/docs/diagrams/Styles.puml b/docs/diagrams/Styles.puml
new file mode 100644
index 0000000000..f71c4d87bd
--- /dev/null
+++ b/docs/diagrams/Styles.puml
@@ -0,0 +1,36 @@
+!include Colors.puml
+
+!define COLOR_UI COLOR_DARK_BLUE
+!define COLOR_MAIN COLOR_BLACK
+!define COLOR_COMMANDS COLOR_DARK_GREEN
+!define COLOR_COMMANDS_SEGMENT COLOR_FAINT_GREEN
+!define COLOR_CONSOLE COLOR_TEAL
+!define COLOR_CONSOLE_SEGMENT COLOR_FAINT_TEAL
+!define COLOR_MODEL COLOR_PURPLE
+!define COLOR_MODEL_SEGMENT COLOR_LIGHT_PURPLE
+!define COLOR_STORAGE COLOR_BROWN
+!define COLOR_STORAGE_SEGMENT COLOR_LIGHT_BROWN
+!define COLOR_ERROR COLOR_ORANGE
+!define COLOR_ERROR_SEGMENT COLOR_FAINT_ORANGE
+!define COLOR_COMMONS COLOR_GREY
+!define COLOR_USER COLOR_RED
+
+!define ABSTRACT {abstract}
+
+skinparam Class {
+ FontColor #FFFFFF
+ BorderThickness 1
+ BorderColor #FFFFFF
+ FontName Arial
+}
+
+skinparam Participant {
+ FontColor #FFFFFF
+}
+
+skinparam classAttributeIconSize 0
+skinparam Shadowing false
+
+hide members
+hide footbox
+hide circle
diff --git a/docs/diagrams/TextUiDisplayInstrumentAllParams.puml b/docs/diagrams/TextUiDisplayInstrumentAllParams.puml
new file mode 100644
index 0000000000..2db9c878e5
--- /dev/null
+++ b/docs/diagrams/TextUiDisplayInstrumentAllParams.puml
@@ -0,0 +1,37 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+!include Styles.puml
+
+box ui COLOR_LIGHT_BLUE
+participant "<>\nTextUi" as TextUi COLOR_UI
+end box
+
+box model COLOR_MODEL_SEGMENT
+participant ":Instrument" as Instrument COLOR_MODEL
+end box
+
+[-> TextUi : displaySpecificInstrumentView(instrument)
+activate TextUi
+
+TextUi -> Instrument : getAllParams()
+activate Instrument
+
+note left
+displaySpecificInstrumentView()
+invoked from ViewCommand.execute()
+end note
+
+Instrument --> TextUi
+deactivate Instrument
+
+TextUi -->[ : printout instrument's parameters
+
+deactivate TextUi
+
+note left
+The getAllParams() method
+returns all the stored information
+on the instrument back as String.
+end note
+
+@enduml
diff --git a/docs/diagrams/TextUiDisplayInstrumentGeneralParams.puml b/docs/diagrams/TextUiDisplayInstrumentGeneralParams.puml
new file mode 100644
index 0000000000..8232ec5a02
--- /dev/null
+++ b/docs/diagrams/TextUiDisplayInstrumentGeneralParams.puml
@@ -0,0 +1,46 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+!include Styles.puml
+
+box ui COLOR_LIGHT_BLUE
+participant "<>\nTextUi" as TextUi COLOR_UI
+end box
+
+box model COLOR_MODEL_SEGMENT
+participant ":Instrument" as Instrument COLOR_MODEL
+end box
+
+[-> TextUi : displayAllInstruments(instruments)
+activate TextUi
+
+TextUi -> TextUi : displayInstruments()
+activate TextUi
+
+ loop each instrument
+ TextUi -> TextUi : constructLineInList(idx, instrument)
+ activate TextUi
+
+ TextUi -> Instrument : getGeneralParams()
+ activate Instrument
+
+ Instrument --> TextUi
+ deactivate Instrument
+
+ TextUi --> TextUi : printout instrument's \ngeneral parameters
+
+ note bottom
+ The getGeneralParams() method
+ returns the instrument's name, current price,
+ and sentiment as String.
+ end note
+
+ deactivate TextUi
+end
+
+TextUi --> TextUi : All instruments' general \nparams printed out
+deactivate TextUi
+[<-- TextUi
+deactivate TextUi
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/images/AddStockSequenceDiagram.png b/docs/images/AddStockSequenceDiagram.png
new file mode 100644
index 0000000000..1917b5b620
Binary files /dev/null and b/docs/images/AddStockSequenceDiagram.png differ
diff --git a/docs/images/AddStockSequenceDiagramRef.png b/docs/images/AddStockSequenceDiagramRef.png
new file mode 100644
index 0000000000..896aa86d58
Binary files /dev/null and b/docs/images/AddStockSequenceDiagramRef.png differ
diff --git a/docs/images/AddStockSequenceExecuteDiagram.png b/docs/images/AddStockSequenceExecuteDiagram.png
new file mode 100644
index 0000000000..e455ddecb0
Binary files /dev/null and b/docs/images/AddStockSequenceExecuteDiagram.png differ
diff --git a/docs/images/ArchitectureDiagram.png b/docs/images/ArchitectureDiagram.png
new file mode 100644
index 0000000000..1e714d6657
Binary files /dev/null and b/docs/images/ArchitectureDiagram.png differ
diff --git a/docs/images/CommandsDiagram.png b/docs/images/CommandsDiagram.png
new file mode 100644
index 0000000000..0621512f36
Binary files /dev/null and b/docs/images/CommandsDiagram.png differ
diff --git a/docs/images/ConsoleDiagram.png b/docs/images/ConsoleDiagram.png
new file mode 100644
index 0000000000..4dc47d3da0
Binary files /dev/null and b/docs/images/ConsoleDiagram.png differ
diff --git a/docs/images/DoneCryptoExecuteDiagram.png b/docs/images/DoneCryptoExecuteDiagram.png
new file mode 100644
index 0000000000..254eb91e50
Binary files /dev/null and b/docs/images/DoneCryptoExecuteDiagram.png differ
diff --git a/docs/images/DoneCryptoSequenceDiagram.png b/docs/images/DoneCryptoSequenceDiagram.png
new file mode 100644
index 0000000000..6f4a92a43d
Binary files /dev/null and b/docs/images/DoneCryptoSequenceDiagram.png differ
diff --git a/docs/images/EditExecuteRefrence.png b/docs/images/EditExecuteRefrence.png
new file mode 100644
index 0000000000..de504d8ec9
Binary files /dev/null and b/docs/images/EditExecuteRefrence.png differ
diff --git a/docs/images/EditExecuteSequenceDiagram.png b/docs/images/EditExecuteSequenceDiagram.png
new file mode 100644
index 0000000000..690735ca09
Binary files /dev/null and b/docs/images/EditExecuteSequenceDiagram.png differ
diff --git a/docs/images/EditInstrumentSequenceDiagram.png b/docs/images/EditInstrumentSequenceDiagram.png
new file mode 100644
index 0000000000..f408dd04b9
Binary files /dev/null and b/docs/images/EditInstrumentSequenceDiagram.png differ
diff --git a/docs/images/EditRefrence.png b/docs/images/EditRefrence.png
new file mode 100644
index 0000000000..8722304c4a
Binary files /dev/null and b/docs/images/EditRefrence.png differ
diff --git a/docs/images/FileManagerDiagram.png b/docs/images/FileManagerDiagram.png
new file mode 100644
index 0000000000..2e601f0733
Binary files /dev/null and b/docs/images/FileManagerDiagram.png differ
diff --git a/docs/images/FileManagerEncodingSequenceDiag.png b/docs/images/FileManagerEncodingSequenceDiag.png
new file mode 100644
index 0000000000..26c76c1131
Binary files /dev/null and b/docs/images/FileManagerEncodingSequenceDiag.png differ
diff --git a/docs/images/FileManagerRefDiagCryptoDecoder.png b/docs/images/FileManagerRefDiagCryptoDecoder.png
new file mode 100644
index 0000000000..5bc66f8cb7
Binary files /dev/null and b/docs/images/FileManagerRefDiagCryptoDecoder.png differ
diff --git a/docs/images/FileManagerSeqBetweenStorageAndDecoder.png b/docs/images/FileManagerSeqBetweenStorageAndDecoder.png
new file mode 100644
index 0000000000..99734c3868
Binary files /dev/null and b/docs/images/FileManagerSeqBetweenStorageAndDecoder.png differ
diff --git a/docs/images/FileManagerSequenceDiagram.png b/docs/images/FileManagerSequenceDiagram.png
new file mode 100644
index 0000000000..dec8d25305
Binary files /dev/null and b/docs/images/FileManagerSequenceDiagram.png differ
diff --git a/docs/images/InvalidInstrumentTypeInFile.png b/docs/images/InvalidInstrumentTypeInFile.png
new file mode 100644
index 0000000000..d3a5ee0f6a
Binary files /dev/null and b/docs/images/InvalidInstrumentTypeInFile.png differ
diff --git a/docs/images/ModelDiagram.png b/docs/images/ModelDiagram.png
new file mode 100644
index 0000000000..6b8a1d5510
Binary files /dev/null and b/docs/images/ModelDiagram.png differ
diff --git a/docs/images/TextUiDisplayInstrumentAllParams.png b/docs/images/TextUiDisplayInstrumentAllParams.png
new file mode 100644
index 0000000000..8e1dab6313
Binary files /dev/null and b/docs/images/TextUiDisplayInstrumentAllParams.png differ
diff --git a/docs/images/TextUiDisplayInstrumentGeneralParams.png b/docs/images/TextUiDisplayInstrumentGeneralParams.png
new file mode 100644
index 0000000000..6a27bc219b
Binary files /dev/null and b/docs/images/TextUiDisplayInstrumentGeneralParams.png differ
diff --git a/docs/images/Theodore.png b/docs/images/Theodore.png
new file mode 100644
index 0000000000..9d60695b5e
Binary files /dev/null and b/docs/images/Theodore.png differ
diff --git a/docs/images/Vignesh.jpeg b/docs/images/Vignesh.jpeg
new file mode 100644
index 0000000000..30dff5f3dc
Binary files /dev/null and b/docs/images/Vignesh.jpeg differ
diff --git a/docs/images/William.png b/docs/images/William.png
new file mode 100644
index 0000000000..af2db8de2d
Binary files /dev/null and b/docs/images/William.png differ
diff --git a/docs/images/WingHo.png b/docs/images/WingHo.png
new file mode 100644
index 0000000000..d3c783ac85
Binary files /dev/null and b/docs/images/WingHo.png differ
diff --git a/docs/images/mTracker_logo.png b/docs/images/mTracker_logo.png
new file mode 100644
index 0000000000..fc4a2d9d2b
Binary files /dev/null and b/docs/images/mTracker_logo.png differ
diff --git a/docs/images/mTracker_logo_cropped.png b/docs/images/mTracker_logo_cropped.png
new file mode 100644
index 0000000000..7b9e057d7f
Binary files /dev/null and b/docs/images/mTracker_logo_cropped.png differ
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index ab75b391b8..0000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# John Doe - Project Portfolio Page
-
-## Overview
-
-
-### Summary of Contributions
diff --git a/docs/team/kum-wh.md b/docs/team/kum-wh.md
new file mode 100644
index 0000000000..6767088394
--- /dev/null
+++ b/docs/team/kum-wh.md
@@ -0,0 +1,56 @@
+# Kum Wing Ho - Project Portfolio Page
+
+## Overview
+I was involved in the development of a greenfield project known as `mTracker`. The `mTracker` program
+serves as a command-line trading journal interface that allows busy individuals to store important financial instrument.
+Throughout the project development, my main responsibilities include:
+* Implementing and handling bugs relenting to `Etf` instrument and adding it to the watchlist.
+* Implementing and handling bugs relenting to `edit` feature.
+* Implementing and handling bugs relenting to `list` feature.
+
+## Summary of Contributions
+Below are some of my contributions to the project
+
+**Code contributed:**
+
+The code I contributed can be found in the reposense link [here](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=T12-1&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=functional-code~other~test-code~docs&since=2021-09-25&tabOpen=true&tabType=authorship&zFR=false&tabAuthor=kum-wh&tabRepo=AY2122S1-CS2113T-T12-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=functional-code~test-code~docs&authorshipIsBinaryFileTypeChecked=false)
+
+**Enhancements**:
+* **New Feature:** Provided the add `etf` into watchlist functionality.
+ * Implemented the `add etf` functionality to allow users to
+ store crypto information into `mTracker`.
+ * Justification: This feature is one of the core features given that `crypto` is a widely known instrument type in
+ the financial markets and is something our user would require support for.
+* **New Feature:** Provided the `edit` functionality.
+ * The `edit` functionality allows the user to edit a current instrument parameters
+ * Justification: This is a core feature that allow users to edit current instruments they added. This allows for updates of parameters on the instruments.
+ * Highlights: The `edit` functionality is required to interact with all instrument type and require requesting of inputs from user.
+* **New Feature:** Provided the `list` functionality.
+ * The `list` functionality allows the user to view all instruments and their general parameters.
+ * Justification: This is a core feature that provide users with a way to view all their instruments they have added.
+
+**Documentation:**
+
+
+ * **UG**
+ * Wrote the Edit feature section.[#96](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/96/files)
+ * Wrote the Abort Operation section. [#246](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/246/files)
+ * Wrote the FAQ section.
+
+
+* **DG**
+ * Wrote the `command` component.[#100](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/100/files)
+ * Wrote the done instrument feature section under Implementation.
+ * Wrote the edit instrument feature section under Implementation.[#230](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/230/files)
+ * Created 4 diagrams:
+ * Edit Parser Sequence Diagram [here](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/EditInstrumentSequenceDiagram.png).
+ * Edit Parser Reference Diagram [here](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/EditRefrence.png).
+ * Edit Execution Sequence Diagram [here](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/EditExecuteSequenceDiagram.png).
+ * Edit Execution Reference Diagram [here](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/EditExecuteRefrence.png).
+
+
+* **Contributions to team-based tasks**
+ * Released v1.0 jar file link.
+ * Writing of JavaDoc for Model, Commands, ui components and the EditInstrumentParser class.[#240](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/240)
+ * Implementation of a feature that improve readability, by adding a new line when the text printed is long.
+ The feature was eventually deemed as unnecessary and not implemented. [#133](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/133)
\ No newline at end of file
diff --git a/docs/team/kvignesh122.md b/docs/team/kvignesh122.md
new file mode 100644
index 0000000000..1d6abd131c
--- /dev/null
+++ b/docs/team/kvignesh122.md
@@ -0,0 +1,64 @@
+# Vignesh's Project Portfolio Page
+
+## Project Overview: mTracker
+
+mTracker is a **CLI-based trading journal** that allows
+user to store and view important trading-related information on their
+shortlisted financial instruments. The program allows users to store
+information on 4 types of instruments: Stock, ETF, Forex, Cryptocurrency.
+mTracker is written in Java 11 and contains over 5000 LoC.
+
+### My key contributions to the project:
+
+- Tested out various real-time market data API provider for initial idea of
+ implementing real-time capabailities in program. Coded and tried out a HTTP client
+ server in Java. Idea was later discarded by team for various reasons.
+- Project was mainly divided among group members based on instrument types.
+I was in charge of implementing all forex-related command and parser classes, and their testing.
+- Implemented the find feature, which allows users to search for
+instruments saved in the watchlist using their input search term.
+- Was instrumental in beautifying the program's UI. Coded the ACSII patterns
+for bye, and mTracker logo at startup.
+- Made sure that error messages were consistent in formatting with fullstops
+and commas. Also made sure grammar is correct for the error messages and the UG due to
+ my strength in language.
+- Coded the main run method in MTracker class and also the bye command for exiting
+program.
+- Abstracted out IndexedCommand class to handle index input by user, and
+managing it alongside view, delete, done, and edit command classes.
+- Abstracted out AddInstrumentParserTest class for all other instruments' addition parser test classes
+to depend on.
+
+### Code contributed
+
+[Vignesh's tP Code Dashboard](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=vignesh&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-25&tabOpen=true&tabType=authorship&tabAuthor=KVignesh122&tabRepo=AY2122S1-CS2113T-T12-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false)
+
+### Enhancements
+- Improved user-friendliness and aesthetics of program by
+ making list command to display only the 3 main parameters for all instruments.
+ Implemented a separate View feature instead for user to take deeper look into any one of the stored financial instruments.
+- Implemented abort feature in Add and Edit operations for user to exit
+the process of adding/editing an instrument prematurely.
+- Wrote extensive Junit tests for testing abort and addition of instrument features
+for crypto and forex.
+
+### Documentation:
+- Reviewed grammar and sentence structure of all UG and DG content; made edits to enhance clarity.
+- Contributed UML class diagram for Command classes in DG: [#238](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/238)
+- Designed TextUi sequence diagram for List and View commands in DG: [#231](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/231)
+- Wrote more than 60% of the UG. ([#88](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/88))
+- Wrote Architecture section in DG: [#89](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/89)
+
+### Team contributions:
+- Major PRs reviewed: [#211](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/211)
+ [#95](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/95)
+ [#91](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/91)
+ [#83](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/83)
+- Prepared the agenda, scheduled and conducted weekly project meetings outside of tutorial/lecture time.
+- Assisted team members with Gradle setup.
+- Planned and maintained team's GitHub issue tracker: [#29](https://github.com/AY2122S1-CS2113T-T12-1/tp/issues/29)
+[#36](https://github.com/AY2122S1-CS2113T-T12-1/tp/issues/36)
+ [#84](https://github.com/AY2122S1-CS2113T-T12-1/tp/issues/84)
+ [#141](https://github.com/AY2122S1-CS2113T-T12-1/tp/issues/141)
+- Improved overall code quality by abstracting out repetitive codeblocks as
+ methods, and removed redundant setters and getters. [#92](https://github.com/AY2122S1-CS2113T-T12-1/tp/issues/92)
diff --git a/docs/team/theodorekwok.md b/docs/team/theodorekwok.md
new file mode 100644
index 0000000000..1c64b9b18f
--- /dev/null
+++ b/docs/team/theodorekwok.md
@@ -0,0 +1,78 @@
+## Theodore Kwok - Project Portfolio Page
+
+### Overview
+I was involved in the development of a greenfield project known as `mTracker`. The `mTracker` program
+serves as a command-line trading journal interface that allows busy individuals to store important financial instruments.
+My main responsibilities include:
+* Being the main code reviewer, conducting code quality and implementation checks on pull requests (PR).
+* Designing and proposing software architectures to implement core features such as adding and editing
+the different financial instruments.
+* Implementing different features in mTracker.
+* Contributed to project management by updating the issues on issue tracker and noting down any possible ways we can
+improve on code quality and design.
+
+### Summary of Contributions
+Below are some of my contributions to the project
+
+* **Code contributed:**
+ * The code I contributed can be found in the reposense link [here](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-25&tabOpen=true&tabType=authorship&tabAuthor=theodorekwok&tabRepo=AY2122S1-CS2113T-T12-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false).
+* **Design architecture of add and edit functionality**
+ * Implemented the foundations for `add` functionality. Seen in PR
+ [#18](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/18) and
+ [#20](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/20)
+ * Designed the add instrument functionality. Used OOP to handle parsing the different instrument types. Eg, the `AddInstrumentParser` class provides
+ easy extensibility for add functionality for different types of instruments.
+ * Proposed `edit` functionality design through this PR [#130](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/130).
+ * Came up with a design that implements edit feature with only one parser class `EditInstrumentParser` and one command
+ class `EditInstrumentCommand`. The helps to reduce the amount of code we needed to write and allows user to edit
+ multiple parameters at once. As edit functionality is done on an existing instrument, we can determine the type of instrument and the kind of parameters supported
+ by that type of instrument.
+ * The design also provides extensibility for any parameters changes made to any of the
+ instrument classes. This is because only the instrument class with the parameter change will need to be modified.
+
+* **Enhancements**:
+ * **New Feature:** Provided the add `stock` into watchlist functionality.
+ * Building on the foundations of `add` functionality, I implemented the `add stock` functionality to allow users to
+ store stock information into `mTracker`.
+ * Justification: This feature is one of the core features given that `stock` is a widely known instrument type in the
+ financial markets and is something our user would require support for.
+ * **New Feature:** Provided the `delete` from watchlist functionality.
+ * The `delete` functionality allows user to remove an instrument from the watchlist.
+ * Justification: This is another core feature as over time and user will need to remove old instruments.
+ * Highlights: There were considerations on how to parse the command. Previously the
+ error checking and handling was done in the `DeleteCommand` class which reduced the cohesion of the class. As input
+ validations and error checking are done in the parser class, it made sense to shift the index checking into the parser
+ classes.
+ * **Feature depth:** Add support for date type in the expiry attribute for the instruments.
+
+* **Documentation:**
+ * **UG**
+ * Contributed to the add stock section through PR [#72](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/72)
+ * Updated UG after bug fixes in v2.1 through PR [#224](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/224)
+ * **DG**
+ * Wrote the `parser` section under Design with the design considerations for add and edit functionality.
+ * Wrote the `add instrument feature` section under Implementation.
+ * Wrote some of the extra sections in dg [#234](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/234).
+ * Created 5 diagrams:
+ * [Architecture](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/ArchitectureDiagram.png)
+ [Console diagram](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/ConsoleDiagram.png)
+ [Add stock diagram](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/AddStockSequenceDiagram.png)
+ [Add stock reference diagram](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/AddStockSequenceDiagramRef.png)
+ [Add stock execute diagram](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/AddStockSequenceExecuteDiagram.png)
+* **Contributions to team-based tasks**
+ * Set up the Github team org/repo.
+ * Contributed to some code refactorings:
+ * Standardising the error handling and creating custom exception from
+ PR [#144](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/144)
+ * Decoupling some functionality from unrelated classes. PR [#142](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/142)
+ * Reviewed most of the PRs to ensure that the code quality standards are met and the implementation follow the
+ sound design principles. Example of such PRs: [#35](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/35)
+ [#41](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/41)
+ [#76](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/76)
+ [#98](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/98)
+ * Reviewed sequence diagram PRs to ensure that the uml conventions and styles are followed. Example PR [here](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/131)
+ * Updated the issue tracker by reporting bugs and code quality issues. Example of issues:
+ [#26](https://github.com/AY2122S1-CS2113T-T12-1/tp/issues/26)
+ [#92](https://github.com/AY2122S1-CS2113T-T12-1/tp/issues/92)
+ [#148](https://github.com/AY2122S1-CS2113T-T12-1/tp/issues/148)
+ * Released v2.0 jar file link [here](https://github.com/AY2122S1-CS2113T-T12-1/tp/releases/tag/v2.0-release).
diff --git a/docs/team/williamlamjy.md b/docs/team/williamlamjy.md
new file mode 100644
index 0000000000..70e8a0392e
--- /dev/null
+++ b/docs/team/williamlamjy.md
@@ -0,0 +1,82 @@
+# William Lam - Project Portfolio Page
+
+## Overview
+I was involved in the development of a greenfield project known as `mTracker`. The `mTracker` program
+serves as a command-line trading journal interface that allows busy individuals to store important financial instruments.
+My main responsibilities include:
+* Providing assisting code reviews to ensure code quality and implementation is of high quality.
+* Implementing different features in mTracker.
+* Contributed to project management by updating issues on issue tracker and noting down any possible ways we can
+ improve on code quality and design.
+* Ensured high code quality by doing code refactoring and fixed multiple bugs present in the code.
+
+## Summary of Contributions
+Below are some of my contributions to the project
+
+* **Code contributed:**
+ * The code I contributed can be found in the reposense link [here](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=T12-1&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=functional-code~other~test-code~docs&since=2021-09-25&tabOpen=true&tabType=authorship&zFR=false&tabAuthor=williamlamjy&tabRepo=AY2122S1-CS2113T-T12-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=functional-code~test-code~docs&authorshipIsBinaryFileTypeChecked=false)
+* **Design architecture of filemanager component**
+ * Implemented the InstrumentEncoder, Storage and Decoder classes. PRs[#76](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/76)
+[#108](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/108) [#209](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/209)
+ * Designed how the storage of data operates and split the process into encoding and decoding processes. The
+ `InstrumentDecoder` is further abstracted into `XYZDecoder` classes which improves extensibility to allow greater code
+ re-usability. This further improves code cohesion and more focus can be placed to abstracting each instrument type. A
+ lot of thought and effort was put in to achieving this high level of SLAP.
+
+* **Design architecture of model component**
+ * Implemented the foundations of the `Instrument`, `InstrumentManager` and `subinstrument` classes. PR [#19](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/19)
+ * Designed the structure of how the instruments are stored and edited through the `InstrumentManager` such that
+ developers could easily further enhance the respective sub-instruments with their specific attributes and methods.
+
+* **Enhancements**:
+ * **New Feature:** Provided the add `crypto` into watchlist functionality. Implemented the `add crypto` functionality to allow users to store crypto information into `mTracker`.
+ * Justification: One of the core features as `crypto` is widely used in the financial markets and requires support
+ * **New Feature:** Provided the `done` functionality. The `done` functionality allows the user to mark an instrument as completed in their watchlist.
+ * Justification: This is another core feature to allow users to keep track of the many instruments they have by
+ marking it as done if they for example sold the instrument. This allows for easier updates on instruments.
+ * Highlights: The `done` functionality differs from other functionalities as it requires encoding and decoding
+ features with the `filemanager` component to store the instrument's status. Thus, extra considerations were taken in
+ implementation of the done feature such that it could be stored as an attribute in pre-existing instruments.
+
+* **Documentation:**
+ * **UG**
+ * Contributed to the add crypto section and summary page through PR [#73](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/73)
+ * Contributed to the contents page with hyperlinks through PR [#143](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/143)
+ * Provide feedback for formatting and wording PR [#248](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/248)
+ * **DG**
+ * Wrote the `filemanager` component.
+ * Wrote the `model` component.
+ * Wrote implementation for `Storing current data` and `Loading pre-existing data` to show the implementation of
+ encoding and decoding processes.
+ * Assisted in the implementation of the `done` command by drawing the sequential diagram.
+ * Created 8 diagrams:
+ * [Model](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/ModelDiagram.png)
+ [FileManager](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/FileManagerDiagram.png)
+ [InstrumentEncoder](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/FileManagerEncodingSequenceDiag.png)
+ [InstrumentDecoder](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/FileManagerSeqBetweenStorageAndDecoder.png)
+ [InstrumentDecoderRef](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/FileManagerSequenceDiagram.png)
+ [CryptoDecoderRef](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/FileManagerRefDiagCryptoDecoder.png)
+ [DoneCommand](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/DoneCryptoSequenceDiagram.png)
+ [DoneCommandRef](https://github.com/AY2122S1-CS2113T-T12-1/tp/blob/master/docs/images/DoneCryptoExecuteDiagram.png)
+ * Provided user stories through PR [#152](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/152)
+ * **Contributions to team-based tasks**
+ * Contributed to some code refactorings:
+ * Refactoring validation methods into `Validate` in the `commons` package from PR [#122](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/122)
+ * Standardising junit parser tests into the same format from PR [#121](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/121)
+ * Contributed to some bug fixes:
+ * Fixing non-positive current price inputs from PR [#198](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/198)
+ * Enabling multiple invalid instrument type inputs when adding an instrument from PR [#198](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/198)
+ * Enabling upper-casing for command inputs from PR [#204](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/204)
+ * Handling file separator issues in input by using a special char from PR [#199](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/199)
+ * Reviewed some PRs to ensure that the code quality standards are met and the implementation follow the
+ sound design principles. Example of such PRs: [#144](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/144)
+ [#20](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/20)
+ [#79](https://github.com/AY2122S1-CS2113T-T12-1/tp/pull/79)
+ * Managed issues in the issue tracker by addressing issues with comments and assigning issues to the corresponding
+ developer. Examples: [#191](https://github.com/AY2122S1-CS2113T-T12-1/tp/issues/191)
+ [#186](https://github.com/AY2122S1-CS2113T-T12-1/tp/issues/186)
+ [#182](https://github.com/AY2122S1-CS2113T-T12-1/tp/issues/182)
+ [#179](https://github.com/AY2122S1-CS2113T-T12-1/tp/issues/179)
+ * **Contributions beyond team-based tasks**
+ * Helped in a query in the forum [here](https://github.com/nus-cs2113-AY2122S1/forum/issues/28)
+ * Reported 17 bugs in another team's program [here](https://github.com/williamlamjy/ped/issues)
\ No newline at end of file
diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..3a08bea393
--- /dev/null
+++ b/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: seedu.mtracker.MTracker
+
diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java
deleted file mode 100644
index 5c74e68d59..0000000000
--- a/src/main/java/seedu/duke/Duke.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package seedu.duke;
-
-import java.util.Scanner;
-
-public class Duke {
- /**
- * Main entry-point for the java.duke.Duke application.
- */
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- System.out.println("What is your name?");
-
- Scanner in = new Scanner(System.in);
- System.out.println("Hello " + in.nextLine());
- }
-}
diff --git a/src/main/java/seedu/mtracker/LogHelper.java b/src/main/java/seedu/mtracker/LogHelper.java
new file mode 100644
index 0000000000..7d3082e111
--- /dev/null
+++ b/src/main/java/seedu/mtracker/LogHelper.java
@@ -0,0 +1,73 @@
+package seedu.mtracker;
+
+import java.io.IOException;
+import java.util.logging.FileHandler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
+//@@author theodorekwok
+/**
+ * A class responsible for handling the error logging and the messages to log.
+ */
+public class LogHelper {
+
+ public static final String LOG_FILE_NAME = "logger.log";
+ public static final String LOG_FILE_ERROR = "Log file not working!";
+
+ public static final String LOG_INVALID_NAME = "User tried to enter an invalid name here";
+ public static final String LOG_INVALID_PRICE = "User tried to enter an invalid price here";
+ public static final String LOG_INVALID_SENTIMENT = "User tried to enter an invalid sentiment here";
+ public static final String LOG_INVALID_PAST_RETURN = "Invalid Past returns entered. Default: double -101.0";
+ public static final String LOG_INVALID_EXPIRY = "User gave an invalid expiry input";
+ public static final String LOG_INVALID_INSTRUMENT = "User tried to add an invalid instrument type here";
+ public static final String LOG_INVALID_COMMAND = "User entered an invalid command to console here";
+
+ public static final String LOG_DATA_FILE_LOAD_ERROR = "mtracker storage text file not loading";
+ public static final String LOG_DATA_FILE_WRITE_ERROR = "Writing to storage text file not loading";
+ public static final String LOG_DATA_FILE_INSTRUMENT_TYPE_CORRUPTED_ERROR = "Instrument type in storage text "
+ + "file got corrupted";
+ public static final String LOG_DATA_FILE_INSTRUMENT_CORRUPTED_ERROR = "Instrument in storage text file "
+ + "got corrupted";
+
+ private static LogHelper logHelper;
+ private final Logger logger;
+
+ private LogHelper() {
+ logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ setupLogger();
+ }
+
+ /**
+ * Checks and create an instance of logHelper if it does not exist.
+ *
+ * @return The main instance of logHelper.
+ */
+ public static LogHelper getInstance() {
+ if (logHelper == null) {
+ logHelper = new LogHelper();
+ }
+ return logHelper;
+ }
+
+
+ public Logger getLogger() {
+ return logger;
+ }
+
+ /**
+ * Prepares the log files and set the logging level.
+ */
+ public void setupLogger() {
+ LogManager.getLogManager().reset();
+ logger.setLevel(Level.INFO);
+
+ try {
+ FileHandler logFile = new FileHandler(LOG_FILE_NAME);
+ logFile.setLevel(Level.INFO);
+ logger.addHandler(logFile);
+ } catch (IOException e) {
+ logger.log(Level.SEVERE, LOG_FILE_ERROR, e);
+ }
+ }
+}
diff --git a/src/main/java/seedu/mtracker/MTracker.java b/src/main/java/seedu/mtracker/MTracker.java
new file mode 100644
index 0000000000..b7f426e4e2
--- /dev/null
+++ b/src/main/java/seedu/mtracker/MTracker.java
@@ -0,0 +1,78 @@
+package seedu.mtracker;
+
+import seedu.mtracker.commands.Command;
+import seedu.mtracker.commands.ExitCommand;
+import seedu.mtracker.commands.InvalidCommand;
+import seedu.mtracker.console.InputParser;
+import seedu.mtracker.filemanager.Storage;
+import seedu.mtracker.model.InstrumentManager;
+import seedu.mtracker.ui.TextUi;
+
+import java.util.logging.Level;
+
+/**
+ * Main class responsible for the running of the whole mTracker application.
+ */
+public class MTracker {
+ private Storage storage;
+ private InstrumentManager instrumentManager;
+ private InputParser parser;
+ private LogHelper logger;
+
+ private static final String MAIN_WORKSPACE = "main";
+
+ public MTracker() {
+ try {
+ logger = LogHelper.getInstance();
+ storage = new Storage();
+ instrumentManager = InstrumentManager.getInstance();
+ parser = new InputParser();
+ storage.loadFileData(instrumentManager);
+ } catch (Exception e) {
+ TextUi.showErrorMessage(e);
+ System.exit(-1);
+ }
+ }
+
+ //@@author KVignesh122
+ /**
+ * Runs the overall logic of the program. Methods takes in user input command
+ * and does the necessary execution, continuously until the user exits program.
+ */
+ public void run() {
+ Command command;
+ String userInput;
+ String[] commandComponents;
+
+ do {
+ userInput = InputParser.getUserInput(MAIN_WORKSPACE);
+ commandComponents = parser.getCommandComponents(userInput);
+ try {
+ command = parser.filterByCommandType(commandComponents, instrumentManager.getInstruments());
+ command.setData(instrumentManager);
+ command.execute();
+ storage.updateFileData(instrumentManager.getInstruments());
+ } catch (Exception e) {
+ logger.getLogger().log(Level.WARNING, e.getMessage());
+ TextUi.showErrorMessage(e);
+ command = new InvalidCommand();
+ }
+ } while (!(command instanceof ExitCommand));
+ }
+
+ /**
+ * Greets user at the start of program and runs the whole program.
+ */
+ public void executeProgram() {
+ TextUi.greetAtStartUp();
+ run();
+ }
+
+ /**
+ * Constructs a new instance of MTracker and executes the program.
+ * This method is the main entry-point for the mTracker application.
+ */
+ public static void main(String[] args) {
+ new MTracker().executeProgram();
+ }
+}
diff --git a/src/main/java/seedu/mtracker/asserthelpers/AssertCommandHelpers.java b/src/main/java/seedu/mtracker/asserthelpers/AssertCommandHelpers.java
new file mode 100644
index 0000000000..b045338b40
--- /dev/null
+++ b/src/main/java/seedu/mtracker/asserthelpers/AssertCommandHelpers.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.asserthelpers;
+
+//@@author theodorekwok
+/**
+ * The helper class that contains all assertions related to the command classes.
+ */
+public abstract class AssertCommandHelpers {
+
+ public static final String INDEX_OUT_OF_BOUNDS = "Index is out of bounds when it should not be";
+
+ /**
+ * Asserts whether that the index that is passed to the IndexedCommand class is not out of bounds.
+ *
+ * @param size The number of instruments in instrumentMananger instance.
+ * @param index The index that is parsed by InputParser and passed into IndexCommand.
+ */
+ public static void assertIndexWithinBounds(int size, int index) {
+ assert index >= 0 && index < size : INDEX_OUT_OF_BOUNDS;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/asserthelpers/AssertOperationHelper.java b/src/main/java/seedu/mtracker/asserthelpers/AssertOperationHelper.java
new file mode 100644
index 0000000000..28aa565db0
--- /dev/null
+++ b/src/main/java/seedu/mtracker/asserthelpers/AssertOperationHelper.java
@@ -0,0 +1,31 @@
+package seedu.mtracker.asserthelpers;
+
+//@@author KVignesh122
+/**
+ * The helper class that contains all assertions related abort operation.
+ */
+public abstract class AssertOperationHelper {
+ public static final String NOT_IN_ADD_OR_EDIT = "Program is currently neither in ADD "
+ + "nor EDIT workspaces.";
+ public static final String NOT_IN_ADD = "Program is not in ADD workspace when it should be.";
+ private static final String EDIT_PROCESS = "edit";
+ private static final String ADD_PROCESS = "add";
+
+ /**
+ * Asserts that the current workspace user is in has to be either 'add' or 'edit'.
+ *
+ * @param workspace A string representing the workspace the user is in.
+ */
+ public static void assertAddEditOperation(String workspace) {
+ assert workspace.equals(ADD_PROCESS) || workspace.equals(EDIT_PROCESS) : NOT_IN_ADD_OR_EDIT;
+ }
+
+ /**
+ * Asserts that current workspace has to be in 'add'.
+ *
+ * @param workspace A string representing the workspace the user is in.
+ */
+ public static void assertIsAddOperation(String workspace) {
+ assert workspace.equals(ADD_PROCESS) : NOT_IN_ADD;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/asserthelpers/AssertParserHelper.java b/src/main/java/seedu/mtracker/asserthelpers/AssertParserHelper.java
new file mode 100644
index 0000000000..bd89842e76
--- /dev/null
+++ b/src/main/java/seedu/mtracker/asserthelpers/AssertParserHelper.java
@@ -0,0 +1,110 @@
+package seedu.mtracker.asserthelpers;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+
+//@@author theodorekwok
+/**
+ * The helper class that contains all assertions related parser classes.
+ */
+public abstract class AssertParserHelper {
+
+ public static final int NUM_GENERAL_PARAMETERS = 3;
+ public static final String MISSING_GENERAL_PARAMETERS = "There are missing general parameters";
+
+ public static final int NUM_STOCK_PARAMETERS = 4;
+ public static final String MISSING_STOCK_PARAMETERS = "There are missing stock parameters";
+
+ public static final int NUM_CRYPTO_PARAMETERS = 5;
+ public static final String MISSING_CRYPTO_PARAMETERS = "There are missing crypto parameters";
+
+ public static final int NUM_FX_PARAMETERS = 7;
+ public static final String MISSING_FX_PARAMETERS = "There are missing forex parameters";
+
+ public static final int NUM_ETF_PARAMETERS = 5;
+ public static final String MISSING_ETF_PARAMETERS = "There are missing Etf parameters";
+
+ public static final int MINIMUM_PRICE = 0;
+ public static final String NEGATIVE_PRICE = "Price recorded is negative";
+
+ public static final String EMPTY_STRING_INPUT = "Parameter is found to be empty when it should not be";
+
+ public static final String INVALID_EXPIRY = "Expiry date is set to the past when it should not be";
+
+ /**
+ * Asserts that the price after parsing is not negative.
+ *
+ * @param price The price obtained after parsing the user price input.
+ */
+ public static void assertPriceNonNegative(String price) {
+ assert Double.parseDouble(price) >= MINIMUM_PRICE : NEGATIVE_PRICE;
+ }
+
+ /**
+ * Asserts that the number of general parameters that is collected after parsing is correct.
+ *
+ * @param parameters The arraylist that contains the general instrument parameters after parsing user inputs.
+ */
+ public static void assertNoMissingGeneralParameters(ArrayList parameters) {
+ assert parameters.size() == NUM_GENERAL_PARAMETERS : MISSING_GENERAL_PARAMETERS;
+ }
+
+ /**
+ * Asserts that the number of stock parameters that is collected after parsing is correct.
+ *
+ * @param stockParameters The arraylist that contains the stock instrument parameters after parsing user inputs.
+ */
+ public static void assertNoMissingStockParameters(ArrayList stockParameters) {
+ assert stockParameters.size() == NUM_STOCK_PARAMETERS : MISSING_STOCK_PARAMETERS;
+ }
+
+ //@@author williamlamjy
+ /**
+ * Asserts that the number of crypto parameters that is collected after parsing is correct.
+ *
+ * @param cryptoParameters The arraylist that contains the crypto instrument parameters after parsing user inputs.
+ */
+ public static void assertNoMissingCryptoParameters(ArrayList cryptoParameters) {
+ assert cryptoParameters.size() == NUM_CRYPTO_PARAMETERS : MISSING_CRYPTO_PARAMETERS;
+ }
+
+ //@@author KVignesh122
+ /**
+ * Asserts that the number of forex parameters that is collected after parsing is correct.
+ *
+ * @param forexParameters The arraylist that contains the forex instrument parameters after parsing user inputs.
+ */
+ public static void assertNoMissingForexParameters(ArrayList forexParameters) {
+ assert forexParameters.size() == NUM_FX_PARAMETERS : MISSING_FX_PARAMETERS;
+ }
+
+ //@@author kum-wh
+ /**
+ * Asserts that the number of etf parameters that is collected after parsing is correct.
+ *
+ * @param etfParameters The arraylist that contains the etf instrument parameters after parsing user inputs.
+ */
+ public static void assertNoMissingEtfParameters(ArrayList etfParameters) {
+ assert etfParameters.size() == NUM_ETF_PARAMETERS : MISSING_ETF_PARAMETERS;
+ }
+
+ //@@author theodorekwok
+ /**
+ * Asserts that the input parsed is not empty.
+ *
+ * @param param The parameter that has been parsed after user input.
+ */
+ public static void assertInputNotEmpty(String param) {
+ assert !param.isEmpty() : EMPTY_STRING_INPUT;
+ }
+
+ /**
+ * Asserts that the date parsed is in the future.
+ *
+ * @param dateParam The date parameter that is parsed after user input.
+ */
+ public static void assertExpiryInTheFuture(String dateParam) {
+ LocalDate dateGiven = LocalDate.parse(dateParam);
+ assert dateGiven.isAfter(LocalDate.now()) || dateGiven.isEqual(LocalDate.now()) : INVALID_EXPIRY;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commands/AddCryptoCommand.java b/src/main/java/seedu/mtracker/commands/AddCryptoCommand.java
new file mode 100644
index 0000000000..a0c49df171
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/AddCryptoCommand.java
@@ -0,0 +1,46 @@
+package seedu.mtracker.commands;
+
+import seedu.mtracker.model.subinstrument.Crypto;
+import seedu.mtracker.ui.TextUi;
+
+import java.time.LocalDate;
+
+//@@author williamlamjy
+/**
+ * Responsible for adding Crypto to the list.
+ */
+public class AddCryptoCommand extends AddInstrumentCommand {
+
+ public static final String COMMAND_WORD = "crypto";
+
+ public static final int EXPIRY_INDEX = 3;
+ public static final int REMARK_INDEX = 4;
+
+ protected LocalDate expiryParameter;
+ protected String remarkParameter;
+
+ public void setCryptoParameters() {
+ expiryParameter = LocalDate.parse(inputParameters.get(EXPIRY_INDEX));
+ remarkParameter = inputParameters.get(REMARK_INDEX);
+ }
+
+ public void createNewCrypto() {
+ newInstrument = new Crypto(nameParameter, currentPriceParameter,
+ sentimentParameter, expiryParameter, remarkParameter);
+ }
+
+ /**
+ * Handles the execution of adding Crypto to the list.
+ *
+ * @return The name of the command.
+ */
+ @Override
+ public String execute() {
+ setAddGeneralParameters();
+ setCryptoParameters();
+ createNewCrypto();
+ instrumentManager.addInstrument(newInstrument);
+ TextUi.displayInstrumentAdded(newInstrument);
+ return COMMAND_WORD;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commands/AddEtfCommand.java b/src/main/java/seedu/mtracker/commands/AddEtfCommand.java
new file mode 100644
index 0000000000..4f24ee82a4
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/AddEtfCommand.java
@@ -0,0 +1,44 @@
+package seedu.mtracker.commands;
+
+import seedu.mtracker.model.subinstrument.Etf;
+import seedu.mtracker.ui.TextUi;
+
+//@@author kum-wh
+/**
+ * Responsible for adding Etf to the list.
+ */
+public class AddEtfCommand extends AddInstrumentCommand {
+
+ public static final String COMMAND_WORD = "etf";
+
+ public static final int PAST_RETURN_INDEX = 3;
+ public static final int REMARK_INDEX = 4;
+
+ protected double pastReturnParameter;
+ protected String remarkParameter;
+
+ public void setEtfParameters() {
+ remarkParameter = inputParameters.get(REMARK_INDEX);
+ pastReturnParameter = Double.parseDouble(inputParameters.get(PAST_RETURN_INDEX));
+ }
+
+ public void createNewEtf() {
+ newInstrument = new Etf(nameParameter, currentPriceParameter,
+ sentimentParameter, pastReturnParameter, remarkParameter);
+ }
+
+ /**
+ * Handles the execution of adding Etf to the list.
+ *
+ * @return The name of the command.
+ */
+ @Override
+ public String execute() {
+ setAddGeneralParameters();
+ setEtfParameters();
+ createNewEtf();
+ instrumentManager.addInstrument(newInstrument);
+ TextUi.displayInstrumentAdded(newInstrument);
+ return COMMAND_WORD;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commands/AddForexCommand.java b/src/main/java/seedu/mtracker/commands/AddForexCommand.java
new file mode 100644
index 0000000000..3ee0aa6d03
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/AddForexCommand.java
@@ -0,0 +1,58 @@
+package seedu.mtracker.commands;
+
+import seedu.mtracker.model.subinstrument.Forex;
+import seedu.mtracker.ui.TextUi;
+
+import java.time.LocalDate;
+
+//@@author KVignesh122
+/**
+ * Responsible for adding Forex to the list.
+ */
+public class AddForexCommand extends AddInstrumentCommand {
+ public static final String COMMAND_WORD = "forex";
+
+ public static final int ENTRY_INDEX = 3;
+ public static final int EXIT_INDEX = 4;
+ public static final int EXPIRY_INDEX = 5;
+ public static final int REMARK_INDEX = 6;
+
+ protected double entryPriceParameter;
+ protected double exitPriceParameter;
+ protected LocalDate expiryParameter;
+ protected String remarkParameter;
+
+ public void setForexParameters() {
+ entryPriceParameter = Double.parseDouble(inputParameters.get(ENTRY_INDEX));
+ exitPriceParameter = Double.parseDouble(inputParameters.get(EXIT_INDEX));
+ expiryParameter = LocalDate.parse(inputParameters.get(EXPIRY_INDEX));
+ remarkParameter = inputParameters.get(REMARK_INDEX);
+ }
+
+ public void createNewFxPair() {
+ newInstrument = new Forex(
+ nameParameter,
+ currentPriceParameter,
+ sentimentParameter,
+ entryPriceParameter,
+ exitPriceParameter,
+ expiryParameter,
+ remarkParameter
+ );
+ }
+
+ /**
+ * Handles the execution of adding Forex to the list.
+ *
+ * @return The name of the command.
+ */
+ @Override
+ public String execute() {
+ setAddGeneralParameters();
+ setForexParameters();
+ createNewFxPair();
+ instrumentManager.addInstrument(newInstrument);
+ TextUi.displayInstrumentAdded(newInstrument);
+ return COMMAND_WORD;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commands/AddInstrumentCommand.java b/src/main/java/seedu/mtracker/commands/AddInstrumentCommand.java
new file mode 100644
index 0000000000..8fc47459da
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/AddInstrumentCommand.java
@@ -0,0 +1,31 @@
+package seedu.mtracker.commands;
+
+import seedu.mtracker.model.Instrument;
+
+//@@author theodorekwok
+/**
+ * Responsible for all commands adding different types of instruments into the list.
+ */
+public class AddInstrumentCommand extends Command {
+
+ public static final String COMMAND_WORD = "add";
+ public static final int NAME_INDEX = 0;
+ public static final int CURRENT_PRICE_INDEX = 1;
+ public static final int SENTIMENT_INDEX = 2;
+
+ protected String nameParameter;
+ protected Double currentPriceParameter;
+ protected String sentimentParameter;
+ protected Instrument newInstrument;
+
+ public void setAddGeneralParameters() {
+ nameParameter = inputParameters.get(NAME_INDEX);
+ currentPriceParameter = Double.parseDouble(inputParameters.get(CURRENT_PRICE_INDEX));
+ sentimentParameter = inputParameters.get(SENTIMENT_INDEX);
+ }
+
+ @Override
+ public String execute() {
+ return COMMAND_WORD;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commands/AddStockCommand.java b/src/main/java/seedu/mtracker/commands/AddStockCommand.java
new file mode 100644
index 0000000000..95fbf6d6f9
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/AddStockCommand.java
@@ -0,0 +1,39 @@
+package seedu.mtracker.commands;
+
+import seedu.mtracker.model.subinstrument.Stock;
+import seedu.mtracker.ui.TextUi;
+
+//@@author theodorekwok
+/**
+ * Responsible for adding Stock to the list.
+ */
+public class AddStockCommand extends AddInstrumentCommand {
+
+ public static final String COMMAND_WORD = "stock";
+ public static final int REMARK_INDEX = 3;
+
+ protected String remarkParameter;
+
+ public void setStockParameters() {
+ remarkParameter = inputParameters.get(REMARK_INDEX);
+ }
+
+ public void createNewStock() {
+ newInstrument = new Stock(nameParameter, currentPriceParameter, sentimentParameter, remarkParameter);
+ }
+
+ /**
+ * Handles the execution of adding Stock to the list.
+ *
+ * @return The name of the command.
+ */
+ @Override
+ public String execute() {
+ setAddGeneralParameters();
+ setStockParameters();
+ createNewStock();
+ instrumentManager.addInstrument(newInstrument);
+ TextUi.displayInstrumentAdded(newInstrument);
+ return COMMAND_WORD;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commands/Command.java b/src/main/java/seedu/mtracker/commands/Command.java
new file mode 100644
index 0000000000..560ccf398b
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/Command.java
@@ -0,0 +1,25 @@
+package seedu.mtracker.commands;
+
+import seedu.mtracker.model.InstrumentManager;
+
+import java.util.ArrayList;
+
+//@@author williamlamjy
+/**
+ * Responsible for all different type of user commands.
+ */
+public abstract class Command {
+
+ protected InstrumentManager instrumentManager;
+ protected ArrayList inputParameters;
+
+ public void setData(InstrumentManager instrumentManager) {
+ this.instrumentManager = instrumentManager;
+ }
+
+ public void setParams(ArrayList parameters) {
+ inputParameters = parameters;
+ }
+
+ public abstract String execute();
+}
diff --git a/src/main/java/seedu/mtracker/commands/DeleteCommand.java b/src/main/java/seedu/mtracker/commands/DeleteCommand.java
new file mode 100644
index 0000000000..d7b6bdef39
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/DeleteCommand.java
@@ -0,0 +1,30 @@
+package seedu.mtracker.commands;
+
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.ui.TextUi;
+
+//@@author theodorekwok
+/**
+ * Responsible for the command that delete an instrument from the list.
+ */
+public class DeleteCommand extends IndexedCommand {
+
+ public static final String COMMAND_WORD = "delete";
+
+ public DeleteCommand() {
+ index = UNINITIALISED_INDEX;
+ }
+
+ /**
+ * Handles the execution of deleting an instrument from the list.
+ *
+ * @return The name of the command.
+ */
+ @Override
+ public String execute() {
+ Instrument instrumentToDelete = getInstrumentAtIndex();
+ instrumentManager.deleteInstrument(index);
+ TextUi.displayInstrumentDeleted(instrumentToDelete);
+ return COMMAND_WORD;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commands/DoneCommand.java b/src/main/java/seedu/mtracker/commands/DoneCommand.java
new file mode 100644
index 0000000000..bc92ffbca1
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/DoneCommand.java
@@ -0,0 +1,30 @@
+package seedu.mtracker.commands;
+
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.ui.TextUi;
+
+//@@author williamlamjy
+/**
+ * Responsible for the command that marks an instrument as done.
+ */
+public class DoneCommand extends IndexedCommand {
+
+ public static final String COMMAND_WORD = "done";
+
+ public DoneCommand() {
+ index = UNINITIALISED_INDEX;
+ }
+
+ /**
+ * Handles the execution of marking an instrument as done.
+ *
+ * @return The name of the command.
+ */
+ @Override
+ public String execute() {
+ Instrument instrumentToComplete = getInstrumentAtIndex();
+ instrumentToComplete.markAsDone();
+ TextUi.displayDoneInstrument(instrumentToComplete);
+ return COMMAND_WORD;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commands/EditInstrumentCommand.java b/src/main/java/seedu/mtracker/commands/EditInstrumentCommand.java
new file mode 100644
index 0000000000..70392e37a4
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/EditInstrumentCommand.java
@@ -0,0 +1,35 @@
+package seedu.mtracker.commands;
+
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.ui.TextUi;
+
+import java.util.HashMap;
+
+//@@author kum-wh
+/**
+ * Handles changing the parameters to the new values.
+ */
+public class EditInstrumentCommand extends IndexedCommand {
+ public static final String COMMAND_WORD = "edit";
+ protected HashMap editedParameters;
+
+ public EditInstrumentCommand(HashMap editedParameters) {
+ index = UNINITIALISED_INDEX;
+ this.editedParameters = editedParameters;
+ }
+
+ /**
+ * Handles the execution of the command, which is to change parameters to new values.
+ *
+ * @return name of the command.
+ */
+ @Override
+ public String execute() {
+ Instrument instrumentToEdit = getInstrumentAtIndex();
+ String instrumentBefore = instrumentToEdit.getAllParams();
+ instrumentManager.editInstrument(index, editedParameters);
+ String instrumentAfter = instrumentToEdit.getAllParams();
+ TextUi.displayEditBeforeAfter(instrumentBefore, instrumentAfter);
+ return COMMAND_WORD;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commands/ExitCommand.java b/src/main/java/seedu/mtracker/commands/ExitCommand.java
new file mode 100644
index 0000000000..b64eca75c7
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/ExitCommand.java
@@ -0,0 +1,23 @@
+package seedu.mtracker.commands;
+
+import seedu.mtracker.ui.TextUi;
+
+//@@author KVignesh122
+/**
+ * Responsible for the command that terminiates the program.
+ */
+public class ExitCommand extends Command {
+
+ public static final String COMMAND_WORD = "bye";
+
+ /**
+ * Handles the execution of terminating the program.
+ *
+ * @return The name of the command.
+ */
+ @Override
+ public String execute() {
+ TextUi.displayExitMessage();
+ return COMMAND_WORD;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commands/FindCommand.java b/src/main/java/seedu/mtracker/commands/FindCommand.java
new file mode 100644
index 0000000000..bd42e96484
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/FindCommand.java
@@ -0,0 +1,36 @@
+package seedu.mtracker.commands;
+
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.ui.TextUi;
+
+import java.util.ArrayList;
+
+//@@author KVignesh122
+/**
+ * Responsible for the command that find instruments containing a specific keyword in the name parameter.
+ */
+public class FindCommand extends Command {
+ public static final String COMMAND_WORD = "find";
+ public static final String EMPTY_STR = "";
+ private String searchString;
+
+ public FindCommand() {
+ searchString = EMPTY_STR;
+ }
+
+ public void setSearchString(String searchString) {
+ this.searchString = searchString;
+ }
+
+ /**
+ * Handles the execution of the finding of instruments.
+ *
+ * @return The name of the command.
+ */
+ @Override
+ public String execute() {
+ ArrayList filteredInstruments = instrumentManager.findInstruments(searchString);
+ TextUi.displayInstrumentsFound(filteredInstruments, searchString);
+ return COMMAND_WORD;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commands/IndexedCommand.java b/src/main/java/seedu/mtracker/commands/IndexedCommand.java
new file mode 100644
index 0000000000..df0d82d827
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/IndexedCommand.java
@@ -0,0 +1,31 @@
+package seedu.mtracker.commands;
+
+import seedu.mtracker.asserthelpers.AssertCommandHelpers;
+import seedu.mtracker.model.Instrument;
+
+//@@author KVignesh122
+/**
+ * Responsible for all commands that requires an index being input along with the command.
+ */
+public abstract class IndexedCommand extends Command {
+ public static final int UNINITIALISED_INDEX = -1;
+ protected int index;
+
+ public void setIndex(int idx) {
+ index = idx;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Gets a specific instrument via its index.
+ *
+ * @return The instrument at a specific index.
+ */
+ public Instrument getInstrumentAtIndex() {
+ AssertCommandHelpers.assertIndexWithinBounds(instrumentManager.getSize(), index);
+ return instrumentManager.getInstrument(index);
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commands/InvalidCommand.java b/src/main/java/seedu/mtracker/commands/InvalidCommand.java
new file mode 100644
index 0000000000..1fe3ccfc8c
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/InvalidCommand.java
@@ -0,0 +1,15 @@
+package seedu.mtracker.commands;
+
+//@@author KVignesh122
+/**
+ * Represents the command being created when an invalid input is entered.
+ */
+public class InvalidCommand extends Command {
+
+ public static final String COMMAND_WORD = "invalid";
+
+ @Override
+ public String execute() {
+ return COMMAND_WORD;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commands/ListCommand.java b/src/main/java/seedu/mtracker/commands/ListCommand.java
new file mode 100644
index 0000000000..beee691a85
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/ListCommand.java
@@ -0,0 +1,27 @@
+package seedu.mtracker.commands;
+
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.ui.TextUi;
+
+import java.util.ArrayList;
+
+//@@author kum-wh
+/**
+ * Responsible for the command that shows all instruments in the list.
+ */
+public class ListCommand extends Command {
+
+ public static final String COMMAND_WORD = "list";
+
+ /**
+ * Handles the execution of listing all the instruments in the list.
+ *
+ * @return The name of the command.
+ */
+ @Override
+ public String execute() {
+ ArrayList instruments = instrumentManager.getInstruments();
+ TextUi.displayAllInstruments(instruments);
+ return COMMAND_WORD;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commands/ViewCommand.java b/src/main/java/seedu/mtracker/commands/ViewCommand.java
new file mode 100644
index 0000000000..c5f503f186
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commands/ViewCommand.java
@@ -0,0 +1,28 @@
+package seedu.mtracker.commands;
+
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.ui.TextUi;
+
+//@@author KVignesh122
+/**
+ * Responsible for the command that allow the user to view all the parameters of a specific instrument.
+ */
+public class ViewCommand extends IndexedCommand {
+ public static final String COMMAND_WORD = "view";
+
+ public ViewCommand() {
+ index = UNINITIALISED_INDEX;
+ }
+
+ /**
+ * Handles the execution of displaying all the parameters of a specific instrument.
+ *
+ * @return The name of the command.
+ */
+ @Override
+ public String execute() {
+ Instrument instrumentToView = getInstrumentAtIndex();
+ TextUi.displaySpecificInstrumentView(instrumentToView);
+ return COMMAND_WORD;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/Validate.java b/src/main/java/seedu/mtracker/commons/Validate.java
new file mode 100644
index 0000000000..2e0c06c474
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/Validate.java
@@ -0,0 +1,469 @@
+package seedu.mtracker.commons;
+
+import seedu.mtracker.LogHelper;
+import seedu.mtracker.commands.AddCryptoCommand;
+import seedu.mtracker.commands.AddEtfCommand;
+import seedu.mtracker.commands.AddForexCommand;
+import seedu.mtracker.commands.AddStockCommand;
+import seedu.mtracker.commons.error.AlreadyDoneError;
+import seedu.mtracker.commons.error.InvalidBoundsError;
+import seedu.mtracker.commons.error.InvalidDateFormatError;
+import seedu.mtracker.commons.error.InvalidEmptyExpiryDateError;
+import seedu.mtracker.commons.error.InvalidEmptyPriceError;
+import seedu.mtracker.commons.error.InvalidEmptySentimentError;
+import seedu.mtracker.commons.error.InvalidEmptyStatusError;
+import seedu.mtracker.commons.error.InvalidInstrumentError;
+import seedu.mtracker.commons.error.InvalidNameError;
+import seedu.mtracker.commons.error.InvalidNegativePriceError;
+import seedu.mtracker.commons.error.InvalidPastDateError;
+import seedu.mtracker.commons.error.InvalidPastReturnError;
+import seedu.mtracker.commons.error.InvalidPastReturnTypeError;
+import seedu.mtracker.commons.error.InvalidPriceError;
+import seedu.mtracker.commons.error.InvalidSentimentError;
+import seedu.mtracker.commons.error.InvalidStatusError;
+import seedu.mtracker.console.AddForexParser;
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.ui.TextUi;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+/**
+ * Validates parameters of instruments to ensure they are of the right format.
+ */
+public class Validate {
+
+ public static final double MINIMUM_PRICE = 0;
+ public static final double MINIMUM_PAST_RETURN = -100;
+
+ public static final String POSITIVE_SENTIMENT = "positive";
+ public static final String NEUTRAL_SENTIMENT = "neutral";
+ public static final String NEGATIVE_SENTIMENT = "negative";
+ public static final String DONE_INDICATOR = "done";
+ public static final String NOT_DONE_INDICATOR = "undone";
+
+ public static final String STATUS_DONE = "true";
+ public static final String STATUS_NOT_DONE = "false";
+
+ protected static final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+
+ //@@author KVignesh122
+ private static final String FOREX_VALID_NAME_REGEX = "^[a-zA-Z]{3}/?[a-zA-Z]{3}$";
+
+ /**
+ * Checks if the instrument's name is empty.
+ * If the instrument is of type forex, it checks that the name is 6 letters.
+ *
+ * @param name Instrument's name
+ * @param instrumentType Instrument's type
+ * @return True if name is invalid.
+ */
+ public static boolean isInvalidNameCondition(String name, String instrumentType) {
+ if (instrumentType.equals(AddForexParser.INSTRUMENT_TYPE)) {
+ return (!name.matches(FOREX_VALID_NAME_REGEX));
+ }
+ return name.isEmpty();
+ }
+
+ /**
+ * Checks if the instrument name is valid.
+ *
+ * @param name Instrument's name
+ * @param instrumentType Instrument's type
+ * @throws InvalidNameError When name is invalid.
+ */
+ public static void checkName(String name, String instrumentType) throws InvalidNameError {
+ if (isInvalidNameCondition(name, instrumentType)) {
+ throw new InvalidNameError(instrumentType);
+ }
+ }
+
+ //@@author williamlamjy
+ /**
+ * Checks if instrument type is one of the 4 valid types.
+ *
+ * @param instrument Instrument that is being validated.
+ * @return True if instrument is invalid.
+ */
+ public static boolean isInvalidInstrument(String instrument) {
+ switch (instrument) {
+ case AddStockCommand.COMMAND_WORD:
+ // Fallthrough
+ case AddCryptoCommand.COMMAND_WORD:
+ // Fallthrough
+ case AddForexCommand.COMMAND_WORD:
+ // Fallthrough
+ case AddEtfCommand.COMMAND_WORD:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Checks if instrument is valid.
+ *
+ * @param instrument Instrument that is being validated.
+ * @throws InvalidInstrumentError When instrument is of invalid type.
+ */
+ public static void checkInstrument(String instrument) throws InvalidInstrumentError {
+ if (isInvalidInstrument(instrument)) {
+ throw new InvalidInstrumentError();
+ }
+ }
+
+ /**
+ * Validates the instrument.
+ * Catches and displays any errors if instrument is invalid.
+ *
+ * @param instrument Instrument that is being validated.
+ * @return True if instrument is valid.
+ */
+ public static boolean isValidInstrument(String instrument) {
+ try {
+ checkInstrument(instrument);
+ } catch (Exception e) {
+ logger.info(LogHelper.LOG_INVALID_INSTRUMENT);
+ TextUi.showErrorMessage(e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Validates instrument name.
+ * Catches and displays any errors if instrument is invalid.
+ *
+ * @param name Instrument's name.
+ * @param instrumentType Instrument's type.
+ * @return True if name is valid.
+ */
+ public static boolean isValidName(String name, String instrumentType) {
+ try {
+ checkName(name, instrumentType);
+ } catch (Exception e) {
+ logger.info(LogHelper.LOG_INVALID_NAME);
+ TextUi.showErrorMessage(e);
+ return false;
+ }
+ return true;
+ }
+
+ //@@author theodorekwok
+ /**
+ * Checks if price is empty.
+ *
+ * @param price Instrument's price.
+ * @throws InvalidEmptyPriceError When price parameter is empty.
+ */
+ public static void checkPriceIsEmpty(String price) throws InvalidEmptyPriceError {
+ if (price.isEmpty()) {
+ throw new InvalidEmptyPriceError();
+ }
+ }
+
+ /**
+ * Checks if price is a valid numerical value.
+ * @param price Instrument's price
+ * @throws InvalidPriceError When price is an invalid numerical.
+ */
+ public static void checkPriceIsDouble(String price) throws InvalidPriceError {
+ try {
+ Double.parseDouble(price);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidPriceError();
+ }
+ }
+
+ /**
+ * Checks if price is positive.
+ *
+ * @param price Instrument's price.
+ * @throws InvalidNegativePriceError When price is negative
+ */
+ public static void checkPriceIsNonNegative(String price) throws InvalidNegativePriceError {
+ double inputPrice = Double.parseDouble(price);
+ if (inputPrice <= MINIMUM_PRICE) {
+ throw new InvalidNegativePriceError();
+ }
+ }
+
+ /**
+ * Checks if price is valid.
+ * Catches and displays any errors if price is invalid.
+ *
+ * @param price Instrument's price.
+ * @return True if price is valid.
+ */
+ public static boolean isValidPrice(String price) {
+ try {
+ checkPriceIsEmpty(price);
+ checkPriceIsDouble(price);
+ checkPriceIsNonNegative(price);
+ } catch (Exception e) {
+ logger.info(LogHelper.LOG_INVALID_PRICE);
+ TextUi.showErrorMessage(e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Validates instrument index.
+ *
+ * @param instruments Instruments in current watchlist.
+ * @param instrumentNumber Instrument index.
+ * @throws InvalidBoundsError When index is negative or greater than size of watch list.
+ */
+ public static void validateIndexWithinBounds(ArrayList instruments, int instrumentNumber)
+ throws InvalidBoundsError {
+ boolean isNegative = instrumentNumber < 0;
+ boolean isGreaterThanListSize = instrumentNumber >= instruments.size();
+ if (isNegative || isGreaterThanListSize) {
+ throw new InvalidBoundsError();
+ }
+ }
+
+ //@@author
+ /**
+ * Checks if instrument is already done.
+ *
+ * @param instruments Instruments in current watchlist.
+ * @param instrumentNumber Instrument index.
+ * @throws AlreadyDoneError When instrument is already done.
+ */
+ public static void checkIsNotDone(ArrayList instruments, int instrumentNumber)
+ throws AlreadyDoneError {
+ Instrument instrument = instruments.get(instrumentNumber);
+ boolean isDoneStatus = instrument.getIsDone();
+ if (isDoneStatus) {
+ throw new AlreadyDoneError();
+ }
+ }
+
+ //@@author theodorekwok
+ /**
+ * Checks if sentiment is empty.
+ *
+ * @param sentiment Instrument's sentiment
+ * @throws InvalidEmptySentimentError When sentiment parameter is empty.
+ */
+ public static void checkSentimentIsEmpty(String sentiment) throws InvalidEmptySentimentError {
+ if (sentiment.isEmpty()) {
+ throw new InvalidEmptySentimentError();
+ }
+ }
+
+ /**
+ * Checks if sentiment is of one of the 3 valid types.
+ * @param sentiment Instrument's sentiment
+ * @throws InvalidSentimentError When sentiment is of invalid type.
+ */
+ public static void checkSentiment(String sentiment) throws InvalidSentimentError {
+ boolean isValidPositiveSentiment = sentiment.equals(POSITIVE_SENTIMENT);
+ boolean isValidNegativeSentiment = sentiment.equals(NEGATIVE_SENTIMENT);
+ boolean isValidNeutralSentiment = sentiment.equals(NEUTRAL_SENTIMENT);
+ if (!isValidPositiveSentiment && !isValidNeutralSentiment && !isValidNegativeSentiment) {
+ throw new InvalidSentimentError();
+ }
+ }
+
+ /**
+ * Checks if sentiment is valid.
+ * Catches and displays any errors if sentiment is invalid.
+ *
+ * @param sentiment Instrument's sentiment
+ * @return True if sentiment is valid.
+ */
+ public static boolean isValidSentiment(String sentiment) {
+ try {
+ checkSentimentIsEmpty(sentiment);
+ checkSentiment(sentiment);
+ } catch (Exception e) {
+ logger.info(LogHelper.LOG_INVALID_SENTIMENT);
+ TextUi.showErrorMessage(e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Checks if past return is a valid numerical.
+ *
+ * @param pastReturn Etf's past return.
+ * @throws InvalidPastReturnTypeError When past return is an invalid numerical.
+ */
+ public static void checkPastReturnIsDouble(String pastReturn) throws InvalidPastReturnTypeError {
+ try {
+ Double.parseDouble(pastReturn);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidPastReturnTypeError();
+ }
+ }
+
+ /**
+ * Checks if past return is less than the minimum value.
+ *
+ * @param pastReturn Etf's past return.
+ * @throws InvalidPastReturnError When past return is less than the minimum value.
+ */
+ public static void checkPastReturnIsValid(String pastReturn) throws InvalidPastReturnError {
+ double pastReturnValue = Double.parseDouble(pastReturn);
+ if (pastReturnValue < MINIMUM_PAST_RETURN) {
+ throw new InvalidPastReturnError();
+ }
+ }
+
+ //@@author kum-wh
+ /**
+ * Checks if past return is valid.
+ * Catches and displays any errors if past return is invalid.
+ *
+ * @param pastReturn Etf's past return.
+ * @return True if past return is valid.
+ */
+ public static boolean isValidPastReturn(String pastReturn) {
+ if (pastReturn.isEmpty()) {
+ return false;
+ }
+ try {
+ checkPastReturnIsDouble(pastReturn);
+ checkPastReturnIsValid(pastReturn);
+ } catch (Exception e) {
+ logger.info(LogHelper.LOG_INVALID_PAST_RETURN);
+ TextUi.showErrorMessage(e);
+ return false;
+ }
+ return true;
+ }
+
+ //@@author theodorekwok
+ /**
+ * Checks if expiry is empty.
+ *
+ * @param expiryInput Instrument's expiry.
+ * @throws InvalidEmptyExpiryDateError When expiry parameter is empty.
+ */
+ public static void checkExpiryIsEmpty(String expiryInput) throws InvalidEmptyExpiryDateError {
+ if (expiryInput.isEmpty()) {
+ throw new InvalidEmptyExpiryDateError();
+ }
+ }
+
+ /**
+ * Checks if expiry is of valid date format.
+ *
+ * @param expiryInput Instrument's expiry
+ * @throws InvalidDateFormatError When expiry is of invalid date format.
+ */
+ public static void checkExpiryIsValidFormat(String expiryInput) throws InvalidDateFormatError {
+ try {
+ LocalDate.parse(expiryInput);
+ } catch (DateTimeParseException e) {
+ throw new InvalidDateFormatError();
+ }
+ }
+
+ /**
+ * Checks if expiry is a future date.
+ *
+ * @param expiryInput Instrument's expiry
+ * @throws InvalidPastDateError When expiry is in the past.
+ */
+ public static void checkExpiryIsInPast(String expiryInput) throws InvalidPastDateError {
+ LocalDate givenDate = LocalDate.parse(expiryInput);
+ if (givenDate.isBefore(LocalDate.now())) {
+ throw new InvalidPastDateError();
+ }
+ }
+
+ /**
+ * Checks if expiry is valid.
+ * Catches and displays any errors if expiry is invalid.
+ *
+ * @param expiryInput Instrument's expiry
+ * @return True if expiry is invalid.
+ */
+ public static boolean isValidExpiry(String expiryInput) {
+ try {
+ checkExpiryIsEmpty(expiryInput);
+ checkExpiryIsValidFormat(expiryInput);
+ checkExpiryIsInPast(expiryInput);
+ } catch (Exception e) {
+ logger.info(LogHelper.LOG_INVALID_EXPIRY);
+ TextUi.showErrorMessage(e);
+ return false;
+ }
+ return true;
+ }
+
+ //@@author kum-wh
+ /**
+ * Checks if done status is empty.
+ *
+ * @param doneStatus Instrument's done status.
+ * @throws InvalidEmptyStatusError When done parameter is empty.
+ */
+ public static void checkStatusIsEmpty(String doneStatus) throws InvalidEmptyStatusError {
+ if (doneStatus.isEmpty()) {
+ throw new InvalidEmptyStatusError();
+ }
+ }
+
+ /**
+ * Checks if done status is valid.
+ *
+ * @param doneStatus Instrument's done status.
+ * @throws InvalidStatusError When done status is of invalid format.
+ */
+ public static void checkStatus(String doneStatus) throws InvalidStatusError {
+ boolean isValidCompletedStatus = doneStatus.equals(DONE_INDICATOR);
+ boolean isValidNotCompletedStatus = doneStatus.equals(NOT_DONE_INDICATOR);
+ if (!isValidCompletedStatus && !isValidNotCompletedStatus) {
+ throw new InvalidStatusError();
+ }
+ }
+
+ /**
+ * Checks if done status is valid.
+ * Catches and displays any errors if done status is invalid.
+ *
+ * @param doneStatus Instrument's done status.
+ * @return True if done status is valid.
+ */
+ public static boolean isValidInputStatus(String doneStatus) {
+ try {
+ checkStatusIsEmpty(doneStatus);
+ checkStatus(doneStatus);
+ } catch (Exception e) {
+ TextUi.showErrorMessage(e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Checks if edit parameters is empty.
+ *
+ * @param input User input of parameters to edit.
+ * @return True if edit parameters is not empty.
+ */
+ public static boolean isNonEmptyEditParameters(String input) {
+ return !input.isEmpty();
+ }
+
+ //@@author theodorekwok
+ /**
+ * Checks if done status is of valid format in the mTracker file.
+ *
+ * @param savedStatusFromFile Done status parameter from the file.
+ * @return True if done status is valid.
+ */
+ public static boolean isValidStatus(String savedStatusFromFile) {
+ boolean isValidDoneStatus = savedStatusFromFile.equals(STATUS_DONE);
+ boolean isValidNotDoneStatus = savedStatusFromFile.equals(STATUS_NOT_DONE);
+ return isValidDoneStatus || isValidNotDoneStatus;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/AlreadyDoneError.java b/src/main/java/seedu/mtracker/commons/error/AlreadyDoneError.java
new file mode 100644
index 0000000000..6e61bf3b94
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/AlreadyDoneError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author KVignesh122
+/**
+ * The custom exception class that is thrown when instrument is marked done again.
+ */
+public class AlreadyDoneError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that instrument already has a completed status.
+ *
+ * @return A string error message that states the instrument is already marked as completed.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INSTRUMENT_MARKED_DONE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/EditEmptyParameterError.java b/src/main/java/seedu/mtracker/commons/error/EditEmptyParameterError.java
new file mode 100644
index 0000000000..24c6ba3ebd
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/EditEmptyParameterError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author kum-wh
+/**
+ * * The custom exception class that is thrown when parameters to edit are not provided.
+ */
+public class EditEmptyParameterError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that parameters to edit cannot be empty.
+ *
+ * @return A string error message that states parameters to edit must be provided.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.EDIT_EMPTY_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/ErrorMessage.java b/src/main/java/seedu/mtracker/commons/error/ErrorMessage.java
new file mode 100644
index 0000000000..c1d4db4a4e
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/ErrorMessage.java
@@ -0,0 +1,109 @@
+package seedu.mtracker.commons.error;
+
+/**
+ * Displays all the user facing errors.
+ */
+public abstract class ErrorMessage {
+
+ public static final String INVALID_INSTRUMENT_GIVEN_ERROR = "Sorry instrument must be either "
+ + "stock, crypto, forex or etf.";
+ public static final String INVALID_ABORT_IN_MAIN_ERROR = "Oops you can only abort if you are in add/edit workflow. "
+ + "Try 'bye' if you would like to exit the program.";
+ public static final String INVALID_COMMAND_ERROR = "Oops, I do not understand you...";
+ public static final String INVALID_FOREX_NAME_GIVEN_ERROR = "Sorry forex pair codes must contain 6 letters! "
+ + "\nNo numbers allowed. (Eg: AUDUSD, EUR/GBP, xauusd, GBPjpy, usd/sgd, etc.)";
+ public static final String INVALID_PRICE_EMPTY_ERROR = "Sorry price cannot be empty.";
+ public static final String INVALID_NEGATIVE_PRICE_ERROR = "Sorry price cannot be negative. "
+ + "It must be a positive number.";
+ public static final String INVALID_PRICE_INPUT_ERROR = "Sorry price must be a numeric value.";
+ public static final String INVALID_SENTIMENT_EMPTY_ERROR = "Sorry sentiment cannot be empty. "
+ + "It must be either positive, negative or neutral.";
+ public static final String INVALID_SENTIMENT_ERROR = "Sorry sentiment must be either "
+ + "positive, negative or neutral.";
+ public static final String INVALID_PAST_RETURN_TYPE_ERROR = "Sorry, past returns must be a numeric value! "
+ + "Input value will be ignored.";
+ public static final String INVALID_PAST_RETURN_ERROR = "Sorry, past returns inserted cannot be lesser than -100."
+ + " Input value will be ignored.";
+ public static final String INVALID_EXPIRY_DATE_EMPTY_ERROR = "Sorry expiry date cannot be empty.";
+ public static final String INVALID_PAST_DATE_GIVEN_ERROR = "Oops, expiry cannot be a date in the past.";
+ public static final String INVALID_DATE_FORMAT_ERROR = "Oops, expiry given must be in YYYY-MM-DD format";
+ public static final String INVALID_INDEX_GIVEN_ERROR = "Oops an invalid index is given. "
+ + "\nPlease provide an acceptable index number corresponding to the instruments in the watchlist.";
+ public static final String INVALID_NO_INDEX_GIVEN_ERROR = "Oops no index given. "
+ + "\nPlease provide an acceptable index number corresponding to the instruments in the watchlist.";
+ public static final String INVALID_INSTRUMENT_NONEXISTENT_ERROR = "Oops, instrument does not exist at that index.";
+ public static final String INVALID_INSTRUMENT_IN_FILE_ERROR = "Oops, it appears that the incorrect instrument "
+ + "type is provided in the mTracker.txt file";
+ public static final String INVALID_NO_SEARCH_STRING_GIVEN_ERROR = "Oops, please input a search"
+ + " string after 'find' command.";
+ public static final String INVALID_STATUS_EDIT_ERROR = "Sorry status entered is invalid. "
+ + "Enter either done or undone.";
+ public static final String INVALID_STATUS_EDIT_EMPTY_ERROR = "Sorry status cannot be empty. "
+ + "Enter either done or undone.";
+ public static final String INSTRUMENT_MARKED_DONE_ERROR = "Instrument at provided index "
+ + "has already been marked as completed!";
+ public static final String ADD_OPERATION_ABORTED = "Addition of new instrument has been aborted! "
+ + "You are in the main workspace now.";
+ public static final String EDIT_OPERATION_ABORTED = "Edit process has been aborted! "
+ + "You are in the main workspace now.";
+
+ public static final String FILE_WRITE_ERROR = "Oh no! There seems to be an error writing to the file";
+ public static final String FILE_LOAD_ERROR = "Oh no! There seems to be an error loading this file";
+
+ //@@author williamlamjy
+ public static final String REMARK_FORMATTING_IN_FILE_ERROR = "Oh no! There seems to be an error in the formatting "
+ + "of the remarks entry in the file";
+ public static final String EXPIRY_FORMATTING_IN_FILE_ERROR = "Oh no! There seems to be an error in the formatting "
+ + "of the expiry entry in the file." + System.lineSeparator() + "Please check that it is of YYYY-MM-DD "
+ + "format.";
+ public static final String ENTRY_PRICE_FORMATTING_IN_FILE_ERROR = "Oh no! There seems to be an error in the "
+ + "formatting of the entry price entry in the file.";
+ public static final String CURR_PRICE_FORMATTING_IN_FILE_ERROR = "Oh no! There seems to be an error in the "
+ + "formatting of the current price entry in the file.";
+ public static final String SENTIMENT_FORMATTING_IN_FILE_ERROR = "Oh no! There seems to be an error in the "
+ + "formatting of the sentiment entry in the file.";
+ public static final String STATUS_FORMATTING_IN_FILE_ERROR = "Oh no! There seems to be an error in the "
+ + "formatting of the status entry in the file.";
+ public static final String NAME_FORMATTING_IN_FILE_ERROR = "Oh no! There seems to be an error in the formatting "
+ + "of the name entry in the file.";
+ public static final String EXIT_PRICE_FORMATTING_IN_FILE_ERROR = "Oh no! There seems to be an error in the "
+ + "formatting of the exit price entry in the file.";
+ public static final String PAST_RETURN_FORMATTING_IN_FILE_ERROR = "Oh no! There seems to be an error in the "
+ + "formatting of the past returns entry in the file.";
+
+ public static final String EMPTY_ENTRY_PRICE_IN_FILE_ERROR = "Oh no! Looks like the entry price entry in the file"
+ + " is empty.";
+ public static final String EMPTY_EXIT_PRICE_IN_FILE_ERROR = "Oh no! Looks like the exit price entry in the file"
+ + " is empty.";
+ public static final String EMPTY_NAME_IN_FILE_ERROR = "Oh no! Looks like the name entry in the file"
+ + " is empty.";
+ public static final String EMPTY_SENTIMENT_IN_FILE_ERROR = "Oh no! Looks like the sentiment entry in the file"
+ + " is empty.";
+ public static final String EMPTY_CURR_PRICE_IN_FILE_ERROR = "Oh no! Looks like the curr price entry in the file"
+ + " is empty.";
+ public static final String EMPTY_STATUS_IN_FILE_ERROR = "Oh no! Looks like the status entry in the file"
+ + " is empty.";
+ public static final String EMPTY_EXPIRY_IN_FILE_ERROR = "Oh no! Looks like the expiry entry in the file is empty!";
+ //@@author
+
+ public static final String EDIT_EMPTY_ERROR = "Edit parameters cannot be empty, aborting edit process.";
+
+ /**
+ * Returns an error message when name provided is empty.
+ *
+ * @param instrumentType The type of instrument the user is providing a name for.
+ * @return A string error message that the instrument name cannot be empty.
+ */
+ public static String addInstrumentNameError(String instrumentType) {
+ return "Sorry " + instrumentType + " cannot have an empty name!";
+ }
+
+ /**
+ * Returns a forex error message when forex name provided is in the wrong format.
+ *
+ * @return A string error message that the forex name is not in the right format.
+ */
+ public static String addForexNameError() {
+ return INVALID_FOREX_NAME_GIVEN_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidBoundsError.java b/src/main/java/seedu/mtracker/commons/error/InvalidBoundsError.java
new file mode 100644
index 0000000000..58ff4e6f1d
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidBoundsError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author theodorekwok
+/**
+ * The custom exception class that is thrown when the index provided is out of bounds.
+ */
+public class InvalidBoundsError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that instrument at that index does not exist.
+ *
+ * @return A string error message that states the instrument does not exist at that index.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_INSTRUMENT_NONEXISTENT_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidCommandError.java b/src/main/java/seedu/mtracker/commons/error/InvalidCommandError.java
new file mode 100644
index 0000000000..e30e1bfadf
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidCommandError.java
@@ -0,0 +1,29 @@
+package seedu.mtracker.commons.error;
+
+//@@author KVignesh122
+/**
+ * The custom exception class that is thrown when the command provided is invalid.
+ */
+public class InvalidCommandError extends Exception {
+
+ private static final String ABORTED = "abort";
+
+ private final String invalidCommand;
+
+ public InvalidCommandError(String invalidCommand) {
+ this.invalidCommand = invalidCommand;
+ }
+
+ /**
+ * Returns the error message to the user stating that command given is not valid.
+ *
+ * @return A string error message that states the command given is not recognised.
+ */
+ @Override
+ public String getMessage() {
+ if (invalidCommand.equals(ABORTED)) {
+ return ErrorMessage.INVALID_ABORT_IN_MAIN_ERROR;
+ }
+ return ErrorMessage.INVALID_COMMAND_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidDateFormatError.java b/src/main/java/seedu/mtracker/commons/error/InvalidDateFormatError.java
new file mode 100644
index 0000000000..0f91256900
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidDateFormatError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author theodorekwok
+/**
+ * The custom exception class that is thrown when the date provided is invalid.
+ */
+public class InvalidDateFormatError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that date given is in the wrong format.
+ *
+ * @return A string error message that states the date given is in the wrong format.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_DATE_FORMAT_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidEmptyExpiryDateError.java b/src/main/java/seedu/mtracker/commons/error/InvalidEmptyExpiryDateError.java
new file mode 100644
index 0000000000..6a06d0d726
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidEmptyExpiryDateError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author KVignesh122
+/**
+ * The custom exception class that is thrown when expiry date is not provided.
+ */
+public class InvalidEmptyExpiryDateError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that date is not given.
+ *
+ * @return A string error message that states date must be provided.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_EXPIRY_DATE_EMPTY_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidEmptyIndexError.java b/src/main/java/seedu/mtracker/commons/error/InvalidEmptyIndexError.java
new file mode 100644
index 0000000000..18609a43e5
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidEmptyIndexError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author theodorekwok
+/**
+ * The custom exception class that is thrown when index is not provided.
+ */
+public class InvalidEmptyIndexError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that index is not given.
+ *
+ * @return A string error message that states index must be provided.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_NO_INDEX_GIVEN_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidEmptyPriceError.java b/src/main/java/seedu/mtracker/commons/error/InvalidEmptyPriceError.java
new file mode 100644
index 0000000000..3cef197487
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidEmptyPriceError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when price is not provided.
+ */
+public class InvalidEmptyPriceError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that price is not given.
+ *
+ * @return A string error message that states price must be provided.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_PRICE_EMPTY_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidEmptySearchStringError.java b/src/main/java/seedu/mtracker/commons/error/InvalidEmptySearchStringError.java
new file mode 100644
index 0000000000..9d43db7cc4
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidEmptySearchStringError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author KVignesh122
+/**
+ * The custom exception class that is thrown when search string is not provided.
+ */
+public class InvalidEmptySearchStringError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that the search string is not given.
+ *
+ * @return A string error message that states search string must be provided.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_NO_SEARCH_STRING_GIVEN_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidEmptySentimentError.java b/src/main/java/seedu/mtracker/commons/error/InvalidEmptySentimentError.java
new file mode 100644
index 0000000000..32781b7470
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidEmptySentimentError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author theodorekwok
+/**
+ * The custom exception class that is thrown when sentiment is not provided.
+ */
+public class InvalidEmptySentimentError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that the sentiment is not given.
+ *
+ * @return A string error message that states sentiment must be provided.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_SENTIMENT_EMPTY_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidEmptyStatusError.java b/src/main/java/seedu/mtracker/commons/error/InvalidEmptyStatusError.java
new file mode 100644
index 0000000000..e88000d563
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidEmptyStatusError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author kum-wh
+/**
+ * * The custom exception class that is thrown when an instrument status is not provided.
+ */
+public class InvalidEmptyStatusError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that done status of an instrument cannot be empty.
+ *
+ * @return A string error message that states done status of an instrument must be provided.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_STATUS_EDIT_EMPTY_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidIndexError.java b/src/main/java/seedu/mtracker/commons/error/InvalidIndexError.java
new file mode 100644
index 0000000000..0f242211d7
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidIndexError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author theodorekwok
+/**
+ * The custom exception class that is thrown when the index provided is not a number.
+ */
+public class InvalidIndexError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that index given is not valid.
+ *
+ * @return A string error message that states the index given is not valid.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_INDEX_GIVEN_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidInstrumentError.java b/src/main/java/seedu/mtracker/commons/error/InvalidInstrumentError.java
new file mode 100644
index 0000000000..88faddb80e
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidInstrumentError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when the instrument type provided is not recognised.
+ */
+public class InvalidInstrumentError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that instrument type given is not recognised.
+ *
+ * @return A string error message that states the instrument type given is not valid.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_INSTRUMENT_GIVEN_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidNameError.java b/src/main/java/seedu/mtracker/commons/error/InvalidNameError.java
new file mode 100644
index 0000000000..b14ccecd1d
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidNameError.java
@@ -0,0 +1,34 @@
+package seedu.mtracker.commons.error;
+
+import seedu.mtracker.console.AddForexParser;
+
+//@@author KVignesh122
+/**
+ * The custom exception class that is thrown when the name of instrument provided is not valid.
+ */
+public class InvalidNameError extends Exception {
+
+ private final String instrumentType;
+
+ /**
+ * Creates a new InvalidNameError instance.
+ *
+ * @param instrumentType The type of instrument that is throwing this error.
+ */
+ public InvalidNameError(String instrumentType) {
+ this.instrumentType = instrumentType;
+ }
+
+ /**
+ * Returns the error message to the user stating that name given is not valid.
+ *
+ * @return A string error message that states the name given is not valid.
+ */
+ @Override
+ public String getMessage() {
+ if (instrumentType.equals(AddForexParser.INSTRUMENT_TYPE)) {
+ return ErrorMessage.addForexNameError();
+ }
+ return ErrorMessage.addInstrumentNameError(instrumentType);
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidNegativePriceError.java b/src/main/java/seedu/mtracker/commons/error/InvalidNegativePriceError.java
new file mode 100644
index 0000000000..061adcefd3
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidNegativePriceError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when price provided is a negative number.
+ */
+public class InvalidNegativePriceError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that price given cannot be negative.
+ *
+ * @return A string error message that states the price must be a positive numbers.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_NEGATIVE_PRICE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidPastDateError.java b/src/main/java/seedu/mtracker/commons/error/InvalidPastDateError.java
new file mode 100644
index 0000000000..80d4c12c2e
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidPastDateError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author theodorekwok
+/**
+ * The custom exception class that is thrown when date provided is in the past.
+ */
+public class InvalidPastDateError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that date given cannot be in the past.
+ *
+ * @return A string error message that states the date cannot be in the past.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_PAST_DATE_GIVEN_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidPastReturnError.java b/src/main/java/seedu/mtracker/commons/error/InvalidPastReturnError.java
new file mode 100644
index 0000000000..df67bb4e41
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidPastReturnError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author theodorekwok
+/**
+ * The custom exception class that is thrown when past return given is less than -100.
+ */
+public class InvalidPastReturnError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that past return given cannot be less than -100.
+ *
+ * @return A string error message that states past return cannot be less than -100.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_PAST_RETURN_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidPastReturnTypeError.java b/src/main/java/seedu/mtracker/commons/error/InvalidPastReturnTypeError.java
new file mode 100644
index 0000000000..4fca0bb162
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidPastReturnTypeError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author theodorekwok
+/**
+ * The custom exception class that is thrown when past return provided is not a number.
+ */
+public class InvalidPastReturnTypeError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that past return given is not a number.
+ *
+ * @return A string error message that states past return must be a numeric value.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_PAST_RETURN_TYPE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidPriceError.java b/src/main/java/seedu/mtracker/commons/error/InvalidPriceError.java
new file mode 100644
index 0000000000..57b9969cde
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidPriceError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author theodorekwok
+/**
+ * The custom exception class that is thrown when price provided is not a number.
+ */
+public class InvalidPriceError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that price given is not a number.
+ *
+ * @return A string error message that states price must be a numeric value.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_PRICE_INPUT_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidSentimentError.java b/src/main/java/seedu/mtracker/commons/error/InvalidSentimentError.java
new file mode 100644
index 0000000000..5647ee363a
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidSentimentError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author theodorekwok
+/**
+ * The custom exception class that is thrown when sentiment provided is not recognised.
+ */
+public class InvalidSentimentError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that sentiment given is not recognised.
+ *
+ * @return A string error message that states the sentiment given is not valid.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_SENTIMENT_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/InvalidStatusError.java b/src/main/java/seedu/mtracker/commons/error/InvalidStatusError.java
new file mode 100644
index 0000000000..3781d5450d
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/InvalidStatusError.java
@@ -0,0 +1,18 @@
+package seedu.mtracker.commons.error;
+
+//@@author kum-wh
+/**
+ * * The custom exception class that is thrown when status provided is not done or undone.
+ */
+public class InvalidStatusError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that status entered is not a valid status.
+ *
+ * @return A string error message that states done status must be either done or undone.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_STATUS_EDIT_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/OperationAbortedError.java b/src/main/java/seedu/mtracker/commons/error/OperationAbortedError.java
new file mode 100644
index 0000000000..9fcf2cd0f4
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/OperationAbortedError.java
@@ -0,0 +1,37 @@
+package seedu.mtracker.commons.error;
+
+import seedu.mtracker.asserthelpers.AssertOperationHelper;
+
+//@@author KVignesh122
+/**
+ * The custom exception class that is thrown when user aborts an add or edit process.
+ */
+public class OperationAbortedError extends Exception {
+ private static final String EDIT_PROCESS = "edit";
+
+ private final String process;
+
+ /**
+ * Creates a new OperationAbortedError instance.
+ *
+ * @param process The type of process is underway when the user call abort.
+ */
+ public OperationAbortedError(String process) {
+ AssertOperationHelper.assertAddEditOperation(process);
+ this.process = process;
+ }
+
+ /**
+ * Returns the error message to the user stating that process has been aborted.
+ *
+ * @return A string error message that states process is terminated.
+ */
+ @Override
+ public String getMessage() {
+ if (process.equals(EDIT_PROCESS)) {
+ return ErrorMessage.EDIT_OPERATION_ABORTED;
+ }
+ AssertOperationHelper.assertIsAddOperation(process);
+ return ErrorMessage.ADD_OPERATION_ABORTED;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/FileLoadError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/FileLoadError.java
new file mode 100644
index 0000000000..bd7d1570e3
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/FileLoadError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author theodorekwok
+/**
+ * The custom exception class that is thrown when there are problems with creating or loading a storage file.
+ */
+public class FileLoadError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that storage file load has an error.
+ *
+ * @return A string error message that states the storage file has a load error.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.FILE_LOAD_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/FileWriteError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/FileWriteError.java
new file mode 100644
index 0000000000..62ef0750bc
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/FileWriteError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when there are problems with writing to the storage file.
+ */
+public class FileWriteError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that writing to the storage file has an error.
+ *
+ * @return A string error message that states the storage file has a write error.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.FILE_WRITE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidCurrPriceSavedInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidCurrPriceSavedInFileError.java
new file mode 100644
index 0000000000..bcc910872a
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidCurrPriceSavedInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file instrument current price is saved in the wrong format.
+ */
+public class InvalidCurrPriceSavedInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that current price in storage file has formatting issues.
+ *
+ * @return A string error message that states the current price in storage file is stored wrongly.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.CURR_PRICE_FORMATTING_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyCurrPriceInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyCurrPriceInFileError.java
new file mode 100644
index 0000000000..1fd7b8d2f4
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyCurrPriceInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file instrument current price is empty.
+ */
+public class InvalidEmptyCurrPriceInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that current price in storage file is empty.
+ *
+ * @return A string error message that states the current price in storage file is empty.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.EMPTY_CURR_PRICE_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyEntryPriceInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyEntryPriceInFileError.java
new file mode 100644
index 0000000000..aecdf6f994
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyEntryPriceInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file forex entry price is empty.
+ */
+public class InvalidEmptyEntryPriceInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that entry price in storage file is empty.
+ *
+ * @return A string error message that states the entry price in storage file is empty.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.EMPTY_ENTRY_PRICE_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyExitPriceInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyExitPriceInFileError.java
new file mode 100644
index 0000000000..ad168d8ff9
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyExitPriceInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file forex exit price is empty.
+ */
+public class InvalidEmptyExitPriceInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that exit price in storage file is empty.
+ *
+ * @return A string error message that states the exit price in storage file is empty.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.EMPTY_EXIT_PRICE_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyExpiryInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyExpiryInFileError.java
new file mode 100644
index 0000000000..f8e1ae040b
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyExpiryInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file instrument expiry date is empty.
+ */
+public class InvalidEmptyExpiryInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that expiry date in storage file is empty.
+ *
+ * @return A string error message that states the expiry date in storage file is empty.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.EMPTY_EXPIRY_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyNameInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyNameInFileError.java
new file mode 100644
index 0000000000..85edeb7742
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyNameInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file instrument name is empty.
+ */
+public class InvalidEmptyNameInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that name in storage file is empty.
+ *
+ * @return A string error message that states the name in storage file is empty.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.EMPTY_NAME_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptySentimentInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptySentimentInFileError.java
new file mode 100644
index 0000000000..b22e32450e
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptySentimentInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file instrument sentiment is empty.
+ */
+public class InvalidEmptySentimentInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that sentiment in storage file is empty.
+ *
+ * @return A string error message that states the sentiment in storage file is empty.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.EMPTY_SENTIMENT_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyStatusInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyStatusInFileError.java
new file mode 100644
index 0000000000..08020f23b2
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEmptyStatusInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file instrument status is empty.
+ */
+public class InvalidEmptyStatusInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that status in storage file is empty.
+ *
+ * @return A string error message that states the status in storage file is empty.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.EMPTY_STATUS_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEntryPriceSavedInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEntryPriceSavedInFileError.java
new file mode 100644
index 0000000000..4711063de8
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidEntryPriceSavedInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file forex entry price is not valid.
+ */
+public class InvalidEntryPriceSavedInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that entry price in storage file is invalid.
+ *
+ * @return A string error message that states the entry price in storage file is in the wrong format.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.ENTRY_PRICE_FORMATTING_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidExitPriceSavedInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidExitPriceSavedInFileError.java
new file mode 100644
index 0000000000..db742e5ea2
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidExitPriceSavedInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file forex exit price is not valid.
+ */
+public class InvalidExitPriceSavedInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that exit price in storage file is invalid.
+ *
+ * @return A string error message that states the exit price in storage file is in the wrong format.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.EXIT_PRICE_FORMATTING_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidExpirySavedInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidExpirySavedInFileError.java
new file mode 100644
index 0000000000..76a7a7202b
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidExpirySavedInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file instrument expiry is not valid.
+ */
+public class InvalidExpirySavedInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that expiry in storage file is invalid.
+ *
+ * @return A string error message that states the expiry in storage file is in the wrong format.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.EXPIRY_FORMATTING_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidInstrumentInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidInstrumentInFileError.java
new file mode 100644
index 0000000000..4eed2b6ff1
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidInstrumentInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when the storage file instrument type provided is not valid.
+ */
+public class InvalidInstrumentInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that storage file instrument type is not valid.
+ *
+ * @return A string error message that states the storage file instrument type is not valid.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.INVALID_INSTRUMENT_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidNameSavedInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidNameSavedInFileError.java
new file mode 100644
index 0000000000..8c4c382de0
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidNameSavedInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file instrument name is not valid.
+ */
+public class InvalidNameSavedInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that name in storage file is invalid.
+ *
+ * @return A string error message that states the name in storage file is in the wrong format.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.NAME_FORMATTING_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidPastReturnSavedInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidPastReturnSavedInFileError.java
new file mode 100644
index 0000000000..f1fb604bf2
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidPastReturnSavedInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file instrument past return is not valid or saved wrongly.
+ */
+public class InvalidPastReturnSavedInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that past return in storage file is invalid.
+ *
+ * @return A string error message that states the past return in storage file is in the wrong format.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.PAST_RETURN_FORMATTING_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidRemarkInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidRemarkInFileError.java
new file mode 100644
index 0000000000..926baa62a2
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidRemarkInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file instrument remark is saved in the wrong format.
+ */
+public class InvalidRemarkInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that remark in storage file is saved in the wrong format.
+ *
+ * @return A string error message that states the remark in storage file is saved in the wrong format.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.REMARK_FORMATTING_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidSentimentSavedInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidSentimentSavedInFileError.java
new file mode 100644
index 0000000000..8b154c0418
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidSentimentSavedInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file instrument sentiment saved is invalid.
+ */
+public class InvalidSentimentSavedInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that sentiment in storage file is invalid.
+ *
+ * @return A string error message that states the sentiment in storage file is saved is invalid.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.SENTIMENT_FORMATTING_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidStatusSavedInFileError.java b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidStatusSavedInFileError.java
new file mode 100644
index 0000000000..7e44d7d8e4
--- /dev/null
+++ b/src/main/java/seedu/mtracker/commons/error/fileerror/InvalidStatusSavedInFileError.java
@@ -0,0 +1,20 @@
+package seedu.mtracker.commons.error.fileerror;
+
+import seedu.mtracker.commons.error.ErrorMessage;
+
+//@@author williamlamjy
+/**
+ * The custom exception class that is thrown when storage file instrument status is saved wrongly.
+ */
+public class InvalidStatusSavedInFileError extends Exception {
+
+ /**
+ * Returns the error message to the user stating that status in storage file is invalid.
+ *
+ * @return A string error message that states the status in storage file is saved is invalid.
+ */
+ @Override
+ public String getMessage() {
+ return ErrorMessage.STATUS_FORMATTING_IN_FILE_ERROR;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/console/AddCryptoParser.java b/src/main/java/seedu/mtracker/console/AddCryptoParser.java
new file mode 100644
index 0000000000..08d3cb5745
--- /dev/null
+++ b/src/main/java/seedu/mtracker/console/AddCryptoParser.java
@@ -0,0 +1,86 @@
+package seedu.mtracker.console;
+
+import seedu.mtracker.asserthelpers.AssertParserHelper;
+import seedu.mtracker.commands.AddCryptoCommand;
+import seedu.mtracker.commons.Validate;
+import seedu.mtracker.commons.error.OperationAbortedError;
+import seedu.mtracker.ui.TextUi;
+
+//@@author williamlamjy
+/**
+ * A class responsible for parsing inputs when user is adding a new crypto instrument.
+ */
+public class AddCryptoParser extends AddInstrumentParser {
+
+ public static String INSTRUMENT_TYPE = "crypto";
+
+ /**
+ * Queries and gets crypto remark from the user.
+ *
+ * @return User remark input.
+ */
+ public String getCryptoRemarkFromUser() {
+ TextUi.displayAddRemarkInstruction();
+ return getUserInput(WORKSPACE);
+ }
+
+ /**
+ * Queries and gets crypto expiry date from the user.
+ *
+ * @return User expiry date input.
+ */
+ public String getCryptoExpiryFromUser() {
+ TextUi.displayAddExpiryInstruction();
+ return getUserInput(WORKSPACE);
+ }
+
+ /**
+ * Gets the user crypto expiry input and adds it into the parameters list.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add crypto process.
+ */
+ public void addCryptoExpiryToParameters() throws OperationAbortedError {
+ String expiry;
+ do {
+ expiry = getCryptoExpiryFromUser();
+ checkIfAbort(expiry, WORKSPACE);
+ } while (!Validate.isValidExpiry(expiry));
+ parameters.add(expiry);
+ AssertParserHelper.assertExpiryInTheFuture(expiry);
+ }
+
+ /**
+ * Gets the user crypto remark input and adds it into the parameters list.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add crypto process.
+ */
+ public void addCryptoRemarkToParameters() throws OperationAbortedError {
+ String remark = getCryptoRemarkFromUser();
+ checkIfAbort(remark, WORKSPACE);
+ parameters.add(remark);
+ }
+
+ /**
+ * Gets crypto specific parameters from the user when adding a new crypto instrument.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add crypto process.
+ */
+ public void getCryptoSpecificParameters() throws OperationAbortedError {
+ addCryptoExpiryToParameters();
+ addCryptoRemarkToParameters();
+ }
+
+ /**
+ * Gets from the user all parameters needed to create a new crypto instrument.
+ *
+ * @return A command for adding a new crypto.
+ * @throws OperationAbortedError If the user wants to abort the add crypto process.
+ */
+ @Override
+ public AddCryptoCommand getInstrumentParameters() throws OperationAbortedError {
+ getGeneralParameters(INSTRUMENT_TYPE);
+ getCryptoSpecificParameters();
+ AssertParserHelper.assertNoMissingCryptoParameters(parameters);
+ return new AddCryptoCommand();
+ }
+}
diff --git a/src/main/java/seedu/mtracker/console/AddEtfParser.java b/src/main/java/seedu/mtracker/console/AddEtfParser.java
new file mode 100644
index 0000000000..e2d6e4ee49
--- /dev/null
+++ b/src/main/java/seedu/mtracker/console/AddEtfParser.java
@@ -0,0 +1,88 @@
+package seedu.mtracker.console;
+
+import seedu.mtracker.asserthelpers.AssertParserHelper;
+import seedu.mtracker.commands.AddEtfCommand;
+import seedu.mtracker.commons.Validate;
+import seedu.mtracker.commons.error.OperationAbortedError;
+import seedu.mtracker.ui.TextUi;
+
+//@@author kum-wh
+/**
+ * A class responsible for parsing inputs when user is adding a new etf instrument.
+ */
+public class AddEtfParser extends AddInstrumentParser {
+
+ public static String INSTRUMENT_TYPE = "etf";
+ public static final double UNDEFINED_PAST_RETURN_VALUE = -101;
+
+ /**
+ * Queries and gets etf remark from the user.
+ *
+ * @return User remark input.
+ */
+ public String getEtfRemarkFromUser() {
+ TextUi.displayAddRemarkInstruction();
+ return getUserInput(WORKSPACE);
+ }
+
+ /**
+ * Queries and gets etf past return from the user.
+ *
+ * @return User past return input.
+ * @throws OperationAbortedError If the user wants to abort the add etf process.
+ */
+ public String getEtfPastReturnFromUser() throws OperationAbortedError {
+ TextUi.displayAddPastReturnInstruction();
+ String userInput = getUserInput(WORKSPACE);
+ checkIfAbort(userInput, WORKSPACE);
+ if (!Validate.isValidPastReturn(userInput)) {
+ return String.valueOf(UNDEFINED_PAST_RETURN_VALUE);
+ }
+ return userInput;
+ }
+
+ /**
+ * Gets the user etf remark input and adds it into the parameters list.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add etf process.
+ */
+ public void addEtfRemarkToParameters() throws OperationAbortedError {
+ String remark = getEtfRemarkFromUser();
+ checkIfAbort(remark, WORKSPACE);
+ parameters.add(remark);
+ }
+
+ /**
+ * Gets the user etf past return input and adds it into the parameters list.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add etf process.
+ */
+ public void addEtfPastReturnToParameters() throws OperationAbortedError {
+ String pastReturn = getEtfPastReturnFromUser();
+ parameters.add(pastReturn);
+ }
+
+ /**
+ * Gets etf specific parameters from the user when adding a new etf instrument.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add etf process.
+ */
+ public void getEtfSpecificParameters() throws OperationAbortedError {
+ addEtfPastReturnToParameters();
+ addEtfRemarkToParameters();
+ }
+
+ /**
+ * Gets from the user all parameters needed to create a new etf instrument.
+ *
+ * @return A command for adding a new etf.
+ * @throws OperationAbortedError If the user wants to abort the add etf process.
+ */
+ @Override
+ public AddEtfCommand getInstrumentParameters() throws OperationAbortedError {
+ getGeneralParameters(INSTRUMENT_TYPE);
+ getEtfSpecificParameters();
+ AssertParserHelper.assertNoMissingEtfParameters(parameters);
+ return new AddEtfCommand();
+ }
+}
diff --git a/src/main/java/seedu/mtracker/console/AddForexParser.java b/src/main/java/seedu/mtracker/console/AddForexParser.java
new file mode 100644
index 0000000000..b97f05cff2
--- /dev/null
+++ b/src/main/java/seedu/mtracker/console/AddForexParser.java
@@ -0,0 +1,140 @@
+package seedu.mtracker.console;
+
+import seedu.mtracker.asserthelpers.AssertParserHelper;
+import seedu.mtracker.commands.AddForexCommand;
+import seedu.mtracker.commons.Validate;
+import seedu.mtracker.commons.error.OperationAbortedError;
+import seedu.mtracker.ui.TextUi;
+
+//@@author KVignesh122
+/**
+ * A class responsible for parsing inputs when user is adding a new forex instrument.
+ */
+public class AddForexParser extends AddInstrumentParser {
+ public static String INSTRUMENT_TYPE = "forex";
+
+ /**
+ * Queries and gets forex remark from the user.
+ *
+ * @return User remark input.
+ */
+ public String getForexRemarkFromUser() {
+ TextUi.displayAddRemarkInstruction();
+ return getUserInput(WORKSPACE);
+ }
+
+ /**
+ * Queries and gets forex expiry date from the user.
+ *
+ * @return User expiry date input.
+ */
+ public String getForexExpiryFromUser() {
+ TextUi.displayAddExpiryInstruction();
+ return getUserInput(WORKSPACE);
+ }
+
+ /**
+ * Queries and gets forex entry price from the user.
+ *
+ * @return User entry price input.
+ */
+ public String getForexEntryFromUser() {
+ TextUi.displayAddEntryPriceInstruction();
+ return getUserInput(WORKSPACE);
+ }
+
+ /**
+ * Queries and gets forex exit price from the user.
+ *
+ * @return User exit price input.
+ */
+ public String getForexExitFromUser() {
+ TextUi.displayAddExitPriceInstruction();
+ return getUserInput(WORKSPACE);
+ }
+
+ /**
+ * Gets the user forex remark input and adds it into the parameters list.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add forex process.
+ */
+ public void addForexRemarkToParameter() throws OperationAbortedError {
+ String remark = getForexRemarkFromUser();
+ checkIfAbort(remark, WORKSPACE);
+ parameters.add(remark);
+ }
+
+ /**
+ * Gets the user forex entry price input and adds it into the parameters list.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add forex process.
+ */
+ public void addForexEntryToParameter() throws OperationAbortedError {
+ String entryPrice;
+ do {
+ entryPrice = getForexEntryFromUser();
+ checkIfAbort(entryPrice, WORKSPACE);
+ } while (!Validate.isValidPrice(entryPrice));
+ parameters.add(entryPrice);
+ AssertParserHelper.assertInputNotEmpty(entryPrice);
+ AssertParserHelper.assertPriceNonNegative(entryPrice);
+ }
+
+ /**
+ * Gets the user forex exit price input and adds it into the parameters list.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add forex process.
+ */
+ public void addForexExitToParameter() throws OperationAbortedError {
+ String exitPrice;
+ do {
+ exitPrice = getForexExitFromUser();
+ checkIfAbort(exitPrice, WORKSPACE);
+ } while (!Validate.isValidPrice(exitPrice));
+ parameters.add(exitPrice);
+ AssertParserHelper.assertInputNotEmpty(exitPrice);
+ AssertParserHelper.assertPriceNonNegative(exitPrice);
+ }
+
+ /**
+ * Gets the user forex expiry date input and adds it into the parameters.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add forex process.
+ */
+ public void addForexExpiryToParameter() throws OperationAbortedError {
+ String expiry;
+ do {
+ expiry = getForexExpiryFromUser();
+ checkIfAbort(expiry, WORKSPACE);
+ } while (!Validate.isValidExpiry(expiry));
+ parameters.add(expiry);
+ AssertParserHelper.assertExpiryInTheFuture(expiry);
+ AssertParserHelper.assertInputNotEmpty(expiry);
+ }
+
+ /**
+ * Gets forex specific parameters from the user parameters when adding a new forex instrument.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add forex process.
+ */
+ public void getForexSpecificParameters() throws OperationAbortedError {
+ addForexEntryToParameter();
+ addForexExitToParameter();
+ addForexExpiryToParameter();
+ addForexRemarkToParameter();
+ }
+
+ /**
+ * Gets from the user all parameters needed to create a new forex instrument.
+ *
+ * @return A command for adding a new forex.
+ * @throws OperationAbortedError If the user wants to abort the add forex process.
+ */
+ @Override
+ public AddForexCommand getInstrumentParameters() throws OperationAbortedError {
+ getGeneralParameters(INSTRUMENT_TYPE);
+ getForexSpecificParameters();
+ AssertParserHelper.assertNoMissingForexParameters(parameters);
+ return new AddForexCommand();
+ }
+}
diff --git a/src/main/java/seedu/mtracker/console/AddInstrumentParser.java b/src/main/java/seedu/mtracker/console/AddInstrumentParser.java
new file mode 100644
index 0000000000..b3a74c7911
--- /dev/null
+++ b/src/main/java/seedu/mtracker/console/AddInstrumentParser.java
@@ -0,0 +1,174 @@
+package seedu.mtracker.console;
+
+import seedu.mtracker.LogHelper;
+import seedu.mtracker.asserthelpers.AssertParserHelper;
+import seedu.mtracker.commands.AddCryptoCommand;
+import seedu.mtracker.commands.AddEtfCommand;
+import seedu.mtracker.commands.AddForexCommand;
+import seedu.mtracker.commands.AddInstrumentCommand;
+import seedu.mtracker.commands.AddStockCommand;
+import seedu.mtracker.commons.Validate;
+import seedu.mtracker.commons.error.InvalidInstrumentError;
+import seedu.mtracker.commons.error.OperationAbortedError;
+import seedu.mtracker.ui.TextUi;
+
+import java.util.ArrayList;
+
+//@@author theodorekwok
+/**
+ * An abstract class that is responsible for parsing common parameters found in every instrument type.
+ */
+public abstract class AddInstrumentParser extends InputParser {
+
+ public static final int INSTRUMENT_COMMAND_INDEX = 0;
+
+ protected static ArrayList parameters;
+ protected static final String WORKSPACE = AddInstrumentCommand.COMMAND_WORD;
+
+ /**
+ * Initialises the parameters attribute as a new list.
+ */
+ public void initParameters() {
+ parameters = new ArrayList<>();
+ }
+
+ public ArrayList getParameters() {
+ return parameters;
+ }
+
+ /**
+ * Queries and gets instrument name from the user.
+ *
+ * @param instrumentType The type of instrument the user is adding.
+ * @return User name input.
+ */
+ public static String getInstrumentNameFromUser(String instrumentType) {
+ TextUi.displayAddInstrumentNameInstruction(instrumentType);
+ return getUserInput(WORKSPACE);
+ }
+
+ /**
+ * Gets the user instrument name input and adds it into the parameters list.
+ *
+ * @param instrumentType The type of instrument the user is adding.
+ * @throws OperationAbortedError If the user wants to abort the add instrument process.
+ */
+ public static void addNameToParameters(String instrumentType) throws OperationAbortedError {
+ String name;
+ do {
+ name = getInstrumentNameFromUser(instrumentType);
+ checkIfAbort(name, WORKSPACE);
+ } while (!Validate.isValidName(name, instrumentType));
+ parameters.add(name);
+ AssertParserHelper.assertInputNotEmpty(name);
+ }
+
+ /**
+ * Queries and gets instrument current price from the user.
+ *
+ * @return User current price input.
+ */
+ public static String getCurrentPriceFromUser() {
+ TextUi.displayAddInstrumentCurrentPriceInstruction();
+ return getUserInput(WORKSPACE);
+ }
+
+ /**
+ * Gets the user current price and adds it into the parameters list.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add instrument process.
+ */
+ public static void addCurrentPriceToParameters() throws OperationAbortedError {
+ String currentPrice;
+ do {
+ currentPrice = getCurrentPriceFromUser();
+ checkIfAbort(currentPrice, WORKSPACE);
+ } while (!Validate.isValidPrice(currentPrice));
+ parameters.add(currentPrice);
+ AssertParserHelper.assertInputNotEmpty(currentPrice);
+ AssertParserHelper.assertPriceNonNegative(currentPrice);
+ }
+
+ /**
+ * Queries and gets instrument sentiment from the user.
+ *
+ * @return User sentiment input.
+ */
+ public static String getInstrumentSentimentFromUser() {
+ TextUi.displayAddInstrumentSentimentInstruction();
+ return getUserInput(WORKSPACE);
+ }
+
+ /**
+ * Gets the user sentiment input and adds it into the parameters.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add instrument process.
+ */
+ public static void addSentimentToParameters() throws OperationAbortedError {
+ String sentiment;
+ do {
+ sentiment = getInstrumentSentimentFromUser().toLowerCase();
+ checkIfAbort(sentiment, WORKSPACE);
+ } while (!Validate.isValidSentiment(sentiment));
+ parameters.add(sentiment);
+ AssertParserHelper.assertInputNotEmpty(sentiment);
+ }
+
+ /**
+ * Gets from the user parameters that are required for adding any of the different instrument types.
+ *
+ * @param instrumentType The type of instrument the user is adding.
+ * @throws OperationAbortedError If the user wants to abort the add instrument process.
+ */
+ public static void getGeneralParameters(String instrumentType) throws OperationAbortedError {
+ addNameToParameters(instrumentType);
+ addCurrentPriceToParameters();
+ addSentimentToParameters();
+ AssertParserHelper.assertNoMissingGeneralParameters(parameters);
+ }
+
+ /**
+ * Gets from the user all parameters needed to create a new instrument.
+ *
+ * @return A command for adding a new instrument.
+ * @throws OperationAbortedError If the user wants to abort the add instrument process.
+ */
+ public abstract AddInstrumentCommand getInstrumentParameters() throws OperationAbortedError;
+
+ /**
+ * Filters and starts the add instrument process based on the type of instrument the user is adding.
+ * Sets up the command to be returned with the parameters filled with the user input details.
+ *
+ * @param commandComponents A string array containing the command words the user gave.
+ * @return A command for adding a new instrument.
+ * @throws InvalidInstrumentError If the user gave an invalid instrument type.
+ * @throws OperationAbortedError If the user wants to abort the add instrument process.
+ */
+ public static AddInstrumentCommand filterByInstrumentType(String[] commandComponents)
+ throws InvalidInstrumentError, OperationAbortedError {
+ AddInstrumentCommand command;
+ AddInstrumentParser addInstrumentParser;
+ switch (commandComponents[INSTRUMENT_COMMAND_INDEX]) {
+ case AddStockCommand.COMMAND_WORD:
+ addInstrumentParser = new AddStockParser();
+ break;
+ case AddCryptoCommand.COMMAND_WORD:
+ addInstrumentParser = new AddCryptoParser();
+ break;
+ case AddForexCommand.COMMAND_WORD:
+ addInstrumentParser = new AddForexParser();
+ break;
+ case AddEtfCommand.COMMAND_WORD:
+ addInstrumentParser = new AddEtfParser();
+ break;
+ default:
+ logger.info(LogHelper.LOG_INVALID_INSTRUMENT);
+ throw new InvalidInstrumentError();
+ }
+ addInstrumentParser.initParameters();
+ command = addInstrumentParser.getInstrumentParameters();
+ command.setParams(addInstrumentParser.getParameters());
+
+ return command;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/console/AddStockParser.java b/src/main/java/seedu/mtracker/console/AddStockParser.java
new file mode 100644
index 0000000000..d4cf5bd8f0
--- /dev/null
+++ b/src/main/java/seedu/mtracker/console/AddStockParser.java
@@ -0,0 +1,59 @@
+package seedu.mtracker.console;
+
+import seedu.mtracker.asserthelpers.AssertParserHelper;
+import seedu.mtracker.commands.AddStockCommand;
+import seedu.mtracker.commons.error.OperationAbortedError;
+import seedu.mtracker.ui.TextUi;
+
+//@@author theodorekwok
+/**
+ * A class responsible for parsing inputs when user is adding a new stock instrument.
+ */
+public class AddStockParser extends AddInstrumentParser {
+
+ public static String STOCK_TYPE = "stock";
+
+ /**
+ * Queries and gets stock remark from the user.
+ *
+ * @return User remark input.
+ */
+ public String getStockRemarkFromUser() {
+ TextUi.displayAddRemarkInstruction();
+ return getUserInput(WORKSPACE);
+ }
+
+ /**
+ * Gets the user stock remark input and adds it into the parameters.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add stock process.
+ */
+ public void addStockRemarkToParameters() throws OperationAbortedError {
+ String remark = getStockRemarkFromUser();
+ checkIfAbort(remark, WORKSPACE);
+ parameters.add(remark);
+ }
+
+ /**
+ * Gets stock specific parameters from the user when adding a new stock instrument.
+ *
+ * @throws OperationAbortedError If the user wants to abort the add stock process.
+ */
+ public void getStockSpecificParameters() throws OperationAbortedError {
+ addStockRemarkToParameters();
+ }
+
+ /**
+ * Gets from the user all parameters needed to create a new stock instrument.
+ *
+ * @return A command for adding a new stock.
+ * @throws OperationAbortedError If the user wants to abort the add stock process.
+ */
+ @Override
+ public AddStockCommand getInstrumentParameters() throws OperationAbortedError {
+ getGeneralParameters(STOCK_TYPE);
+ getStockSpecificParameters();
+ AssertParserHelper.assertNoMissingStockParameters(parameters);
+ return new AddStockCommand();
+ }
+}
diff --git a/src/main/java/seedu/mtracker/console/EditInstrumentParser.java b/src/main/java/seedu/mtracker/console/EditInstrumentParser.java
new file mode 100644
index 0000000000..d2f53a04ae
--- /dev/null
+++ b/src/main/java/seedu/mtracker/console/EditInstrumentParser.java
@@ -0,0 +1,268 @@
+package seedu.mtracker.console;
+
+import seedu.mtracker.commands.EditInstrumentCommand;
+import seedu.mtracker.commons.Validate;
+import seedu.mtracker.commons.error.OperationAbortedError;
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.ui.TextUi;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+//@@author kum-wh
+/**
+ * A class responsible for parsing inputs when the user wants to edit an existing instrument.
+ */
+public class EditInstrumentParser extends InputParser {
+
+ protected static HashMap editedParameters;
+
+ protected static final String NAME_ATTRIBUTE = "name";
+ protected static final String CURRENT_PRICE_ATTRIBUTE = "current-price";
+ protected static final String SENTIMENT_ATTRIBUTE = "sentiment";
+ protected static final String REMARK_ATTRIBUTE = "remarks";
+ protected static final String PAST_RETURN_ATTRIBUTE = "past-returns";
+ protected static final String ENTRY_PRICE_ATTRIBUTE = "entry-price";
+ protected static final String EXIT_PRICE_ATTRIBUTE = "exit-price";
+ protected static final String EXPIRY_ATTRIBUTE = "expiry";
+ protected static final String DONE_ATTRIBUTE = "done-status";
+ protected static final String WORKSPACE = EditInstrumentCommand.COMMAND_WORD;
+
+ protected static final double UNDEFINED_PAST_RETURN_VALUE = -101;
+
+ /**
+ * Gets the user new name input and stores it in a hashmap that maps the name attribute to input.
+ * Process is skipped if the user does not want to edit the name.
+ *
+ * @param instrumentType The type of instrument user is editing.
+ * @param parametersGiven The set of parameters of the instrument the user wants to edit.
+ * @throws OperationAbortedError If the user wants to abort the edit instrument process.
+ */
+ public void editNameParameter(String instrumentType, HashSet parametersGiven)
+ throws OperationAbortedError {
+ if (!parametersGiven.contains(NAME_ATTRIBUTE)) {
+ return;
+ }
+ String inputName;
+ do {
+ TextUi.displayEditName();
+ inputName = getUserInput(WORKSPACE);
+ checkIfAbort(inputName, WORKSPACE);
+ } while (!Validate.isValidName(inputName, instrumentType));
+ editedParameters.put(NAME_ATTRIBUTE, inputName);
+ }
+
+ /**
+ * Gets the user new current price input and stores it in a hashmap that maps the current price attribute to input.
+ * Process is skipped if the user does not want to edit the current price.
+ *
+ * @param parametersGiven The set of parameters of the instrument the user wants to edit.
+ * @throws OperationAbortedError If the user wants to abort the edit instrument process.
+ */
+ public void editCurrentPriceParameter(HashSet parametersGiven)
+ throws OperationAbortedError {
+ if (!parametersGiven.contains(CURRENT_PRICE_ATTRIBUTE)) {
+ return;
+ }
+ String inputCurrentPrice;
+ do {
+ TextUi.displayEditCurrentPrice();
+ inputCurrentPrice = getUserInput(WORKSPACE);
+ checkIfAbort(inputCurrentPrice, WORKSPACE);
+ } while (!Validate.isValidPrice(inputCurrentPrice));
+ editedParameters.put(CURRENT_PRICE_ATTRIBUTE, inputCurrentPrice);
+ }
+
+ /**
+ * Gets the user new sentiment input and stores it in a hashmap that maps the sentiment attribute to input.
+ * Process is skipped if the user does not want to edit the sentiment.
+ *
+ * @param parametersGiven The set of parameters of the instrument the user wants to edit.
+ * @throws OperationAbortedError If the user wants to abort the edit instrument process.
+ */
+ public void editSentimentsParameter(HashSet parametersGiven)
+ throws OperationAbortedError {
+ if (!parametersGiven.contains(SENTIMENT_ATTRIBUTE)) {
+ return;
+ }
+ String inputSentiment;
+ do {
+ TextUi.displayEditSentiment();
+ inputSentiment = getUserInput(WORKSPACE).toLowerCase();
+ checkIfAbort(inputSentiment, WORKSPACE);
+ } while (!Validate.isValidSentiment(inputSentiment));
+ editedParameters.put(SENTIMENT_ATTRIBUTE, inputSentiment);
+ }
+
+ /**
+ * Gets the user new remark input and stores it in a hashmap that maps the remark attribute to input.
+ * Process is skipped if the user does not want to edit the remark.
+ *
+ * @param parametersGiven The set of parameters of the instrument the user wants to edit.
+ * @throws OperationAbortedError If the user wants to abort the edit instrument process.
+ */
+ public void editRemarkParameter(HashSet parametersGiven)
+ throws OperationAbortedError {
+ if (!parametersGiven.contains(REMARK_ATTRIBUTE)) {
+ return;
+ }
+ TextUi.displayEditRemark();
+ String inputRemark = getUserInput(WORKSPACE);
+ checkIfAbort(inputRemark, WORKSPACE);
+ editedParameters.put(REMARK_ATTRIBUTE, inputRemark);
+ }
+
+ /**
+ * Gets the user new past return input and stores it in a hashmap that maps the past return attribute to input.
+ * Process is skipped if the user does not want to edit the past return.
+ *
+ * @param parametersGiven The set of parameters of the instrument the user wants to edit.
+ * @throws OperationAbortedError If the user wants to abort the edit instrument process.
+ */
+ public void editPastReturnParameter(HashSet parametersGiven)
+ throws OperationAbortedError {
+ if (!parametersGiven.contains(PAST_RETURN_ATTRIBUTE)) {
+ return;
+ }
+ TextUi.displayEditPastReturn();
+ String inputReturn = getUserInput(WORKSPACE);
+ checkIfAbort(inputReturn, WORKSPACE);
+ if (!Validate.isValidPastReturn(inputReturn)) {
+ inputReturn = String.valueOf(UNDEFINED_PAST_RETURN_VALUE);
+ }
+ editedParameters.put(PAST_RETURN_ATTRIBUTE, inputReturn);
+ }
+
+ /**
+ * Gets the user new entry price input and stores it in a hashmap that maps the entry price attribute to input.
+ * Process is skipped if the user does not want to edit the entry price.
+ *
+ * @param parametersGiven The set of parameters of the instrument the user wants to edit.
+ * @throws OperationAbortedError If the user wants to abort the edit instrument process.
+ */
+ public void editEntryPriceParameter(HashSet parametersGiven)
+ throws OperationAbortedError {
+ if (!parametersGiven.contains(ENTRY_PRICE_ATTRIBUTE)) {
+ return;
+ }
+ String inputEntryPrice;
+ do {
+ TextUi.displayEditEntryPrice();
+ inputEntryPrice = getUserInput(WORKSPACE);
+ checkIfAbort(inputEntryPrice, WORKSPACE);
+ } while (!Validate.isValidPrice(inputEntryPrice));
+ editedParameters.put(ENTRY_PRICE_ATTRIBUTE, inputEntryPrice);
+ }
+
+ /**
+ * Gets the user new exit price input and stores it in a hashmap that maps the exit price attribute to input.
+ * Process is skipped if the user does not want to edit the exit price.
+ *
+ * @param parametersGiven The set of parameters of the instrument the user wants to edit.
+ * @throws OperationAbortedError If the user wants to abort the edit instrument process.
+ */
+ public void editExitPriceParameter(HashSet parametersGiven)
+ throws OperationAbortedError {
+ if (!parametersGiven.contains(EXIT_PRICE_ATTRIBUTE)) {
+ return;
+ }
+ String inputExitPrice;
+ do {
+ TextUi.displayEditExitPrice();
+ inputExitPrice = getUserInput(WORKSPACE);
+ checkIfAbort(inputExitPrice, WORKSPACE);
+ } while (!Validate.isValidPrice(inputExitPrice));
+ editedParameters.put(EXIT_PRICE_ATTRIBUTE, inputExitPrice);
+ }
+
+ /**
+ * Gets the user new done status input and stores it in a hashmap that maps the status attribute to input.
+ * Process is skipped if the user does not want to edit the status.
+ *
+ * @param parametersGiven The set of parameters of the instrument the user wants to edit.
+ * @throws OperationAbortedError If the user wants to abort the edit instrument process.
+ */
+ public void editDoneStatus(HashSet parametersGiven) throws OperationAbortedError {
+ if (!parametersGiven.contains(DONE_ATTRIBUTE)) {
+ return;
+ }
+ String inputStatus;
+ do {
+ TextUi.displayEditStatus();
+ inputStatus = getUserInput(WORKSPACE).toLowerCase();
+ checkIfAbort(inputStatus, WORKSPACE);
+ } while (!Validate.isValidInputStatus(inputStatus));
+ editedParameters.put(DONE_ATTRIBUTE, inputStatus);
+ }
+
+ /**
+ * Gets the user new expiry input and stores it in a hashmap that maps the expiry attribute to input.
+ * Process is skipped if the user does not want to edit the expiry.
+ *
+ * @param parametersGiven The set of parameters of the instrument the user wants to edit.
+ * @throws OperationAbortedError If the user wants to abort the edit instrument process.
+ */
+ public void editExpiryParameter(HashSet parametersGiven)
+ throws OperationAbortedError {
+ if (!parametersGiven.contains(EXPIRY_ATTRIBUTE)) {
+ return;
+ }
+ String inputExpiry;
+ do {
+ TextUi.displayEditExpiry();
+ inputExpiry = getUserInput(WORKSPACE);
+ checkIfAbort(inputExpiry, WORKSPACE);
+ } while (!Validate.isValidExpiry(inputExpiry));
+ editedParameters.put(EXPIRY_ATTRIBUTE, inputExpiry);
+ }
+
+ /**
+ * Gets from the user all the new values to needed to the update the existing instrument.
+ *
+ * @param parametersGiven The set of parameters of the instrument the user wants to edit.
+ * @param instrumentOfInterest The instrument the user wants to edit.
+ * @throws OperationAbortedError If the user wants to abort the edit instrument process.
+ */
+ public void getEditedParameters(HashSet parametersGiven, Instrument instrumentOfInterest)
+ throws OperationAbortedError {
+ String instrumentType = instrumentOfInterest.getType().toLowerCase();
+ editNameParameter(instrumentType, parametersGiven);
+ editCurrentPriceParameter(parametersGiven);
+ editSentimentsParameter(parametersGiven);
+ editPastReturnParameter(parametersGiven);
+ editEntryPriceParameter(parametersGiven);
+ editExitPriceParameter(parametersGiven);
+ editExpiryParameter(parametersGiven);
+ editRemarkParameter(parametersGiven);
+ editDoneStatus(parametersGiven);
+ }
+
+ /**
+ * Creates the edit command and gets from the user the new values to update the instrument with.
+ *
+ * @param parametersGiven The set of parameters of the instrument the user wants to edit.
+ * @param instrumentOfInterest The instrument the user wants to edit.
+ * @param instrumentIndex The index number of the instrument to edit.
+ * @return A command for editing an existing instrument.
+ * @throws OperationAbortedError If the user wants to abort the edit instrument process.
+ */
+ public EditInstrumentCommand createEditCommand(HashSet parametersGiven,
+ Instrument instrumentOfInterest, int instrumentIndex)
+ throws OperationAbortedError {
+ EditInstrumentCommand command;
+ editedParameters = new HashMap<>();
+ getEditedParameters(parametersGiven, instrumentOfInterest);
+ command = new EditInstrumentCommand(editedParameters);
+ command.setIndex(instrumentIndex);
+ return command;
+ }
+
+ /**
+ * Gets the hashmap containing the instrument attributes and its edited value.
+ *
+ * @return A hashmap that contains the instrument attributes and its new value.
+ */
+ public static HashMap getEditedParametersHash() {
+ return editedParameters;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/console/InputParser.java b/src/main/java/seedu/mtracker/console/InputParser.java
new file mode 100644
index 0000000000..a5c0c2adec
--- /dev/null
+++ b/src/main/java/seedu/mtracker/console/InputParser.java
@@ -0,0 +1,361 @@
+package seedu.mtracker.console;
+
+import seedu.mtracker.LogHelper;
+import seedu.mtracker.commands.AddInstrumentCommand;
+import seedu.mtracker.commands.Command;
+import seedu.mtracker.commands.DeleteCommand;
+import seedu.mtracker.commands.DoneCommand;
+import seedu.mtracker.commands.EditInstrumentCommand;
+import seedu.mtracker.commands.ExitCommand;
+import seedu.mtracker.commands.FindCommand;
+import seedu.mtracker.commands.ListCommand;
+import seedu.mtracker.commands.ViewCommand;
+import seedu.mtracker.commons.Validate;
+import seedu.mtracker.commons.error.AlreadyDoneError;
+import seedu.mtracker.commons.error.EditEmptyParameterError;
+import seedu.mtracker.commons.error.InvalidBoundsError;
+import seedu.mtracker.commons.error.InvalidCommandError;
+import seedu.mtracker.commons.error.InvalidEmptyIndexError;
+import seedu.mtracker.commons.error.InvalidEmptySearchStringError;
+import seedu.mtracker.commons.error.InvalidIndexError;
+import seedu.mtracker.commons.error.InvalidInstrumentError;
+import seedu.mtracker.commons.error.OperationAbortedError;
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.ui.TextUi;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Scanner;
+import java.util.logging.Logger;
+
+//@@author theodorekwok
+/**
+ * A class responsible for all the user main command inputs.
+ */
+public class InputParser {
+
+ public static final String SEPARATOR = " ";
+ public static final int INDEX_OFFSET = 1;
+ public static final int INSTRUMENT_INDEX = 1;
+ public static final int SEARCH_STR_INDEX_START = 1;
+ public static final String ABORTED = "abort";
+
+ public static final int MAIN_COMMAND_INDEX = 0;
+
+ protected static final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ protected static Scanner inputScanner;
+
+ private int instrumentNumber;
+ private String searchString;
+
+ /**
+ * Constructs the InputParser object and initialises it with a scanner for getting user input.
+ */
+ public InputParser() {
+ inputScanner = new Scanner(System.in);
+ }
+
+ /**
+ * Prompts the user to provide an input.
+ *
+ * @param currentWorkspace The current workflow the user is in.
+ * @return The user input.
+ */
+ public static String getUserInput(String currentWorkspace) {
+ TextUi.displayPrompter(currentWorkspace);
+ return inputScanner.nextLine().trim();
+ }
+
+ public int getInstrumentNumber() {
+ return instrumentNumber;
+ }
+
+ /**
+ * Starts the add instrument workflow and prompts the user provide the type of instrument to add.
+ *
+ * @return A command to add a new instrument.
+ * @throws InvalidInstrumentError If the user gave an invalid instrument type.
+ * @throws OperationAbortedError If the user wants to abort the add instrument process.
+ */
+ public AddInstrumentCommand getAddInstrumentParameters()
+ throws InvalidInstrumentError, OperationAbortedError {
+ String addInstrumentType;
+ do {
+ TextUi.displayAddInstrumentFirstInstruction();
+ addInstrumentType = getUserInput(AddInstrumentCommand.COMMAND_WORD).toLowerCase();
+ checkIfAbort(addInstrumentType, AddInstrumentCommand.COMMAND_WORD);
+ } while (!Validate.isValidInstrument(addInstrumentType));
+ return AddInstrumentParser.filterByInstrumentType(getCommandComponents(addInstrumentType));
+ }
+
+ /**
+ * Checks and prepares the delete command when the user wants to delete an instrument.
+ *
+ * @param commandComponents A string array containing the command words the user gave.
+ * @param instruments The list of instruments currently in the user's watchlist.
+ * @return A command to delete an instrument.
+ * @throws InvalidIndexError If the user does not provide a valid number.
+ * @throws InvalidEmptyIndexError If the user does not provide any number.
+ * @throws InvalidBoundsError If the user gives a number that is outside the range of the instrument list.
+ */
+ public DeleteCommand getDeleteInstrumentCommand(String[] commandComponents, ArrayList instruments)
+ throws InvalidIndexError, InvalidEmptyIndexError, InvalidBoundsError {
+ DeleteCommand deleteCommand = new DeleteCommand();
+ getAndValidateIndexNumber(commandComponents, instruments);
+ deleteCommand.setIndex(instrumentNumber);
+ return deleteCommand;
+ }
+
+ //@@author KVignesh122
+ /**
+ * Checks and prepares the view command when the user wants to view a specific instrument.
+ *
+ * @param commandComponents A string array containing the command words the user gave.
+ * @param instruments The list of instruments currently in the user's watchlist.
+ * @return A command to view an instrument.
+ * @throws InvalidIndexError If the user does not provide a valid number.
+ * @throws InvalidEmptyIndexError If the user does not provide any number.
+ * @throws InvalidBoundsError If the user gives a number that is outside the range of the instrument list.
+ */
+ public ViewCommand getViewInstrumentCommand(String[] commandComponents, ArrayList instruments)
+ throws InvalidIndexError, InvalidEmptyIndexError, InvalidBoundsError {
+ ViewCommand viewCommand = new ViewCommand();
+ getAndValidateIndexNumber(commandComponents, instruments);
+ viewCommand.setIndex(instrumentNumber);
+ return viewCommand;
+ }
+
+ /**
+ * Checks and prepare the find command when a user wants to find instruments.
+ *
+ * @param commandComponents A string array containing the command words the user gave.
+ * @return A command to find instruments.
+ * @throws InvalidEmptySearchStringError If the user does not provide a search string.
+ */
+ public FindCommand getFindInstrumentsCommand(String[] commandComponents)
+ throws InvalidEmptySearchStringError {
+ FindCommand findCommand = new FindCommand();
+ constructSearchString(commandComponents);
+ findCommand.setSearchString(searchString);
+ return findCommand;
+ }
+
+ //@@author williamlamjy
+ /**
+ * Checks and prepares the done command when the user wants to set the instrument status as done.
+ *
+ * @param commandComponents A string array containing the command words the user gave.
+ * @param instruments The list of instruments currently in the user's watchlist.
+ * @return A command to set the status of an instrument as completed.
+ * @throws InvalidIndexError If the user does not provide a valid number.
+ * @throws InvalidEmptyIndexError If the user does not provide any number.
+ * @throws InvalidBoundsError If the user gives a number that is outside the range of the instrument list.
+ * @throws AlreadyDoneError If the user tries to set the status of an already done instrument.
+ */
+ public DoneCommand getDoneInstrumentCommand(String[] commandComponents, ArrayList instruments)
+ throws InvalidIndexError, InvalidEmptyIndexError, InvalidBoundsError, AlreadyDoneError {
+ DoneCommand doneCommand = new DoneCommand();
+ getAndValidateIndexNumber(commandComponents, instruments);
+ getAndValidateDoneStatus(commandComponents, instruments);
+ doneCommand.setIndex(instrumentNumber);
+ return doneCommand;
+ }
+
+ //@@author kum-wh
+ /**
+ * Checks and filters the user given parameters based on the instrument of interest set of valid attributes.
+ *
+ * @param parametersToEdit The set of parameters of the instrument the user wants to edit.
+ * @param validAttributes The set of acceptable attributes of the instrument of interest.
+ * @return A set containing the remaining valid parameters after filtering.
+ */
+ public HashSet filterInvalidParameters(String[] parametersToEdit, HashSet validAttributes) {
+ HashSet filteredAttributes = new HashSet<>();
+ for (String param : parametersToEdit) {
+ if (validAttributes.contains(param)) {
+ filteredAttributes.add(param);
+ } else {
+ TextUi.displayEditInvalidAttribute(param);
+ }
+ }
+ return filteredAttributes;
+ }
+
+ /**
+ * Gets from the user the attributes to edit.
+ *
+ * @param validAttributes The set of acceptable attributes of the instrument of interest.
+ * @return A set containing valid parameters.
+ * @throws OperationAbortedError If the user wants to abort the edit instrument process.
+ * @throws EditEmptyParameterError If the user did not provide any parameters.
+ */
+ public HashSet getParametersToEdit(HashSet validAttributes)
+ throws OperationAbortedError, EditEmptyParameterError {
+ String parametersToEdit = getUserInput(EditInstrumentCommand.COMMAND_WORD).toLowerCase();
+ if (!Validate.isNonEmptyEditParameters(parametersToEdit)) {
+ throw new EditEmptyParameterError();
+ }
+ checkIfAbort(parametersToEdit, EditInstrumentCommand.COMMAND_WORD);
+ String[] parameters = getCommandComponents(parametersToEdit);
+ return filterInvalidParameters(parameters, validAttributes);
+ }
+
+ /**
+ * Checks and prepares the edit command when the user wants to edit an existing instrument.
+ *
+ * @param commandComponents A string array containing the command words the user gave.
+ * @param instruments The list of instruments currently in the user's watchlist.
+ * @return A command to edit an existing instrument.
+ * @throws InvalidIndexError If the user does not provide a valid number.
+ * @throws InvalidEmptyIndexError If the user does not provide any number.
+ * @throws InvalidBoundsError If the user gives a number that is outside the range of the instrument list.
+ * @throws OperationAbortedError If the user wants to abort the edit instrument process.
+ * @throws EditEmptyParameterError If the user did not provide any parameters.
+ */
+ public EditInstrumentCommand getEditInstrumentCommand(String[] commandComponents, ArrayList instruments)
+ throws InvalidIndexError, InvalidEmptyIndexError, InvalidBoundsError,
+ OperationAbortedError, EditEmptyParameterError {
+ getAndValidateIndexNumber(commandComponents, instruments);
+ Instrument instrumentToEdit = instruments.get(instrumentNumber);
+ TextUi.displayEditInstrumentFirstInstruction(instrumentToEdit);
+ HashSet parametersToEdit = getParametersToEdit(instrumentToEdit.getValidAttribute());
+ EditInstrumentParser editInstrumentParser = new EditInstrumentParser();
+ return editInstrumentParser.createEditCommand(parametersToEdit, instrumentToEdit, instrumentNumber);
+ }
+ //@@author
+
+ /**
+ * Checks if the index number provided is valid.
+ *
+ * @param commandComponents A string array containing the command words the user gave.
+ * @param instruments The list of instruments currently in the user's watchlist.
+ * @throws InvalidIndexError If the user does not provide a valid number.
+ * @throws InvalidEmptyIndexError If the user does not provide any number.
+ * @throws InvalidBoundsError If the user gives a number that is outside the range of the instrument list.
+ */
+ private void getAndValidateIndexNumber(String[] commandComponents, ArrayList instruments)
+ throws InvalidEmptyIndexError, InvalidIndexError, InvalidBoundsError {
+ getIndexNumber(commandComponents);
+ Validate.validateIndexWithinBounds(instruments, instrumentNumber);
+ }
+
+ /**
+ * Checks if the instrument is already done.
+ *
+ * @param commandComponents A string array containing the command words the user gave.
+ * @param instruments The list of instruments currently in the user's watchlist.
+ * @throws AlreadyDoneError If the user tries to set the status of an already done instrument.
+ * @throws InvalidIndexError If the user does not provide a valid number.
+ * @throws InvalidEmptyIndexError If the user does not provide any number.
+ */
+ private void getAndValidateDoneStatus(String[] commandComponents, ArrayList instruments)
+ throws AlreadyDoneError, InvalidEmptyIndexError, InvalidIndexError {
+ getIndexNumber(commandComponents);
+ Validate.checkIsNotDone(instruments, instrumentNumber);
+ }
+
+ //@@author theodorekwok
+ /**
+ * Filters and returns the command type based on the user input.
+ *
+ * @param commandComponents A string array containing the command words the user gave.
+ * @param instruments The list of instruments currently in the user's watchlist.
+ * @return The command the user wants to execute.
+ * @throws Exception If any of the user's addition or lack of inputs violates the command requirements.
+ */
+ public Command filterByCommandType(String[] commandComponents, ArrayList instruments)
+ throws Exception {
+ Command command;
+ String commandGiven = commandComponents[MAIN_COMMAND_INDEX].toLowerCase();
+ switch (commandGiven) {
+ case ListCommand.COMMAND_WORD:
+ command = new ListCommand();
+ break;
+ case AddInstrumentCommand.COMMAND_WORD:
+ command = getAddInstrumentParameters();
+ break;
+ case DeleteCommand.COMMAND_WORD:
+ command = getDeleteInstrumentCommand(commandComponents, instruments);
+ break;
+ case ViewCommand.COMMAND_WORD:
+ command = getViewInstrumentCommand(commandComponents, instruments);
+ break;
+ case DoneCommand.COMMAND_WORD:
+ command = getDoneInstrumentCommand(commandComponents, instruments);
+ break;
+ case EditInstrumentCommand.COMMAND_WORD:
+ command = getEditInstrumentCommand(commandComponents, instruments);
+ break;
+ case FindCommand.COMMAND_WORD:
+ command = getFindInstrumentsCommand(commandComponents);
+ break;
+ case ExitCommand.COMMAND_WORD:
+ command = new ExitCommand();
+ break;
+ default:
+ logger.info(LogHelper.LOG_INVALID_COMMAND);
+ throw new InvalidCommandError(commandGiven);
+ }
+ return command;
+ }
+
+ /**
+ * Takes the user input and split it into individual words.
+ * Removes any additional spaces in the front and back of the input.
+ *
+ * @param commandInput The user input.
+ * @return A string array containing the command words the user gave.
+ */
+ public String[] getCommandComponents(String commandInput) {
+ return commandInput.trim().split(SEPARATOR);
+ }
+
+ /**
+ * Parses the user input and gets the index value.
+ *
+ * @param commandComponents A string array containing the command words the user gave.
+ * @throws InvalidEmptyIndexError If the user does not provide any number.
+ * @throws InvalidIndexError If the user does not provide a valid number.
+ */
+ public void getIndexNumber(String[] commandComponents) throws InvalidEmptyIndexError, InvalidIndexError {
+ try {
+ instrumentNumber = Integer.parseInt(commandComponents[INSTRUMENT_INDEX]) - INDEX_OFFSET;
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidEmptyIndexError();
+ } catch (NumberFormatException e) {
+ throw new InvalidIndexError();
+ }
+ }
+
+ //@@author KVignesh122
+ /**
+ * Builds the search string the user wants to use to find the instruments.
+ *
+ * @param commandComponents A string array containing the command words the user gave.
+ * @throws InvalidEmptySearchStringError If the user does not provide a search string.
+ */
+ public void constructSearchString(String[] commandComponents) throws InvalidEmptySearchStringError {
+ try {
+ searchString = commandComponents[SEARCH_STR_INDEX_START];
+ for (int i = SEARCH_STR_INDEX_START + 1; i < commandComponents.length; i++) {
+ searchString += SEPARATOR + commandComponents[i];
+ }
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidEmptySearchStringError();
+ }
+ }
+
+ /**
+ * Checks whether the user wants to abort either the add or edit process.
+ *
+ * @param userInput The user input.
+ * @param currentProcess The current workflow the user is in.
+ * @throws OperationAbortedError If the user wants to abort the add/edit instrument process.
+ */
+ public static void checkIfAbort(String userInput, String currentProcess)
+ throws OperationAbortedError {
+ if (userInput.equalsIgnoreCase(ABORTED)) {
+ throw new OperationAbortedError(currentProcess);
+ }
+ }
+}
diff --git a/src/main/java/seedu/mtracker/filemanager/CryptoDecoder.java b/src/main/java/seedu/mtracker/filemanager/CryptoDecoder.java
new file mode 100644
index 0000000000..edff3ab3fc
--- /dev/null
+++ b/src/main/java/seedu/mtracker/filemanager/CryptoDecoder.java
@@ -0,0 +1,133 @@
+package seedu.mtracker.filemanager;
+
+import seedu.mtracker.commons.Validate;
+import seedu.mtracker.commons.error.fileerror.InvalidCurrPriceSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyCurrPriceInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyExpiryInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyNameInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptySentimentInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyStatusInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidExpirySavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidNameSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidRemarkInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidSentimentSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidStatusSavedInFileError;
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.model.InstrumentManager;
+import seedu.mtracker.model.subinstrument.Crypto;
+
+import java.time.LocalDate;
+
+//@@author williamlamjy
+/**
+ * Decodes and adds crypto instruments into the InstrumentManager.
+ */
+public class CryptoDecoder extends InstrumentDecoder {
+
+ public static final int CRYPTO_EXPIRY_INDEX = 5;
+ public static final int CRYPTO_REMARK_INDEX = 6;
+ protected static LocalDate decodedExpiry;
+ protected static String decodedRemark;
+
+ /**
+ * Gets expiry from the mTracker file.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @return Expiry of the crypto.
+ * @throws InvalidEmptyExpiryInFileError When the expiry parameter is empty in the file.
+ */
+ public static String getExpiryFromFile(String[] textSegment) throws InvalidEmptyExpiryInFileError {
+ String expiry;
+ try {
+ expiry = textSegment[CRYPTO_EXPIRY_INDEX];
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidEmptyExpiryInFileError();
+ }
+ return expiry;
+ }
+
+ /**
+ * Gets remark from the mTracker file.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @return Remark of the instrument.
+ * @throws InvalidRemarkInFileError When the remark parameter is of invalid format.
+ */
+ public static String getRemarkFromFile(String[] textSegment) throws InvalidRemarkInFileError {
+ String remark;
+ try {
+ remark = textSegment[CRYPTO_REMARK_INDEX];
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidRemarkInFileError();
+ }
+ return remark;
+ }
+
+ /**
+ * Validates that specific crypto attributes are of the right format.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @throws InvalidExpirySavedInFileError When the expiry parameter is of invalid format.
+ */
+ private static void validateSpecificAttributes(String[] textSegment) throws InvalidExpirySavedInFileError {
+ if (!Validate.isValidExpiry(textSegment[CRYPTO_EXPIRY_INDEX])) {
+ throw new InvalidExpirySavedInFileError();
+ }
+ }
+
+ private static void decodeSpecificAttributes(String expiry, String remark) {
+ decodedExpiry = LocalDate.parse(expiry);
+ decodedRemark = remark;
+ }
+
+ /**
+ * Validates and decodes the specific attributes of the crypto.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @throws InvalidEmptyExpiryInFileError When the expiry parameter is empty in the file.
+ * @throws InvalidRemarkInFileError When the remark parameter is of invalid format.
+ * @throws InvalidExpirySavedInFileError When the expiry parameter is of invalid format.
+ */
+ public static void validateAndDecodeSpecificAttributes(String[] textSegment) throws InvalidEmptyExpiryInFileError,
+ InvalidRemarkInFileError, InvalidExpirySavedInFileError {
+ String expiry = getExpiryFromFile(textSegment);
+ String remark = getRemarkFromFile(textSegment);
+ validateSpecificAttributes(textSegment);
+ decodeSpecificAttributes(expiry, remark);
+ }
+
+ /**
+ * Adds the validated and decoded crypto to the InstrumentManager.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @param instrumentManager Current InstrumentManager.
+ * @throws InvalidNameSavedInFileError When the name parameter is of invalid format.
+ * @throws InvalidSentimentSavedInFileError When the sentiment parameter is of invalid format.
+ * @throws InvalidCurrPriceSavedInFileError When the current price parameter is of invalid format.
+ * @throws InvalidEmptyNameInFileError When the name parameter is empty in the file.
+ * @throws InvalidEmptyCurrPriceInFileError When the current price parameter is empty in the file.
+ * @throws InvalidEmptySentimentInFileError When the sentiment parameter is empty in the file.
+ * @throws InvalidEmptyStatusInFileError When the done status parameter is empty in the file.
+ * @throws InvalidStatusSavedInFileError When the done status parameter is of invalid format.
+ * @throws InvalidEmptyExpiryInFileError When the expiry parameter is empty in the file.
+ * @throws InvalidRemarkInFileError When the remark parameter is of invalid format.
+ * @throws InvalidExpirySavedInFileError When the expiry parameter is of invalid format.
+ */
+ public static void addCryptoToList(String[] textSegment, InstrumentManager instrumentManager)
+ throws InvalidNameSavedInFileError, InvalidSentimentSavedInFileError, InvalidCurrPriceSavedInFileError,
+ InvalidEmptyNameInFileError, InvalidEmptySentimentInFileError, InvalidEmptyStatusInFileError,
+ InvalidStatusSavedInFileError, InvalidEmptyCurrPriceInFileError, InvalidEmptyExpiryInFileError,
+ InvalidRemarkInFileError, InvalidExpirySavedInFileError {
+ validateAndDecodeGeneralAttributes(textSegment);
+ validateAndDecodeSpecificAttributes(textSegment);
+ Instrument crypto = createDecodedInstrument();
+ setDoneStatus(decodedIsDone, crypto);
+ instrumentManager.addInstrument(crypto);
+ }
+
+ private static Instrument createDecodedInstrument() {
+ return new Crypto(decodedName, decodedCurrPrice, decodedSentiment,
+ decodedExpiry, decodedRemark);
+ }
+
+}
diff --git a/src/main/java/seedu/mtracker/filemanager/EtfDecoder.java b/src/main/java/seedu/mtracker/filemanager/EtfDecoder.java
new file mode 100644
index 0000000000..eb5d6e54d9
--- /dev/null
+++ b/src/main/java/seedu/mtracker/filemanager/EtfDecoder.java
@@ -0,0 +1,125 @@
+package seedu.mtracker.filemanager;
+
+import seedu.mtracker.commons.Validate;
+import seedu.mtracker.commons.error.fileerror.InvalidCurrPriceSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyCurrPriceInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyNameInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptySentimentInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyStatusInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidNameSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidPastReturnSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidRemarkInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidSentimentSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidStatusSavedInFileError;
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.model.InstrumentManager;
+import seedu.mtracker.model.subinstrument.Etf;
+
+//@@author williamlamjy
+public class EtfDecoder extends InstrumentDecoder {
+
+ public static final int PAST_RETURN_INDEX = 5;
+ public static final int ETF_REMARK_INDEX = 6;
+ public static final double EMPTY_PAST_RETURN = -101.0;
+ protected static double decodedPastReturn;
+ protected static String decodedRemark;
+
+ /**
+ * Gets past return from the mTracker file.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @return Past return of the Etf.
+ * @throws InvalidPastReturnSavedInFileError When the past return parameter is of invalid format.
+ */
+ public static String getPastReturnFromFile(String[] textSegment) throws InvalidPastReturnSavedInFileError {
+ String pastReturn;
+ try {
+ pastReturn = textSegment[PAST_RETURN_INDEX];
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidPastReturnSavedInFileError();
+ }
+ return pastReturn;
+ }
+
+ /**
+ * Gets remark from the mTracker file.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @return Remark of the instrument.
+ * @throws InvalidRemarkInFileError When the remark parameter is of invalid format.
+ */
+ public static String getRemarkFromFile(String[] textSegment) throws InvalidRemarkInFileError {
+ String remark;
+ try {
+ remark = textSegment[ETF_REMARK_INDEX];
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidRemarkInFileError();
+ }
+ return remark;
+ }
+
+ private static void decodeSpecificAttributes(String pastReturn, String remark) {
+ if (pastReturn.isEmpty()) {
+ decodedPastReturn = EMPTY_PAST_RETURN;
+ } else {
+ decodedPastReturn = Double.parseDouble(pastReturn);
+ }
+ decodedRemark = remark;
+ }
+
+ private static void validateSpecificAttributes(String[] textSegment, String pastReturn)
+ throws InvalidPastReturnSavedInFileError {
+ if (!pastReturn.isEmpty() && !Validate.isValidPastReturn(textSegment[PAST_RETURN_INDEX])) {
+ throw new InvalidPastReturnSavedInFileError();
+ }
+ }
+
+ /**
+ * Validates and decodes the specific attributes of the Etf.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @throws InvalidRemarkInFileError When the remark parameter is of invalid format.
+ * @throws InvalidPastReturnSavedInFileError When the past return parameter is of invalid format.
+ */
+ public static void validateAndDecodeSpecificAttributes(String[] textSegment) throws
+ InvalidRemarkInFileError, InvalidPastReturnSavedInFileError {
+ String pastReturn = getPastReturnFromFile(textSegment);
+ String remark = getRemarkFromFile(textSegment);
+ validateSpecificAttributes(textSegment, pastReturn);
+ decodeSpecificAttributes(pastReturn, remark);
+ }
+
+ /**
+ * Adds the validated and decoded Etf to the InstrumentManager.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @param instrumentManager Current InstrumentManager.
+ * @throws InvalidNameSavedInFileError When the name parameter is of invalid format.
+ * @throws InvalidSentimentSavedInFileError When the sentiment parameter is of invalid format.
+ * @throws InvalidCurrPriceSavedInFileError When the current price parameter is of invalid format.
+ * @throws InvalidEmptyNameInFileError When the name parameter is empty in the file.
+ * @throws InvalidEmptyCurrPriceInFileError When the current price parameter is empty in the file.
+ * @throws InvalidEmptySentimentInFileError When the sentiment parameter is empty in the file.
+ * @throws InvalidEmptyStatusInFileError When the done status parameter is empty in the file.
+ * @throws InvalidStatusSavedInFileError When the done status parameter is of invalid format.
+ * @throws InvalidRemarkInFileError When the remark parameter is of invalid format.
+ * @throws InvalidPastReturnSavedInFileError When the past return parameter is of invalid format.
+ */
+ public static void addEtfToList(String[] textSegment, InstrumentManager instrumentManager)
+ throws InvalidNameSavedInFileError, InvalidSentimentSavedInFileError, InvalidCurrPriceSavedInFileError,
+ InvalidEmptyNameInFileError, InvalidEmptySentimentInFileError, InvalidEmptyStatusInFileError,
+ InvalidStatusSavedInFileError, InvalidEmptyCurrPriceInFileError, InvalidPastReturnSavedInFileError,
+ InvalidRemarkInFileError {
+ validateAndDecodeGeneralAttributes(textSegment);
+ validateAndDecodeSpecificAttributes(textSegment);
+ Instrument etf = createDecodedInstrument();
+ setDoneStatus(decodedIsDone, etf);
+ instrumentManager.addInstrument(etf);
+ }
+
+ private static Instrument createDecodedInstrument() {
+ return new Etf(decodedName, decodedCurrPrice, decodedSentiment,
+ decodedPastReturn, decodedRemark);
+ }
+
+}
diff --git a/src/main/java/seedu/mtracker/filemanager/ForexDecoder.java b/src/main/java/seedu/mtracker/filemanager/ForexDecoder.java
new file mode 100644
index 0000000000..d1ad155722
--- /dev/null
+++ b/src/main/java/seedu/mtracker/filemanager/ForexDecoder.java
@@ -0,0 +1,190 @@
+package seedu.mtracker.filemanager;
+
+import seedu.mtracker.commons.Validate;
+import seedu.mtracker.commons.error.fileerror.InvalidCurrPriceSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyCurrPriceInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyEntryPriceInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyExitPriceInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyExpiryInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyNameInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptySentimentInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyStatusInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEntryPriceSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidExitPriceSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidExpirySavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidNameSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidRemarkInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidSentimentSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidStatusSavedInFileError;
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.model.InstrumentManager;
+import seedu.mtracker.model.subinstrument.Forex;
+
+import java.time.LocalDate;
+
+//@@author williamlamjy
+/**
+ * Decodes and adds forex instruments into the InstrumentManager.
+ */
+public class ForexDecoder extends InstrumentDecoder {
+
+ public static final int ENTRY_PRICE_INDEX = 5;
+ public static final int EXIT_PRICE_INDEX = 6;
+ public static final int FOREX_EXPIRY_INDEX = 7;
+ public static final int FOREX_REMARK_INDEX = 8;
+ protected static double decodedEntryPrice;
+ protected static double decodedExitPrice;
+ protected static LocalDate decodedExpiry;
+ protected static String decodedRemark;
+
+ /**
+ * Gets entry price from the mTracker file.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @return Entry price of the forex
+ * @throws InvalidEmptyEntryPriceInFileError When the entry price parameter is empty in the file.
+ */
+ public static String getEntryPriceFromFile(String[] textSegment) throws InvalidEmptyEntryPriceInFileError {
+ String entryPrice;
+ try {
+ entryPrice = textSegment[ENTRY_PRICE_INDEX];
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidEmptyEntryPriceInFileError();
+ }
+ return entryPrice;
+ }
+
+ /**
+ * Gets exit price from the mTracker file.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @return Exit price of the forex
+ * @throws InvalidEmptyExitPriceInFileError When the exit price parameter is empty in the file.
+ */
+ public static String getExitPriceFromFile(String[] textSegment) throws InvalidEmptyExitPriceInFileError {
+ String exitPrice;
+ try {
+ exitPrice = textSegment[EXIT_PRICE_INDEX];
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidEmptyExitPriceInFileError();
+ }
+ return exitPrice;
+ }
+
+ /**
+ * Gets expiry from the mTracker file.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @return Expiry of the forex.
+ * @throws InvalidEmptyExpiryInFileError When the expiry parameter is empty in the file.
+ */
+ public static String getExpiryFromFile(String[] textSegment) throws InvalidEmptyExpiryInFileError {
+ String expiry;
+ try {
+ expiry = textSegment[FOREX_EXPIRY_INDEX];
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidEmptyExpiryInFileError();
+ }
+ return expiry;
+ }
+
+ /**
+ * Gets remark from the mTracker file.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @return Remark of the instrument.
+ * @throws InvalidRemarkInFileError When the remark parameter is of invalid format.
+ */
+ public static String getRemarkFromFile(String[] textSegment) throws InvalidRemarkInFileError {
+ String remark;
+ try {
+ remark = textSegment[FOREX_REMARK_INDEX];
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidRemarkInFileError();
+ }
+ return remark;
+ }
+
+ /**
+ * Validates and decodes the specific attributes of the forex.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @throws InvalidEmptyEntryPriceInFileError When the entry price parameter is empty in the file.
+ * @throws InvalidEmptyExitPriceInFileError When the exit price parameter is empty in the file.
+ * @throws InvalidEmptyExpiryInFileError When the expiry parameter is empty in the file.
+ * @throws InvalidRemarkInFileError When the remark parameter is of invalid format.
+ * @throws InvalidExpirySavedInFileError When the expiry parameter is of invalid format.
+ * @throws InvalidEntryPriceSavedInFileError When the entry price parameter is of invalid format.
+ * @throws InvalidExitPriceSavedInFileError When the exit price parameter is of invalid format.
+ */
+ public static void validateAndDecodeSpecificAttributes(String[] textSegment)
+ throws InvalidEmptyEntryPriceInFileError, InvalidEmptyExitPriceInFileError, InvalidEmptyExpiryInFileError,
+ InvalidRemarkInFileError, InvalidEntryPriceSavedInFileError, InvalidExitPriceSavedInFileError,
+ InvalidExpirySavedInFileError {
+ String entryPrice = getEntryPriceFromFile(textSegment);
+ String exitPrice = getExitPriceFromFile(textSegment);
+ String expiry = getExpiryFromFile(textSegment);
+ String remark = getRemarkFromFile(textSegment);
+ validateSpecificAttributes(textSegment);
+ decodeSpecificAttributes(entryPrice, exitPrice, expiry, remark);
+ }
+
+ private static void validateSpecificAttributes(String[] textSegment) throws InvalidExpirySavedInFileError,
+ InvalidExitPriceSavedInFileError, InvalidEntryPriceSavedInFileError {
+ if (!Validate.isValidPrice(textSegment[ENTRY_PRICE_INDEX])) {
+ throw new InvalidEntryPriceSavedInFileError();
+ }
+ if (!Validate.isValidPrice(textSegment[EXIT_PRICE_INDEX])) {
+ throw new InvalidExitPriceSavedInFileError();
+ }
+ if (!Validate.isValidExpiry(textSegment[FOREX_EXPIRY_INDEX])) {
+ throw new InvalidExpirySavedInFileError();
+ }
+ }
+
+ private static void decodeSpecificAttributes(String entryPrice, String exitPrice, String expiry, String remark) {
+ decodedEntryPrice = Double.parseDouble(entryPrice);
+ decodedExitPrice = Double.parseDouble(exitPrice);
+ decodedExpiry = LocalDate.parse(expiry);
+ decodedRemark = remark;
+ }
+
+ /**
+ * Adds the validated and decoded forex to the InstrumentManager.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @param instrumentManager Current InstrumentManager.
+ * @throws InvalidNameSavedInFileError When the name parameter is of invalid format.
+ * @throws InvalidSentimentSavedInFileError When the sentiment parameter is of invalid format.
+ * @throws InvalidCurrPriceSavedInFileError When the current price parameter is of invalid format.
+ * @throws InvalidEmptyNameInFileError When the name parameter is empty in the file.
+ * @throws InvalidEmptyCurrPriceInFileError When the current price parameter is empty in the file.
+ * @throws InvalidEmptySentimentInFileError When the sentiment parameter is empty in the file.
+ * @throws InvalidEmptyStatusInFileError When the done status parameter is empty in the file.
+ * @throws InvalidStatusSavedInFileError When the done status parameter is of invalid format.
+ * @throws InvalidEntryPriceSavedInFileError When the entry price parameter is of invalid format.
+ * @throws InvalidExitPriceSavedInFileError When the exit price parameter is of invalid format.
+ * @throws InvalidEmptyEntryPriceInFileError When the entry price parameter is empty in the file.
+ * @throws InvalidEmptyExitPriceInFileError When the exit price parameter is empty in the file.
+ * @throws InvalidEmptyExpiryInFileError When the expiry parameter is empty in the file.
+ * @throws InvalidRemarkInFileError When the remark parameter is of invalid format.
+ * @throws InvalidExpirySavedInFileError When the expiry parameter is of invalid format.
+ */
+ public static void addForexToList(String[] textSegment, InstrumentManager instrumentManager)
+ throws InvalidNameSavedInFileError, InvalidSentimentSavedInFileError, InvalidCurrPriceSavedInFileError,
+ InvalidEmptyNameInFileError, InvalidEmptySentimentInFileError, InvalidEmptyStatusInFileError,
+ InvalidStatusSavedInFileError, InvalidEmptyCurrPriceInFileError, InvalidEntryPriceSavedInFileError,
+ InvalidExitPriceSavedInFileError, InvalidExpirySavedInFileError, InvalidEmptyExitPriceInFileError,
+ InvalidEmptyExpiryInFileError, InvalidEmptyEntryPriceInFileError, InvalidRemarkInFileError {
+ validateAndDecodeGeneralAttributes(textSegment);
+ validateAndDecodeSpecificAttributes(textSegment);
+ Instrument forex = createDecodedInstrument();
+ setDoneStatus(decodedIsDone, forex);
+ instrumentManager.addInstrument(forex);
+ }
+
+ private static Instrument createDecodedInstrument() {
+ return new Forex(decodedName, decodedCurrPrice, decodedSentiment,
+ decodedEntryPrice, decodedExitPrice, decodedExpiry, decodedRemark);
+ }
+}
diff --git a/src/main/java/seedu/mtracker/filemanager/InstrumentDecoder.java b/src/main/java/seedu/mtracker/filemanager/InstrumentDecoder.java
new file mode 100644
index 0000000000..45ba7de66c
--- /dev/null
+++ b/src/main/java/seedu/mtracker/filemanager/InstrumentDecoder.java
@@ -0,0 +1,242 @@
+package seedu.mtracker.filemanager;
+
+import seedu.mtracker.LogHelper;
+import seedu.mtracker.commons.Validate;
+import seedu.mtracker.commons.error.fileerror.InvalidCurrPriceSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyCurrPriceInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyNameInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptySentimentInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyStatusInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidInstrumentInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidNameSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidSentimentSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidStatusSavedInFileError;
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.model.InstrumentManager;
+import seedu.mtracker.ui.TextUi;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+//@@author williamlamjy
+/**
+ * Decodes the pre-existing instruments saved in the mTracker file.
+ * Adds the decoded instruments into the current InstrumentManager.
+ */
+public class InstrumentDecoder {
+
+ public static final int SPLIT_FUNCTION_LIMIT_VALUE = -1;
+
+ public static final String TYPE_CRYPTO = "crypto";
+ public static final String TYPE_STOCK = "stock";
+ public static final String TYPE_ETF = "etf";
+ public static final String TYPE_FOREX = "forex";
+
+ public static final int INDEX_START = 1;
+
+ public static final int TYPE_INDEX = 0;
+ public static final int NAME_INDEX = 1;
+ public static final int CURR_PRICE_INDEX = 2;
+ public static final int SENTIMENT_INDEX = 3;
+ public static final int IS_DONE_INDEX = 4;
+
+ protected static String decodedSentiment;
+ protected static String decodedName;
+ protected static double decodedCurrPrice;
+ protected static boolean decodedIsDone;
+
+ protected static final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+
+ protected static final int ASCII_CODE = 127;
+ protected static final char FILE_SEPARATOR = (char) ASCII_CODE;
+
+ /**
+ * Gets the instrument's name from the file.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @return Name of the instrument.
+ * @throws InvalidEmptyNameInFileError When the name parameter is empty in the file.
+ */
+ public static String getNameFromFile(String[] textSegment) throws InvalidEmptyNameInFileError {
+ String name;
+ try {
+ name = textSegment[NAME_INDEX];
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidEmptyNameInFileError();
+ }
+ return name;
+ }
+
+ /**
+ * Gets the instrument's current price from the file.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @return Current price of the instrument.
+ * @throws InvalidEmptyCurrPriceInFileError When the current price parameter is empty in the file.
+ */
+ public static String getCurrPriceFromFile(String[] textSegment) throws InvalidEmptyCurrPriceInFileError {
+ String currPrice;
+ try {
+ currPrice = textSegment[CURR_PRICE_INDEX];
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidEmptyCurrPriceInFileError();
+ }
+ return currPrice;
+ }
+
+ /**
+ * Gets the instrument's sentiment from the file.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @return Sentiment of the instrument
+ * @throws InvalidEmptySentimentInFileError When the sentiment parameter is empty in the file.
+ */
+ public static String getSentimentFromFile(String[] textSegment) throws InvalidEmptySentimentInFileError {
+ String sentiment;
+ try {
+ sentiment = textSegment[SENTIMENT_INDEX];
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidEmptySentimentInFileError();
+ }
+ return sentiment;
+ }
+
+ /**
+ * Gets the instrument's done status from the file.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @return Done status of the instrument
+ * @throws InvalidEmptyStatusInFileError When the done status parameter is empty in the file.
+ */
+ public static String getDoneStatusFromFile(String[] textSegment) throws InvalidEmptyStatusInFileError {
+ String status;
+ try {
+ status = textSegment[IS_DONE_INDEX];
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidEmptyStatusInFileError();
+ }
+ return status;
+ }
+
+ private static void validateGeneralAttributes(String[] textSegment) throws InvalidSentimentSavedInFileError,
+ InvalidNameSavedInFileError, InvalidCurrPriceSavedInFileError, InvalidStatusSavedInFileError {
+ if (!Validate.isValidName(textSegment[NAME_INDEX], textSegment[TYPE_INDEX])) {
+ throw new InvalidNameSavedInFileError();
+ }
+ if (!Validate.isValidPrice(textSegment[CURR_PRICE_INDEX])) {
+ throw new InvalidCurrPriceSavedInFileError();
+ }
+ if (!Validate.isValidSentiment(textSegment[SENTIMENT_INDEX])) {
+ throw new InvalidSentimentSavedInFileError();
+ }
+ if (!Validate.isValidStatus(textSegment[IS_DONE_INDEX])) {
+ throw new InvalidStatusSavedInFileError();
+ }
+ }
+
+ /**
+ * Decodes the three general attributes of the instrument.
+ *
+ * @param name Name of instrument.
+ * @param currPrice Current price of instrument.
+ * @param sentiment Sentiment of instrument.
+ * @param status Done status of instrument.
+ */
+ public static void decodeGeneralAttributes(String name, String currPrice, String sentiment, String status) {
+ decodedName = name;
+ decodedCurrPrice = Double.parseDouble(currPrice);
+ decodedSentiment = sentiment;
+ decodedIsDone = Boolean.parseBoolean(status);
+ }
+
+ /**
+ * Validates the three general attributes of the instrument and decodes the attributes.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @throws InvalidEmptyNameInFileError When the name parameter is empty in the file.
+ * @throws InvalidEmptyCurrPriceInFileError When the current price parameter is empty in the file.
+ * @throws InvalidEmptySentimentInFileError When the sentiment parameter is empty in the file.
+ * @throws InvalidSentimentSavedInFileError When the sentiment parameter is of invalid format.
+ * @throws InvalidEmptyStatusInFileError When the done status parameter is empty in the file.
+ * @throws InvalidNameSavedInFileError When the name parameter is of invalid format.
+ * @throws InvalidCurrPriceSavedInFileError When the current price parameter is of invalid format.
+ * @throws InvalidStatusSavedInFileError When the done status parameter is of invalid format.
+ */
+ public static void validateAndDecodeGeneralAttributes(String[] textSegment) throws InvalidEmptyNameInFileError,
+ InvalidEmptyCurrPriceInFileError, InvalidEmptySentimentInFileError, InvalidSentimentSavedInFileError,
+ InvalidEmptyStatusInFileError, InvalidNameSavedInFileError, InvalidCurrPriceSavedInFileError,
+ InvalidStatusSavedInFileError {
+ String name = getNameFromFile(textSegment);
+ String currPrice = getCurrPriceFromFile(textSegment);
+ String sentiment = getSentimentFromFile(textSegment);
+ String status = getDoneStatusFromFile(textSegment);
+ validateGeneralAttributes(textSegment);
+ decodeGeneralAttributes(name, currPrice, sentiment, status);
+ }
+
+ /**
+ * Sets the done status of the instrument.
+ *
+ * @param isDone Whether the instrument is marked as done.
+ * @param doneInstrument The instrument that is setting its done status.
+ */
+ public static void setDoneStatus(boolean isDone, Instrument doneInstrument) {
+ if (!isDone) {
+ return;
+ }
+ doneInstrument.markAsDone();
+ }
+
+ /**
+ * Reads the encoded pre-existing instruments in the file.
+ * Adds the decoded instruments into the InstrumentManager.
+ *
+ * @param instrumentManager Current InstrumentManager.
+ * @param fileLines List of lines in the mTracker file.
+ */
+ public static void readFile(InstrumentManager instrumentManager, List fileLines) {
+ AtomicInteger idx = new AtomicInteger(INDEX_START);
+ fileLines.stream()
+ .forEach((line) -> {
+ String[] textSegment = line.split(String.valueOf(FILE_SEPARATOR), SPLIT_FUNCTION_LIMIT_VALUE);
+ try {
+ addSavedInstrumentToList(instrumentManager, textSegment);
+ } catch (Exception e) {
+ TextUi.showErrorMessage(e);
+ TextUi.ignoreCorruptedInstrument(idx);
+ logger.warning(LogHelper.LOG_DATA_FILE_INSTRUMENT_CORRUPTED_ERROR);
+ } finally {
+ idx.getAndIncrement();
+ }
+ });
+ }
+
+ /**
+ * Adds the corresponding instrument into the InstrumentManager.
+ *
+ * @param instrumentManager Current InstrumentManager.
+ * @param textSegment Array containing the parameters of an instrument.
+ * @throws Exception When there is an error adding the instrument to the InstrumentManager.
+ */
+ public static void addSavedInstrumentToList(InstrumentManager instrumentManager, String[] textSegment)
+ throws Exception {
+ switch (textSegment[TYPE_INDEX].toLowerCase()) {
+ case TYPE_CRYPTO:
+ CryptoDecoder.addCryptoToList(textSegment, instrumentManager);
+ break;
+ case TYPE_STOCK:
+ StockDecoder.addStockToList(textSegment, instrumentManager);
+ break;
+ case TYPE_FOREX:
+ ForexDecoder.addForexToList(textSegment, instrumentManager);
+ break;
+ case TYPE_ETF:
+ EtfDecoder.addEtfToList(textSegment, instrumentManager);
+ break;
+ default:
+ logger.warning(LogHelper.LOG_DATA_FILE_INSTRUMENT_TYPE_CORRUPTED_ERROR);
+ throw new InvalidInstrumentInFileError();
+ }
+ }
+}
diff --git a/src/main/java/seedu/mtracker/filemanager/InstrumentEncoder.java b/src/main/java/seedu/mtracker/filemanager/InstrumentEncoder.java
new file mode 100644
index 0000000000..dca3fa143f
--- /dev/null
+++ b/src/main/java/seedu/mtracker/filemanager/InstrumentEncoder.java
@@ -0,0 +1,55 @@
+package seedu.mtracker.filemanager;
+
+import seedu.mtracker.LogHelper;
+import seedu.mtracker.commons.error.fileerror.FileWriteError;
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.ui.TextUi;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+//@@author williamlamjy
+/**
+ * Encodes the pre-existing instruments into a savable format to store into the mTracker file.
+ */
+public class InstrumentEncoder {
+
+ protected static final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+
+ /**
+ * Writes to the file with the formatted instrument.
+ *
+ * @param line String of the formatted instrument to be written into a line in the file.
+ * @param writeToFile FileWriter that writes to the file.
+ * @throws FileWriteError When there is an error writing to the file.
+ */
+ public static void editFile(String line, FileWriter writeToFile) throws FileWriteError {
+ try {
+ writeToFile.write(line);
+ } catch (IOException e) {
+ logger.severe(LogHelper.LOG_DATA_FILE_WRITE_ERROR);
+ throw new FileWriteError();
+ }
+ }
+
+ /**
+ * Writes to the file the entire list of formatted instruments to be saved.
+ *
+ * @param instruments Instruments to be saved.
+ * @param writeToFile FileWriter that writes to the file.
+ * @throws IOException When there is an error writing to the file.
+ */
+ public static void writeFile(ArrayList instruments, FileWriter writeToFile) throws IOException {
+ instruments.stream()
+ .forEach(instrument -> {
+ try {
+ editFile(instrument.textFileFormatting() + System.lineSeparator(), writeToFile);
+ } catch (FileWriteError e) {
+ TextUi.showErrorMessage(e);
+ }
+ });
+ writeToFile.close();
+ }
+}
diff --git a/src/main/java/seedu/mtracker/filemanager/StockDecoder.java b/src/main/java/seedu/mtracker/filemanager/StockDecoder.java
new file mode 100644
index 0000000000..2a5fbe4b74
--- /dev/null
+++ b/src/main/java/seedu/mtracker/filemanager/StockDecoder.java
@@ -0,0 +1,87 @@
+package seedu.mtracker.filemanager;
+
+import seedu.mtracker.commons.error.fileerror.InvalidCurrPriceSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyCurrPriceInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyNameInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptySentimentInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyStatusInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidNameSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidRemarkInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidSentimentSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidStatusSavedInFileError;
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.model.InstrumentManager;
+import seedu.mtracker.model.subinstrument.Stock;
+
+//@@author williamlamjy
+/**
+ * Decodes and adds stock instruments into the InstrumentManager.
+ */
+public class StockDecoder extends InstrumentDecoder {
+
+ public static final int STOCK_REMARK_INDEX = 5;
+ protected static String decodedRemark;
+
+ /**
+ * Gets remark from the mTracker file.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @return Remark of the instrument.
+ * @throws InvalidRemarkInFileError When the remark parameter is of invalid format.
+ */
+ public static String getRemarkFromFile(String[] textSegment) throws InvalidRemarkInFileError {
+ String remark;
+ try {
+ remark = textSegment[STOCK_REMARK_INDEX];
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidRemarkInFileError();
+ }
+ return remark;
+ }
+
+ private static void decodeSpecificAttributes(String remark) {
+ decodedRemark = remark;
+ }
+
+ /**
+ * Validates and decodes the specific attributes of the stock.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @throws InvalidRemarkInFileError When the remark parameter is of invalid format.
+ */
+ public static void validateAndDecodeSpecificAttributes(String[] textSegment) throws InvalidRemarkInFileError {
+ String remark = getRemarkFromFile(textSegment);
+ decodeSpecificAttributes(remark);
+ }
+
+ /**
+ * Adds the validated and decoded stock to the InstrumentManager.
+ *
+ * @param textSegment Array containing the parameters of an instrument.
+ * @param instrumentManager Current InstrumentManager.
+ * @throws InvalidNameSavedInFileError When the name parameter is of invalid format.
+ * @throws InvalidSentimentSavedInFileError When the sentiment parameter is of invalid format.
+ * @throws InvalidCurrPriceSavedInFileError When the current price parameter is of invalid format.
+ * @throws InvalidEmptyNameInFileError When the name parameter is empty in the file.
+ * @throws InvalidEmptyCurrPriceInFileError When the current price parameter is empty in the file.
+ * @throws InvalidEmptySentimentInFileError When the sentiment parameter is empty in the file.
+ * @throws InvalidEmptyStatusInFileError When the done status parameter is empty in the file.
+ * @throws InvalidStatusSavedInFileError When the done status parameter is of invalid format.
+ * @throws InvalidRemarkInFileError When the remark parameter is of invalid format.
+ */
+ public static void addStockToList(String[] textSegment, InstrumentManager instrumentManager)
+ throws InvalidNameSavedInFileError, InvalidSentimentSavedInFileError, InvalidCurrPriceSavedInFileError,
+ InvalidEmptyNameInFileError, InvalidEmptySentimentInFileError, InvalidEmptyStatusInFileError,
+ InvalidStatusSavedInFileError, InvalidEmptyCurrPriceInFileError, InvalidRemarkInFileError {
+ validateAndDecodeGeneralAttributes(textSegment);
+ validateAndDecodeSpecificAttributes(textSegment);
+ Instrument stock = createDecodedInstrument();
+ setDoneStatus(decodedIsDone, stock);
+ instrumentManager.addInstrument(stock);
+ }
+
+ private static Instrument createDecodedInstrument() {
+ return new Stock(decodedName, decodedCurrPrice, decodedSentiment, decodedRemark);
+ }
+
+}
diff --git a/src/main/java/seedu/mtracker/filemanager/Storage.java b/src/main/java/seedu/mtracker/filemanager/Storage.java
new file mode 100644
index 0000000000..aff1c3ec73
--- /dev/null
+++ b/src/main/java/seedu/mtracker/filemanager/Storage.java
@@ -0,0 +1,89 @@
+package seedu.mtracker.filemanager;
+
+import seedu.mtracker.LogHelper;
+import seedu.mtracker.commons.error.fileerror.FileLoadError;
+import seedu.mtracker.commons.error.fileerror.FileWriteError;
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.model.InstrumentManager;
+import seedu.mtracker.ui.TextUi;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+//@@author williamlamjy
+/**
+ * Stores data of pre-existing instruments and loads pre-existing data when the program runs.
+ */
+public class Storage {
+ public static final String FILE_PATH = "data/mTracker.txt";
+ protected static final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ private final File file;
+ private final Path path;
+
+ /**
+ * Initializes the Storage class with a file object.
+ */
+ public Storage() {
+ file = new File(FILE_PATH);
+ path = Paths.get(FILE_PATH);
+ }
+
+ /**
+ * Loads pre-existing data into the instrument manager.
+ * If the file does not exist, a new file and directory will be created.
+ *
+ * @param instrumentManager The instrument manager to be updated.
+ * @throws FileLoadError When there is an error loading the file.
+ */
+ public void loadFileData(InstrumentManager instrumentManager) throws FileLoadError {
+ try {
+ if (!Files.exists(path) || !Files.isRegularFile(path)) {
+ TextUi.displayCreateFile();
+ file.getParentFile().mkdir();
+ file.createNewFile();
+ return;
+ }
+ TextUi.displayLoadingFile();
+ InstrumentDecoder.readFile(instrumentManager, Files.readAllLines(path));
+ } catch (IOException e) {
+ logger.severe(LogHelper.LOG_DATA_FILE_LOAD_ERROR);
+ throw new FileLoadError();
+ }
+ }
+
+ /**
+ * Creates a file writer to write to the mTracker file.
+ * Writes to the mTracker file with the saved instruments.
+ *
+ * @param instruments Instruments to be saved from the current session.
+ * @throws FileWriteError When there is an error writing to the file.
+ */
+ public void writeFileData(ArrayList instruments) throws FileWriteError {
+ try {
+ FileWriter writeToFile = new FileWriter(file);
+ InstrumentEncoder.writeFile(instruments, writeToFile);
+ } catch (IOException e) {
+ logger.severe(LogHelper.LOG_DATA_FILE_WRITE_ERROR);
+ throw new FileWriteError();
+ }
+ }
+
+ /**
+ * Updates the mTracker file with added instruments.
+ *
+ * @param instruments Instruments to be saved from the current session.
+ */
+ public void updateFileData(ArrayList instruments) {
+ try {
+ writeFileData(instruments);
+ } catch (FileWriteError e) {
+ TextUi.showErrorMessage(e);
+ }
+ }
+}
diff --git a/src/main/java/seedu/mtracker/model/Instrument.java b/src/main/java/seedu/mtracker/model/Instrument.java
new file mode 100644
index 0000000000..2b2a970b1e
--- /dev/null
+++ b/src/main/java/seedu/mtracker/model/Instrument.java
@@ -0,0 +1,238 @@
+package seedu.mtracker.model;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * Represents an instrument in the InstrumentManager.
+ * Contains common instrument parameters.
+ */
+public abstract class Instrument {
+
+ public static final String DATE_REGEX = "MMM dd yyyy";
+ public static final String SEMICOLON_SEP = "; ";
+ public static final String SPACE = " ";
+
+ protected String name;
+ protected double currentPrice;
+ protected String sentiment;
+ protected boolean isDone;
+
+ protected static final String TAB = "\t";
+ protected static final int ASCII_CODE = 127;
+ protected static final char FILE_SEPARATOR = (char) ASCII_CODE;
+ protected static final String DONE_SYMBOL = "[X]";
+ protected static final String NOT_DONE_SYMBOL = "[ ]";
+ protected static HashSet validAttribute;
+
+ protected static final String NAME_ATTRIBUTE = "name";
+ protected static final String CURRENT_PRICE_ATTRIBUTE = "current-price";
+ protected static final String SENTIMENT_ATTRIBUTE = "sentiment";
+ protected static final String REMARK_ATTRIBUTE = "remarks";
+ protected static final String DONE_ATTRIBUTE = "done-status";
+ protected static final String SEPARATOR = ", ";
+ protected static final String DONE_INDICATOR = "done";
+ protected static final String UNDONE_INDICATOR = "undone";
+ protected static final String REMARK_FIELD = "Remarks: ";
+
+ private static final String TYPE_FIELD = "Type: ";
+ private static final String NAME_FIELD = "Name: ";
+ private static final String CURRENT_PRICE_FIELD = "Current Price: ";
+ private static final String SENTIMENT_FIELD = "Sentiment: ";
+
+
+ public Instrument(String name, double currentPrice, String sentiment) {
+ this.name = name;
+ this.currentPrice = currentPrice;
+ this.sentiment = sentiment;
+ this.isDone = false;
+ validAttribute = new HashSet<>();
+ }
+
+ //@@author williamlamjy
+ public boolean getIsDone() {
+ return isDone;
+ }
+
+ /**
+ * Sets instrument as done.
+ */
+ public void markAsDone() {
+ isDone = true;
+ }
+
+ /**
+ * Sets instrument as not done.
+ */
+ public void markAsNotDone() {
+ isDone = false;
+ }
+
+ /**
+ * Gets the icon representing the done status of the instrument.
+ *
+ * @return DONE_SYMBOL if instrument is marked as done and NOT_DONE_SYMBOL if instrument is not marked as done.
+ */
+ public String getStatusIcon() {
+ return (isDone ? DONE_SYMBOL : NOT_DONE_SYMBOL);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ //@@author kum-wh
+ public void setName(String inputName) {
+ name = inputName;
+ }
+
+ public void setCurrentPrice(double inputCurrentPrice) {
+ currentPrice = inputCurrentPrice;
+ }
+
+ public void setSentiment(String inputSentiment) {
+ sentiment = inputSentiment;
+ }
+
+ /**
+ * Sets name parameter to the new name if name parameter is being edited.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editName(HashMap editedParameters) {
+ if (!editedParameters.containsKey(NAME_ATTRIBUTE)) {
+ return;
+ }
+ setName(editedParameters.get(NAME_ATTRIBUTE));
+ }
+
+ /**
+ * Sets done status parameter to the new status if done status parameter is being edited.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editDoneStatus(HashMap editedParameters) {
+ if (!editedParameters.containsKey(DONE_ATTRIBUTE)) {
+ return;
+ }
+ if (editedParameters.get(DONE_ATTRIBUTE).equals(DONE_INDICATOR)) {
+ markAsDone();
+ return;
+ }
+ markAsNotDone();
+ assert (editedParameters.get(DONE_ATTRIBUTE).equals(UNDONE_INDICATOR));
+ }
+
+ /**
+ * Sets current price parameter to the new price if current price parameter is being edited.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editCurrentPrice(HashMap editedParameters) {
+ if (!editedParameters.containsKey(CURRENT_PRICE_ATTRIBUTE)) {
+ return;
+ }
+ double updatedPrice = Double.parseDouble(editedParameters.get(CURRENT_PRICE_ATTRIBUTE));
+ setCurrentPrice(updatedPrice);
+ }
+
+ /**
+ * Sets sentiment parameter to the new sentiment if sentiment parameter is being edited.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editSentiment(HashMap editedParameters) {
+ if (!editedParameters.containsKey(SENTIMENT_ATTRIBUTE)) {
+ return;
+ }
+ setSentiment(editedParameters.get(SENTIMENT_ATTRIBUTE));
+ }
+
+ /**
+ * Sets name, current price, sentiment and done status to its respective new values, if parameters is being edited.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editGeneralParameter(HashMap editedParameters) {
+ editName(editedParameters);
+ editCurrentPrice(editedParameters);
+ editSentiment(editedParameters);
+ editDoneStatus(editedParameters);
+ }
+
+ /**
+ * Sets all the parameters being edited to its new values.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editParameter(HashMap editedParameters) {
+ editGeneralParameter(editedParameters);
+ }
+
+ public abstract String getType();
+
+ //@@author williamlamjy
+ /**
+ * Formats all the parameters of the instrument to save to text file.
+ *
+ * @return A formatted string to save to text file.
+ */
+ public String textFileFormatting() {
+ return getType().toLowerCase() + FILE_SEPARATOR + name + FILE_SEPARATOR
+ + currentPrice + FILE_SEPARATOR + sentiment + FILE_SEPARATOR
+ + isDone;
+ }
+
+ //@@author kum-wh
+ /**
+ * Gets all the type of parameters in the instrument in one string.
+ *
+ * @return A string containing all the type of parameters of the instrument.
+ */
+ public String editParameterInstructions() {
+ return DONE_ATTRIBUTE + SEPARATOR
+ + NAME_ATTRIBUTE + SEPARATOR
+ + CURRENT_PRICE_ATTRIBUTE + SEPARATOR
+ + SENTIMENT_ATTRIBUTE;
+ }
+
+ //@@author KVignesh122
+ public abstract String getTypeIcon();
+
+ /**
+ * Gets all the parameters of the instrument, with each parameter on a newline.
+ *
+ * @return A string containing all parameters of the instrument.
+ */
+ public String getAllParams() {
+ return TYPE_FIELD + getType() + TAB + getStatusIcon() + System.lineSeparator()
+ + NAME_FIELD + name + System.lineSeparator()
+ + CURRENT_PRICE_FIELD + currentPrice + System.lineSeparator()
+ + SENTIMENT_FIELD + sentiment + System.lineSeparator();
+ }
+
+ /**
+ * Gets the done status, name, current price and sentiment parameters of the instrument.
+ *
+ * @return A string containing the general parameters of the instrument.
+ */
+ public String getGeneralParams() {
+ return getTypeIcon() + getStatusIcon()
+ + SPACE + name + SEMICOLON_SEP + currentPrice + SEMICOLON_SEP + sentiment;
+ }
+
+ //@@author kum-wh
+ /**
+ * Adds all the type of parameters in an instrument into a HashSet.
+ *
+ * @return HashSet containing the type of parameters of the instrument.
+ */
+ public HashSet getValidAttribute() {
+ validAttribute.add(NAME_ATTRIBUTE);
+ validAttribute.add(CURRENT_PRICE_ATTRIBUTE);
+ validAttribute.add(SENTIMENT_ATTRIBUTE);
+ validAttribute.add(REMARK_ATTRIBUTE);
+ validAttribute.add(DONE_ATTRIBUTE);
+ return validAttribute;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/model/InstrumentManager.java b/src/main/java/seedu/mtracker/model/InstrumentManager.java
new file mode 100644
index 0000000000..668d3943e9
--- /dev/null
+++ b/src/main/java/seedu/mtracker/model/InstrumentManager.java
@@ -0,0 +1,76 @@
+package seedu.mtracker.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.stream.Collectors;
+
+//@@author williamlamjy
+/**
+ * Responsible for storing and managing of all the different types of instruments.
+ */
+public class InstrumentManager {
+
+ private final ArrayList instruments;
+ private static InstrumentManager instrumentManager;
+
+ private InstrumentManager() {
+ instruments = new ArrayList<>();
+ }
+
+ /**
+ * Allows other class to get the only instance of this class instead of creating a new instance.
+ *
+ * @return The only instance of this class.
+ */
+ public static InstrumentManager getInstance() {
+ if (instrumentManager == null) {
+ instrumentManager = new InstrumentManager();
+ }
+ return instrumentManager;
+ }
+
+ public int getSize() {
+ return instruments.size();
+ }
+
+ public ArrayList getInstruments() {
+ return instruments;
+ }
+
+ public Instrument getInstrument(int index) {
+ return instruments.get(index);
+ }
+
+ public void addInstrument(Instrument addedInstrument) {
+ instruments.add(addedInstrument);
+ }
+
+ //@@author theodorekwok
+ /**
+ * Scans through the list and filter instruments with name contains a keyword entered by user.
+ *
+ * @param keyword The word input by user to find.
+ * @return A list containing instruments containing keyword in its name.
+ */
+ public ArrayList findInstruments(String keyword) {
+ return (ArrayList) instruments.stream()
+ .filter((instrument) -> instrument.getName().contains(keyword))
+ .collect(Collectors.toList());
+ }
+
+ public void deleteInstrument(int index) {
+ instruments.remove(index);
+ }
+
+ //@@author kum-wh
+ /**
+ * Sets the parameters of the instruments to the new values.
+ *
+ * @param index The index of the instrument to edit.
+ * @param editedParameters The parameters to be edited and its new values.
+ */
+ public void editInstrument(int index, HashMap editedParameters) {
+ Instrument instrument = instruments.get(index);
+ instrument.editParameter(editedParameters);
+ }
+}
diff --git a/src/main/java/seedu/mtracker/model/subinstrument/Crypto.java b/src/main/java/seedu/mtracker/model/subinstrument/Crypto.java
new file mode 100644
index 0000000000..fb91f122f7
--- /dev/null
+++ b/src/main/java/seedu/mtracker/model/subinstrument/Crypto.java
@@ -0,0 +1,153 @@
+package seedu.mtracker.model.subinstrument;
+
+import seedu.mtracker.model.Instrument;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.HashSet;
+
+//@@author williamlamjy
+/**
+ * Represents a Crypto type instrument.
+ */
+public class Crypto extends Instrument {
+
+ protected LocalDate expiry;
+ protected String remark;
+ protected static final String CRYPTO_ICON = "[C]";
+ protected static final String TYPE_INSTRUMENT = "Crypto";
+ protected static final String EXPIRY_FIELD = "Expiry: ";
+ protected static final String EXPIRY_ATTRIBUTE = "expiry";
+
+ public Crypto(String name, double currentPrice, String sentiment, LocalDate expiry, String remark) {
+ super(name, currentPrice, sentiment);
+ this.expiry = expiry;
+ this.remark = remark;
+ }
+
+ /**
+ * Formats date into String.
+ *
+ * @return Expiry date formatted into string.
+ */
+ public String formatExpiry() {
+ return expiry.format(DateTimeFormatter.ofPattern(DATE_REGEX));
+ }
+
+ //@@author kum-wh
+ public void setExpiry(LocalDate inputExpiry) {
+ expiry = inputExpiry;
+ }
+
+ public void setRemark(String inputRemark) {
+ remark = inputRemark;
+ }
+
+ /**
+ * Sets remark parameter to the new remark if remark parameter is being edited.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editRemark(HashMap editedParameters) {
+ if (!editedParameters.containsKey(REMARK_ATTRIBUTE)) {
+ return;
+ }
+ setRemark(editedParameters.get(REMARK_ATTRIBUTE));
+ }
+
+ /**
+ * Sets expiry parameter to the new expiry if expiry parameter is being edited.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editExpiry(HashMap editedParameters) {
+ if (!editedParameters.containsKey(EXPIRY_ATTRIBUTE)) {
+ return;
+ }
+ LocalDate updateExpiry = LocalDate.parse(editedParameters.get(EXPIRY_ATTRIBUTE));
+ setExpiry(updateExpiry);
+ }
+
+ /**
+ * Sets all instrument specific parameters being edited to its new values.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editSpecificParameter(HashMap editedParameters) {
+ editExpiry(editedParameters);
+ editRemark(editedParameters);
+ }
+
+ /**
+ * Sets all the Crypto parameters being edited to its new values.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ @Override
+ public void editParameter(HashMap editedParameters) {
+ editGeneralParameter(editedParameters);
+ editSpecificParameter(editedParameters);
+ }
+
+ //@@author
+ @Override
+ public String getType() {
+ return TYPE_INSTRUMENT;
+ }
+
+ //@@author williamlamjy
+ /**
+ * Formats all Crypto parameters to save to text file.
+ *
+ * @return A formatted string to save to text file.
+ */
+ @Override
+ public String textFileFormatting() {
+ return super.textFileFormatting() + FILE_SEPARATOR + expiry
+ + FILE_SEPARATOR + remark;
+ }
+
+ //@@author kum-wh
+ /**
+ * Gets all the type of Crypto parameters in one string.
+ *
+ * @return A string containing all the type of Crypto parameters.
+ */
+ @Override
+ public String editParameterInstructions() {
+ return super.editParameterInstructions() + SEPARATOR + EXPIRY_ATTRIBUTE + SEPARATOR
+ + REMARK_ATTRIBUTE;
+ }
+
+ //@@author
+ @Override
+ public String getTypeIcon() {
+ return CRYPTO_ICON;
+ }
+
+ /**
+ * Gets all the Crypto parameters, with each parameter on a newline.
+ *
+ * @return A string containing all the Crypto parameters.
+ */
+ @Override
+ public String getAllParams() {
+ return super.getAllParams()
+ + EXPIRY_FIELD + formatExpiry() + System.lineSeparator()
+ + REMARK_FIELD + remark;
+ }
+
+ //@@author kum-wh
+ /**
+ * Adds all the type of Crypto parameters into a HashSet.
+ *
+ * @return HashSet containing the type of Crypto parameters.
+ */
+ @Override
+ public HashSet getValidAttribute() {
+ super.getValidAttribute();
+ validAttribute.add(EXPIRY_ATTRIBUTE);
+ return validAttribute;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/model/subinstrument/Etf.java b/src/main/java/seedu/mtracker/model/subinstrument/Etf.java
new file mode 100644
index 0000000000..43fd28c2d9
--- /dev/null
+++ b/src/main/java/seedu/mtracker/model/subinstrument/Etf.java
@@ -0,0 +1,153 @@
+package seedu.mtracker.model.subinstrument;
+
+import seedu.mtracker.model.Instrument;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+//@@author kum-wh
+/**
+ * Represents an Etf type instrument.
+ */
+public class Etf extends Instrument {
+
+ protected String remark;
+ protected double pastReturn;
+ protected static final String ETF_ICON = "[E]";
+ protected static final String TYPE_INSTRUMENT = "Etf";
+ protected static final String EMPTY_STRING = "";
+
+ protected static final String PAST_RETURN_FIELD = "Past Returns: ";
+ protected static final String PAST_RETURN_ATTRIBUTE = "past-returns";
+ protected static final double UNDEFINED_PAST_RETURN_VALUE = -101.0;
+
+ public Etf(String name, double currentPrice, String sentiment, double pastReturn, String remark) {
+ super(name, currentPrice, sentiment);
+ this.pastReturn = pastReturn;
+ this.remark = remark;
+ }
+
+ public void setRemark(String inputRemark) {
+ remark = inputRemark;
+ }
+
+ public void setPastReturn(Double inputPastReturn) {
+ pastReturn = inputPastReturn;
+ }
+
+ /**
+ * Sets past return parameter to the new past return if past return parameter is being edited.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editPastReturn(HashMap editedParameters) {
+ if (!editedParameters.containsKey(PAST_RETURN_ATTRIBUTE)) {
+ return;
+ }
+ Double updateReturn = Double.parseDouble(editedParameters.get(PAST_RETURN_ATTRIBUTE));
+ setPastReturn(updateReturn);
+ }
+
+ /**
+ * Sets remark parameter to the new remark if remark parameter is being edited.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editRemark(HashMap editedParameters) {
+ if (!editedParameters.containsKey(REMARK_ATTRIBUTE)) {
+ return;
+ }
+ setRemark(editedParameters.get(REMARK_ATTRIBUTE));
+ }
+
+ /**
+ * Sets all instrument specific parameters being edited to its new values.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editSpecificParameters(HashMap editedParameters) {
+ editPastReturn(editedParameters);
+ editRemark(editedParameters);
+ }
+
+ /**
+ * Sets all the Etf parameters being edited to its new values.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ @Override
+ public void editParameter(HashMap editedParameters) {
+ editGeneralParameter(editedParameters);
+ editSpecificParameters(editedParameters);
+ }
+
+ /**
+ * Gets the value past return in string.
+ *
+ * @return Empty string if past return is undefined else the value of the past return in string.
+ */
+ public String getPastReturn() {
+ if (pastReturn == UNDEFINED_PAST_RETURN_VALUE) {
+ return EMPTY_STRING;
+ }
+ return String.valueOf(pastReturn);
+ }
+
+ /**
+ * Gets all the type of Etf parameters in one string.
+ *
+ * @return A string containing all the type of Etf parameters.
+ */
+ @Override
+ public String editParameterInstructions() {
+ return super.editParameterInstructions() + SEPARATOR + PAST_RETURN_ATTRIBUTE + SEPARATOR
+ + REMARK_ATTRIBUTE;
+ }
+
+ @Override
+ public String getType() {
+ return TYPE_INSTRUMENT;
+ }
+
+ //@@author williamlamjy
+ /**
+ * Formats all Etf parameters to save to text file.
+ *
+ * @return A formatted string to save to text file.
+ */
+ @Override
+ public String textFileFormatting() {
+ return super.textFileFormatting() + FILE_SEPARATOR + getPastReturn()
+ + FILE_SEPARATOR + remark;
+ }
+ //@@author
+
+ @Override
+ public String getTypeIcon() {
+ return ETF_ICON;
+ }
+
+ /**
+ * Gets all the Etf parameters, with each parameter on a newline.
+ *
+ * @return A string containing all the Etf parameters.
+ */
+ @Override
+ public String getAllParams() {
+ return super.getAllParams()
+ + PAST_RETURN_FIELD + getPastReturn() + System.lineSeparator()
+ + REMARK_FIELD + remark;
+ }
+
+ /**
+ * Adds all the type of Etf parameters into a HashSet.
+ *
+ * @return HashSet containing the type of Etf parameters.
+ */
+ @Override
+ public HashSet getValidAttribute() {
+ super.getValidAttribute();
+ validAttribute.add(PAST_RETURN_ATTRIBUTE);
+ return validAttribute;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/model/subinstrument/Forex.java b/src/main/java/seedu/mtracker/model/subinstrument/Forex.java
new file mode 100644
index 0000000000..46e7e372e6
--- /dev/null
+++ b/src/main/java/seedu/mtracker/model/subinstrument/Forex.java
@@ -0,0 +1,216 @@
+package seedu.mtracker.model.subinstrument;
+
+import seedu.mtracker.model.Instrument;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.HashSet;
+
+//@@author KVignesh122
+/**
+ * Represents a Forex type instrument.
+ */
+public class Forex extends Instrument {
+
+ protected double entryPrice;
+ protected double exitPrice;
+ protected LocalDate expiry;
+ protected String remark;
+
+ protected static final String FOREX_ICON = "[F]";
+ protected static final String TYPE_INSTRUMENT = "Forex";
+
+ protected static final String ENTRY_PRICE_FIELD = "Entry Price: ";
+ protected static final String EXIT_PRICE_FIELD = "Exit Price: ";
+ protected static final String EXPIRY_FIELD = "Expiry: ";
+
+ protected static final String ENTRY_PRICE_ATTRIBUTE = "entry-price";
+ protected static final String EXIT_PRICE_ATTRIBUTE = "exit-price";
+ protected static final String EXPIRY_ATTRIBUTE = "expiry";
+
+ public Forex(
+ String name,
+ double currentPrice,
+ String sentiment,
+ double entryPrice,
+ double exitPrice,
+ LocalDate expiry,
+ String remark
+ ) {
+ super(name, currentPrice, sentiment);
+ this.entryPrice = entryPrice;
+ this.exitPrice = exitPrice;
+ this.expiry = expiry;
+ this.remark = remark;
+ }
+
+ //@@author kum-wh
+ /**
+ * Gets all the type of Forex parameters in one string.
+ *
+ * @return A string containing all the type of Forex parameters.
+ */
+ @Override
+ public String editParameterInstructions() {
+ return super.editParameterInstructions() + SEPARATOR + ENTRY_PRICE_ATTRIBUTE + SEPARATOR
+ + EXIT_PRICE_ATTRIBUTE + SEPARATOR
+ + EXPIRY_ATTRIBUTE + SEPARATOR
+ + REMARK_ATTRIBUTE;
+ }
+
+ //@@author theodorekwok
+ /**
+ * Formats date into String.
+ *
+ * @return Expiry date formatted into string.
+ */
+ public String formatExpiry() {
+ return expiry.format(DateTimeFormatter.ofPattern(DATE_REGEX));
+ }
+
+ //@@author kum-wh
+ public void setRemark(String inputRemark) {
+ remark = inputRemark;
+ }
+
+ public void setEntryPrice(Double inputEntryPrice) {
+ entryPrice = inputEntryPrice;
+ }
+
+ public void setExitPrice(Double inputExitPrice) {
+ exitPrice = inputExitPrice;
+ }
+
+ public void setExpiry(LocalDate inputExpiry) {
+ expiry = inputExpiry;
+ }
+
+ /**
+ * Sets remark parameter to the new remark if remark parameter is being edited.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editRemark(HashMap editedParameters) {
+ if (!editedParameters.containsKey(REMARK_ATTRIBUTE)) {
+ return;
+ }
+ setRemark(editedParameters.get(REMARK_ATTRIBUTE));
+ }
+
+ /**
+ * Sets expiry parameter to the new expiry if expiry parameter is being edited.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editExpiry(HashMap editedParameters) {
+ if (!editedParameters.containsKey(EXPIRY_ATTRIBUTE)) {
+ return;
+ }
+ LocalDate updateExpiry = LocalDate.parse(editedParameters.get(EXPIRY_ATTRIBUTE));
+ setExpiry(updateExpiry);
+ }
+
+ /**
+ * Sets entry price parameter to the new price if entry price parameter is being edited.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editEntryPrice(HashMap editedParameters) {
+ if (!editedParameters.containsKey(ENTRY_PRICE_ATTRIBUTE)) {
+ return;
+ }
+ Double updateEntryPrice = Double.parseDouble(editedParameters.get(ENTRY_PRICE_ATTRIBUTE));
+ setEntryPrice(updateEntryPrice);
+ }
+
+ /**
+ * Sets exit price parameter to the new price if exit price parameter is being edited.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editExitPrice(HashMap editedParameters) {
+ if (!editedParameters.containsKey(EXIT_PRICE_FIELD)) {
+ return;
+ }
+ Double updateExitPrice = Double.parseDouble(editedParameters.get(EXIT_PRICE_FIELD));
+ setExitPrice(updateExitPrice);
+ }
+
+ /**
+ * Sets all instrument specific parameters being edited to its new values.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editSpecificParameter(HashMap editedParameters) {
+ editEntryPrice(editedParameters);
+ editExitPrice(editedParameters);
+ editExpiry(editedParameters);
+ editRemark(editedParameters);
+ }
+
+ /**
+ * Sets all the Forex parameters being edited to its new values.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ @Override
+ public void editParameter(HashMap editedParameters) {
+ editGeneralParameter(editedParameters);
+ editSpecificParameter(editedParameters);
+ }
+
+ //@@author
+ @Override
+ public String getType() {
+ return TYPE_INSTRUMENT;
+ }
+
+ @Override
+ public String getTypeIcon() {
+ return FOREX_ICON;
+ }
+
+ //@@author williamlamjy
+ /**
+ * Formats all Forex parameters to save to text file.
+ *
+ * @return A formatted string to save to text file.
+ */
+ @Override
+ public String textFileFormatting() {
+ return super.textFileFormatting() + FILE_SEPARATOR + entryPrice
+ + FILE_SEPARATOR + exitPrice + FILE_SEPARATOR + expiry
+ + FILE_SEPARATOR + remark;
+ }
+ //@@author
+
+ /**
+ * Gets all the Forex parameters, with each parameter on a newline.
+ *
+ * @return A string containing all the Forex parameters.
+ */
+ @Override
+ public String getAllParams() {
+ return super.getAllParams()
+ + ENTRY_PRICE_FIELD + entryPrice + System.lineSeparator()
+ + EXIT_PRICE_FIELD + exitPrice + System.lineSeparator()
+ + EXPIRY_FIELD + formatExpiry() + System.lineSeparator()
+ + REMARK_FIELD + remark;
+ }
+
+ //@@author kum-wh
+ /**
+ * Adds all the type of Forex parameters into a HashSet.
+ *
+ * @return HashSet containing the type of Forex parameters.
+ */
+ @Override
+ public HashSet getValidAttribute() {
+ super.getValidAttribute();
+ validAttribute.add(ENTRY_PRICE_ATTRIBUTE);
+ validAttribute.add(EXIT_PRICE_ATTRIBUTE);
+ validAttribute.add(EXPIRY_ATTRIBUTE);
+ return validAttribute;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/model/subinstrument/Stock.java b/src/main/java/seedu/mtracker/model/subinstrument/Stock.java
new file mode 100644
index 0000000000..72ee038911
--- /dev/null
+++ b/src/main/java/seedu/mtracker/model/subinstrument/Stock.java
@@ -0,0 +1,98 @@
+package seedu.mtracker.model.subinstrument;
+
+import seedu.mtracker.model.Instrument;
+
+import java.util.HashMap;
+
+//@@author theodorekwok
+/**
+ * Represents a Stock type instrument.
+ */
+public class Stock extends Instrument {
+
+ protected String remark;
+ protected static final String STOCK_ICON = "[S]";
+ protected static final String TYPE_INSTRUMENT = "Stock";
+
+ public Stock(String name, double currentPrice, String sentiment, String remark) {
+ super(name, currentPrice, sentiment);
+ this.remark = remark;
+ }
+
+ public String getRemark() {
+ return remark;
+ }
+
+ //@@author kum-wh
+ public void setRemark(String inputRemark) {
+ remark = inputRemark;
+ }
+
+ /**
+ * Sets remark parameter to the new remark if remark parameter is being edited.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ public void editRemark(HashMap editedParameters) {
+ if (!editedParameters.containsKey(REMARK_ATTRIBUTE)) {
+ return;
+ }
+ setRemark(editedParameters.get(REMARK_ATTRIBUTE));
+ }
+
+ /**
+ * Sets all the Stock parameters being edited to its new values.
+ *
+ * @param editedParameters HashMap containing parameters to edit and the new values.
+ */
+ @Override
+ public void editParameter(HashMap editedParameters) {
+ editGeneralParameter(editedParameters);
+ editRemark(editedParameters);
+ }
+
+ /**
+ * Gets all the type of Stock parameters in one string.
+ *
+ * @return A string containing all the type of Stock parameters.
+ */
+ @Override
+ public String editParameterInstructions() {
+ return super.editParameterInstructions() + SEPARATOR + REMARK_ATTRIBUTE;
+ }
+
+ //@@author
+ @Override
+ public String getType() {
+ return TYPE_INSTRUMENT;
+ }
+
+ //@@author williamlamjy
+ /**
+ * Formats all Stock parameters to save to text file.
+ *
+ * @return A formatted string to save to text file.
+ */
+ @Override
+ public String textFileFormatting() {
+ return super.textFileFormatting()
+ + FILE_SEPARATOR + getRemark();
+ }
+ //@@author
+
+ @Override
+ public String getTypeIcon() {
+ return STOCK_ICON;
+ }
+
+ /**
+ * Adds all the type of Stock parameters into a HashSet.
+ *
+ * @return HashSet containing the type of Stock parameters.
+ */
+ @Override
+ public String getAllParams() {
+ return super.getAllParams()
+ + REMARK_FIELD + remark;
+ }
+}
diff --git a/src/main/java/seedu/mtracker/ui/TextUi.java b/src/main/java/seedu/mtracker/ui/TextUi.java
new file mode 100644
index 0000000000..719d45d247
--- /dev/null
+++ b/src/main/java/seedu/mtracker/ui/TextUi.java
@@ -0,0 +1,415 @@
+package seedu.mtracker.ui;
+
+import seedu.mtracker.model.Instrument;
+
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
+
+//@@author KVignesh122
+/**
+ * Responsible for all interactions with the user.
+ */
+public class TextUi {
+
+ private static final String INDEX_BRACKET = ") ";
+ private static final String TYPE_HEADER = "Please key in the type of instrument: ";
+ private static final String REMARK_HEADER = "Remarks (optional): ";
+ private static final String SENTIMENT_HEADER = "Sentiment for instrument: ";
+ private static final String CURRENT_PRICE_HEADER = "Current Price: ";
+ private static final String ENTRY_PRICE_HEADER = "Entry Price: ";
+ private static final String EXIT_PRICE_HEADER = "Exit Price: ";
+ private static final String EXPIRY_HEADER = "Expiry (YYYY-MM-DD): ";
+ private static final String PAST_RETURN_HEADER = "Past Returns (optional): ";
+
+ private static final String LINE_DECORATOR = "_".repeat(80);
+ private static final String LOGO = " _________ __\n"
+ + " | _ _ | [ | _\n"
+ + " _ .--..--.|_/ | | \\_| .--. ,--. .---. | | / ] .---. _ .--.\n"
+ + "[ `.-. .-. | | | [ `/'`\\]`'_\\ : / /'`\\] | '' < / /__\\\\[ `/'`\\]\n"
+ + " | | | | | | _| |_ | | /| | |,| \\__. | |`\\ \\| \\__., | |\n"
+ + "[___||__||__]|_____|[___] \\'-;__/'.___.'[__| \\_]'.__.'[___]\n";
+ private static final String BYE_LOGO = " ______ _______ _\n"
+ + "( ___ \\ |\\ /|( ____ \\( )\n"
+ + "| ( ) )( \\ / )| ( \\/| |\n"
+ + "| (__/ / \\ (_) / | (__ | |\n"
+ + "| __ ( \\ / | __) | |\n"
+ + "| ( \\ \\ ) ( | ( (_)\n"
+ + "| )___) ) | | | (____/| _\n"
+ + "|/ \\___/ \\_/ (_______/(_)";
+
+ private static final String TAB = "\t";
+
+ private static final String EDIT_NAME_MESSAGE = "Enter new name:";
+ private static final String EDIT_CURRENT_PRICE_MESSAGE = "Enter new Current price:";
+ private static final String EDIT_SENTIMENT_MESSAGE = "Enter new Sentiment:";
+ private static final String EDIT_REMARK_MESSAGE = "Enter new Remarks:";
+ private static final String EDIT_PAST_RETURN_MESSAGE = "Enter new Past Returns:";
+ private static final String EDIT_ENTRY_MESSAGE = "Enter new Entry Price:";
+ private static final String EDIT_EXIT_MESSAGE = "Enter new Exit Price:";
+ private static final String EDIT_EXPIRY_MESSAGE = "Enter new Expiry (YYYY-MM-DD):";
+ private static final String EDIT_STATUS_MESSAGE = "Enter new Status (please enter either done or undone):";
+ private static final String WATCHLIST_HEADER = "CURRENT WATCHLIST";
+
+ private static final int NONE_FOUND = 0;
+
+ /**
+ * Displays the new instrument added along with its parameters.
+ *
+ * @param newInstrument The new instrument added to the list.
+ */
+ public static void displayInstrumentAdded(Instrument newInstrument) {
+ System.out.println(TAB + newInstrument.getGeneralParams() + " - has been added to list.");
+ }
+
+ /**
+ * Prompts user to enter the type of instrument being added.
+ */
+ public static void displayAddInstrumentFirstInstruction() {
+ System.out.println(TAB + TYPE_HEADER);
+ }
+
+ /**
+ * Prompts user to enter the name of the instrument being added.
+ *
+ * @param instrumentType The type of the instrument being added.
+ */
+ public static void displayAddInstrumentNameInstruction(String instrumentType) {
+ System.out.println(TAB + "Name of " + instrumentType + ": ");
+ }
+
+ /**
+ * Prompts user to enter the current price of the instrument being added.
+ */
+ public static void displayAddInstrumentCurrentPriceInstruction() {
+ System.out.println(TAB + CURRENT_PRICE_HEADER);
+ }
+
+ /**
+ * Prompts user to enter the sentiment of the instrument being added.
+ */
+ public static void displayAddInstrumentSentimentInstruction() {
+ System.out.println(TAB + SENTIMENT_HEADER);
+ }
+
+ /**
+ * Prompts user to enter the remark of the instrument being added.
+ */
+ public static void displayAddRemarkInstruction() {
+ System.out.println(TAB + REMARK_HEADER);
+ }
+
+ /**
+ * Prompts user to enter the expiry of the instrument being added.
+ */
+ public static void displayAddExpiryInstruction() {
+ System.out.println(TAB + EXPIRY_HEADER);
+ }
+
+ /**
+ * Prompts user to enter the entry price of the instrument being added.
+ */
+ public static void displayAddEntryPriceInstruction() {
+ System.out.println(TAB + ENTRY_PRICE_HEADER);
+ }
+
+ /**
+ * Prompts user to enter the exit price of the instrument being added.
+ */
+ public static void displayAddExitPriceInstruction() {
+ System.out.println(TAB + EXIT_PRICE_HEADER);
+ }
+
+ /**
+ * Prompts user to enter the past return of the instrument being added.
+ */
+ public static void displayAddPastReturnInstruction() {
+ System.out.println(TAB + PAST_RETURN_HEADER);
+ }
+
+ /**
+ * Displays all the instruments and their general parameters.
+ *
+ * @param instruments The list containing all the instruments.
+ */
+ public static void displayInstruments(ArrayList instruments) {
+ int idx = 0;
+ for (Instrument instrument: instruments) {
+ idx += 1;
+ System.out.println(constructLineInList(idx, instrument));
+ }
+ }
+
+ /**
+ * Displays all the instruments and their general parameters, along with separators and header for readability.
+ *
+ * @param instruments The list containing all instruments.
+ */
+ public static void displayAllInstruments(ArrayList instruments) {
+ System.out.println(LINE_DECORATOR);
+ System.out.println(WATCHLIST_HEADER);
+ displayInstruments(instruments);
+ System.out.println(LINE_DECORATOR);
+ }
+
+ /**
+ * Displays to user the number of instruments found or no instrument is found.
+ *
+ * @param numFound The number of instruments found.
+ * @param searchTerm The keyword being search.
+ */
+ private static void displayFoundMessage(int numFound, String searchTerm) {
+ if (numFound == NONE_FOUND) {
+ System.out.println("There were no instruments found for search string, " + searchTerm + ".");
+ return;
+ }
+ System.out.println("There were " + numFound + " instrument(s) found for search string, " + searchTerm + ".");
+ }
+
+ /**
+ * Displays to the user the list of instruments with name containing a specific keyword.
+ *
+ * @param instruments The list of instruments with name containing that keyword.
+ * @param searchString The keyword being used to search.
+ */
+ public static void displayInstrumentsFound(ArrayList instruments, String searchString) {
+ System.out.println(LINE_DECORATOR);
+ displayInstruments(instruments);
+ displayFoundMessage(instruments.size(), searchString);
+ System.out.println(LINE_DECORATOR);
+ }
+
+ /**
+ * Concatenates the instrument with its index and its general parameters.
+ *
+ * @param idx The index of the instrument.
+ * @param instrument The instrument to be displayed.
+ * @return The concatted string of index and general parameters.
+ */
+ private static String constructLineInList(int idx, Instrument instrument) {
+ return idx + INDEX_BRACKET + instrument.getGeneralParams();
+ }
+
+ /**
+ * Displays all the parameters of an instrument to the user.
+ *
+ * @param instrument The instrument to display to the user.
+ */
+ public static void displaySpecificInstrumentView(Instrument instrument) {
+ System.out.println(LINE_DECORATOR);
+ System.out.println(instrument.getAllParams());
+ System.out.println(LINE_DECORATOR);
+ }
+
+ //@@author williamlamjy
+ /**
+ * Informs the user of the instrument that has been marked as done.
+ *
+ * @param instrument The instrument that is marked as done.
+ */
+ public static void displayDoneInstrument(Instrument instrument) {
+ System.out.println(TAB + "Nice! I have marked this instrument as completed:"
+ + System.lineSeparator() + TAB + TAB
+ + instrument.getGeneralParams());
+ }
+
+ //@@author theodorekwok
+ /**
+ * Informs the user that an instrument has been deleted from the list.
+ *
+ * @param instrument The instrument that is deleted.
+ */
+ public static void displayInstrumentDeleted(Instrument instrument) {
+ System.out.println(LINE_DECORATOR);
+ System.out.println("Noted. " + instrument.getGeneralParams() + " - removed from your watchlist");
+ System.out.println(LINE_DECORATOR);
+ }
+
+ //@@author williamlamjy
+ /**
+ * Informs the user that the text file for saving cannot be detected and a new one will be created.
+ */
+ public static void displayCreateFile() {
+ System.out.println("Unable to find a saved file. Creating a new one now...");
+ }
+
+ /**
+ * Informs the user that a text file used for saving have been detected and its content will be read.
+ */
+ public static void displayLoadingFile() {
+ System.out.println("Found a saved file. Loading the saved data now...");
+ }
+
+ /**
+ * Displays the respective error message to the user when an error is encountered.
+ *
+ * @param e The error being encountered.
+ */
+ public static void showErrorMessage(Exception e) {
+ System.out.println(e.getMessage());
+ }
+
+ //@@author theodorekwok
+ /**
+ * Informs the user that an instrument in the text file is corrupted and will not be read from the file.
+ *
+ * @param idx The index of the instrument.
+ */
+ public static void ignoreCorruptedInstrument(AtomicInteger idx) {
+ System.out.println("Ignoring saved instrument " + idx + " as it was corrupted.");
+ System.out.println(LINE_DECORATOR);
+ }
+
+ //@@author KVignesh122
+ /**
+ * Displays a farewell message when the user exit the program.
+ */
+ public static void displayExitMessage() {
+ System.out.println(BYE_LOGO);
+ System.out.println("Thank you for using mTracker.\n"
+ + "MAY THE MARKETS BE WITH YOU!!!");
+ }
+
+ /**
+ * Displays to the user the current workspace the user is in.
+ *
+ * @param workspace The current workspace the user is in.
+ */
+ public static void displayPrompter(String workspace) {
+ String prompter = "mTracker$" + workspace + "> ";
+ System.out.print(prompter);
+ }
+
+ //@@author kum-wh
+ /**
+ * Prompts the user to enter the parameters to edit.
+ *
+ * @param instrument The instrument being edited.
+ */
+ public static void displayEditInstrumentFirstInstruction(Instrument instrument) {
+ System.out.println(TAB + "Please enter one or more " + instrument.getType()
+ + " parameters to edit separated by a single space only." + System.lineSeparator()
+ + TAB + instrument.editParameterInstructions());
+ }
+
+ /**
+ * Informs the user that a certain input parameters is invalid and will not be processed.
+ *
+ * @param inputAttribute The input parameters that is invalid.
+ */
+ public static void displayEditInvalidAttribute(String inputAttribute) {
+ System.out.println(inputAttribute + " is an invalid attribute of this instrument and will be ignored.");
+ }
+
+ /**
+ * Prompts the user to enter the new name.
+ */
+ public static void displayEditName() {
+ System.out.println(TAB + EDIT_NAME_MESSAGE);
+ }
+
+ /**
+ * Prompts the user to enter the new current price.
+ */
+ public static void displayEditCurrentPrice() {
+ System.out.println(TAB + EDIT_CURRENT_PRICE_MESSAGE);
+ }
+
+ /**
+ * Prompts the user to enter the new sentiment value.
+ */
+ public static void displayEditSentiment() {
+ System.out.println(TAB + EDIT_SENTIMENT_MESSAGE);
+ }
+
+ /**
+ * Prompts the user to enter the new remark.
+ */
+ public static void displayEditRemark() {
+ System.out.println(TAB + EDIT_REMARK_MESSAGE);
+ }
+
+ /**
+ * Prompts the user to enter the new past return value.
+ */
+ public static void displayEditPastReturn() {
+ System.out.println(TAB + EDIT_PAST_RETURN_MESSAGE);
+ }
+
+ /**
+ * Prompts the user to enter the new entry price.
+ */
+ public static void displayEditEntryPrice() {
+ System.out.println(TAB + EDIT_ENTRY_MESSAGE);
+ }
+
+ /**
+ * Prompts the user to enter the new exit price.
+ */
+ public static void displayEditExitPrice() {
+ System.out.println(TAB + EDIT_EXIT_MESSAGE);
+ }
+
+ /**
+ * Prompts the user to enter the new expiry value.
+ */
+ public static void displayEditExpiry() {
+ System.out.println(TAB + EDIT_EXPIRY_MESSAGE);
+ }
+
+ /**
+ * Prompts the user to enter the new done status.
+ */
+ public static void displayEditStatus() {
+ System.out.println(TAB + EDIT_STATUS_MESSAGE);
+ }
+
+ /**
+ * Displays the parameters of the instrument before and after editing.
+ *
+ * @param beforeEdit The parameters of the instrument before editing.
+ * @param afterEdit The parameters of the instrument after editing.
+ */
+ public static void displayEditChanges(String beforeEdit, String afterEdit) {
+ System.out.println(LINE_DECORATOR);
+ System.out.println("Before:");
+ System.out.println(beforeEdit);
+ System.out.println(LINE_DECORATOR);
+ System.out.println("Changed To:");
+ System.out.println(afterEdit);
+ System.out.println(LINE_DECORATOR);
+ }
+
+ /**
+ * Displays the parameters of the instrument before and after editing if changes were made,
+ * else display that no changes were made.
+ *
+ * @param beforeEdit The parameters of the instrument before editing.
+ * @param afterEdit The parameters of the instrument after editing.
+ */
+ public static void displayEditBeforeAfter(String beforeEdit, String afterEdit) {
+ if (beforeEdit.equals(afterEdit)) {
+ displayEditNoChange();
+ } else {
+ displayEditChanges(beforeEdit, afterEdit);
+ }
+ }
+
+ /**
+ * Informs the user no changes were made to if no changes were made in the edit function.
+ */
+ public static void displayEditNoChange() {
+ System.out.println("No changes to instrument was made.");
+ }
+
+ //@@author KVignesh122
+ /**
+ * Displays the message that greet the user on start up.
+ */
+ public static void greetAtStartUp() {
+ System.out.println(LINE_DECORATOR);
+ System.out.println(LOGO);
+ System.out.println("Hello! I am mTracker, your personal assistant bot that\n"
+ + "helps you keep track of the markets.\nWhat should I do for you now?");
+ System.out.println(LINE_DECORATOR);
+ }
+}
diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/mtracker/MTrackerTest.java
similarity index 79%
rename from src/test/java/seedu/duke/DukeTest.java
rename to src/test/java/seedu/mtracker/MTrackerTest.java
index 2dda5fd651..754b70f08a 100644
--- a/src/test/java/seedu/duke/DukeTest.java
+++ b/src/test/java/seedu/mtracker/MTrackerTest.java
@@ -1,10 +1,10 @@
-package seedu.duke;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
+package seedu.mtracker;
import org.junit.jupiter.api.Test;
-class DukeTest {
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class MTrackerTest {
@Test
public void sampleTest() {
assertTrue(true);
diff --git a/src/test/java/seedu/mtracker/commons/ValidateTest.java b/src/test/java/seedu/mtracker/commons/ValidateTest.java
new file mode 100644
index 0000000000..48a1a43971
--- /dev/null
+++ b/src/test/java/seedu/mtracker/commons/ValidateTest.java
@@ -0,0 +1,88 @@
+package seedu.mtracker.commons;
+
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.Arrays;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+//@@author williamlamjy
+class ValidateTest {
+
+ public static final String EMPTY_INPUT = "";
+ public static final String VALID_TEST_NAME = "testName";
+ public static final String VALID_PRICE = "123.43";
+ public static final String INVALID_PRICE = "32fvr";
+ public static final String[] VALID_SENTIMENTS = {"positive", "neutral", "negative"};
+ public static final String INVALID_SENTIMENT = "invalid";
+ public static final String INVALID_EXPIRY = "18 Oct";
+ public static final int DAYS_DIFFERENCE = 1;
+ public static final LocalDate FUTURE_DATE = LocalDate.now().plusDays(DAYS_DIFFERENCE);
+ public static final LocalDate PAST_DATE = LocalDate.now().minusDays(DAYS_DIFFERENCE);
+
+ public static final String VALID_TEST_INSTRUMENT = "testInstrument";
+
+ @Test
+ void validateName_validName_expectSuccess() {
+ assertTrue(Validate.isValidName(VALID_TEST_NAME, VALID_TEST_INSTRUMENT));
+ }
+
+ @Test
+ void validateName_emptyName_expectFailure() {
+ assertFalse(Validate.isValidName(EMPTY_INPUT, VALID_TEST_INSTRUMENT));
+ }
+
+ @Test
+ void validateCurrentPrice_validNumber_expectSuccess() {
+ assertTrue(Validate.isValidPrice(VALID_PRICE));
+ }
+
+ @Test
+ void validateCurrentPrice_invalidNumber_expectFailure() {
+ assertFalse(Validate.isValidPrice(INVALID_PRICE));
+ }
+
+ @Test
+ void validateCurrentPrice_emptyNumber_expectFailure() {
+ assertFalse(Validate.isValidPrice(EMPTY_INPUT));
+ }
+
+ @Test
+ void validateSentiment_validSentiment_expectSuccess() {
+ Arrays.stream(VALID_SENTIMENTS)
+ .forEach((sentiment) -> assertTrue(Validate.isValidSentiment(sentiment)));
+ }
+
+ @Test
+ void validateSentiment_invalidSentiment_expectFailure() {
+ assertFalse(Validate.isValidSentiment(INVALID_SENTIMENT));
+ }
+
+ @Test
+ void validateSentiment_emptySentiment_expectFailure() {
+ assertFalse(Validate.isValidSentiment(EMPTY_INPUT));
+ }
+
+ //@@author theodorekwok
+ @Test
+ void addExpiry_validExpiryInFuture_expectSuccess() {
+ assertTrue(Validate.isValidExpiry(FUTURE_DATE.toString()));
+ }
+
+ @Test
+ void addExpiry_emptyExpiry_expectFailure() {
+ assertFalse(Validate.isValidExpiry(EMPTY_INPUT));
+ }
+
+ @Test
+ void addExpiry_invalidExpiry_expectFailure() {
+ assertFalse(Validate.isValidExpiry(INVALID_EXPIRY));
+ }
+
+ @Test
+ void addExpiry_validExpiryButIsInPast_expectFailure() {
+ assertFalse(Validate.isValidExpiry(PAST_DATE.toString()));
+ }
+}
diff --git a/src/test/java/seedu/mtracker/console/AddCryptoParserTest.java b/src/test/java/seedu/mtracker/console/AddCryptoParserTest.java
new file mode 100644
index 0000000000..254af79daf
--- /dev/null
+++ b/src/test/java/seedu/mtracker/console/AddCryptoParserTest.java
@@ -0,0 +1,196 @@
+package seedu.mtracker.console;
+
+import org.junit.jupiter.api.Test;
+import seedu.mtracker.commons.error.OperationAbortedError;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+//@@author williamlamjy
+class AddCryptoParserTest extends GeneralInstrumentParserTest {
+ public static final int PARAMETER_SIZE = 5;
+
+ public static final String USER_INPUT_NO_REMARK = "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + " ";
+
+ public static final String USER_INPUT_WITH_REMARK = "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ public static final String[] EXPECTED_PARAMS_NO_REMARK = {
+ "TTTXXX",
+ "23.4",
+ "positive",
+ String.valueOf(FUTURE_DATE),
+ "",
+ };
+
+ public static final String[] EXPECTED_PARAMS_WITH_REMARK = {
+ "TTTXXX",
+ "23.4",
+ "positive",
+ String.valueOf(FUTURE_DATE),
+ "fooRemarks"
+ };
+
+ public static final String USER_INPUT_TRY_INVALID_NAME = SEPARATOR_SPECIFIER.repeat(2) + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + " ";
+
+ public static final String USER_INPUT_TRY_INVALID_PRICE = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "2sd3.4"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ public static final String USER_INPUT_TRY_INVALID_SENTIMENT = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "foobar"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER.repeat(2) + "positive"
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ public static final String USER_INPUT_TRY_EMPTY_EXPIRY = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER.repeat(2) + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ public static final String USER_INPUT_TRY_INVALID_EXPIRY = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + "2021/11/1"
+ + SEPARATOR_SPECIFIER + "1 January 2021"
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ public static final String USER_INPUT_TRY_PAST_EXPIRY = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + PAST_DATE
+ + SEPARATOR_SPECIFIER + PAST_DATE
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ //@@author KVignesh122
+ public static final String USER_INPUT_TRY_ABORT_AT_REMARK = "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + ABORT;
+
+ public static final String USER_INPUT_TRY_ABORT_AT_NAME = SEPARATOR_SPECIFIER + ABORT;
+
+ public static final String USER_INPUT_TRY_ABORT_AT_PRICE = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "2sd3.4"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER + ABORT;
+
+ public static final String USER_INPUT_TRY_ABORT_AT_SENTIMENT = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "foobar"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER.repeat(2) + ABORT;
+
+ public static final String USER_INPUT_TRY_ABORT_AT_EXPIRY = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER.repeat(2) + ABORT;
+
+ //@@author williamlamjy
+ void testCryptoParameters(String input, String[] expectedParameters) throws OperationAbortedError {
+ simulateConsoleInput(input);
+ AddCryptoParser testCryptoParser = new AddCryptoParser();
+ verifyInstrumentParameters(testCryptoParser, expectedParameters);
+ }
+
+ @Override
+ public int getParameterSize() {
+ return PARAMETER_SIZE;
+ }
+
+ @Test
+ void addCryptoParams_noRemark_expectSuccess() throws OperationAbortedError {
+ testCryptoParameters(USER_INPUT_NO_REMARK, EXPECTED_PARAMS_NO_REMARK);
+ }
+
+ @Test
+ void addCryptoParams_allValidParametersWithRemark_expectSuccess() throws OperationAbortedError {
+ testCryptoParameters(USER_INPUT_WITH_REMARK,
+ EXPECTED_PARAMS_WITH_REMARK);
+ }
+
+ @Test
+ void addCryptoParams_tryInvalidNameMultipleTimes_expectSuccess() throws OperationAbortedError {
+ testCryptoParameters(USER_INPUT_TRY_INVALID_NAME, EXPECTED_PARAMS_NO_REMARK);
+ }
+
+ @Test
+ void addCryptoParams_tryInvalidPriceMultipleTimes_expectSuccess() throws OperationAbortedError {
+ testCryptoParameters(USER_INPUT_TRY_INVALID_PRICE,
+ EXPECTED_PARAMS_WITH_REMARK);
+ }
+
+ @Test
+ void addCryptoParams_tryInvalidSentimentMultipleTimes_expectSuccess() throws OperationAbortedError {
+ testCryptoParameters(USER_INPUT_TRY_INVALID_SENTIMENT,
+ EXPECTED_PARAMS_WITH_REMARK);
+ }
+
+ @Test
+ void addCryptoParams_tryEmptyExpiryMultipleTimes_expectSuccess() throws OperationAbortedError {
+ testCryptoParameters(USER_INPUT_TRY_EMPTY_EXPIRY,
+ EXPECTED_PARAMS_WITH_REMARK);
+ }
+
+ @Test
+ void addForexParams_tryInvalidDateMultipleTimes_expectSuccess() throws OperationAbortedError {
+ testCryptoParameters(USER_INPUT_TRY_INVALID_EXPIRY, EXPECTED_PARAMS_WITH_REMARK);
+ }
+
+ @Test
+ void addForexParams_tryPastDateMultipleTimes_expectSuccess() throws OperationAbortedError {
+ testCryptoParameters(USER_INPUT_TRY_PAST_EXPIRY, EXPECTED_PARAMS_WITH_REMARK);
+ }
+
+ //@@author KVignesh122
+ @Test
+ void addCryptoParams_abortAtName_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testCryptoParameters(USER_INPUT_TRY_ABORT_AT_NAME, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addCryptoParams_abortAtPrice_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testCryptoParameters(USER_INPUT_TRY_ABORT_AT_PRICE, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addCryptoParams_abortAtSentiment_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testCryptoParameters(USER_INPUT_TRY_ABORT_AT_SENTIMENT, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addCryptoParams_abortAtExpiry_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testCryptoParameters(USER_INPUT_TRY_ABORT_AT_EXPIRY, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addCryptoParams_abortAtRemark_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testCryptoParameters(USER_INPUT_TRY_ABORT_AT_REMARK, NO_PARAMS_EXPECTED));
+ }
+}
diff --git a/src/test/java/seedu/mtracker/console/AddEtfParserTest.java b/src/test/java/seedu/mtracker/console/AddEtfParserTest.java
new file mode 100644
index 0000000000..a12117b959
--- /dev/null
+++ b/src/test/java/seedu/mtracker/console/AddEtfParserTest.java
@@ -0,0 +1,173 @@
+package seedu.mtracker.console;
+
+import org.junit.jupiter.api.Test;
+import seedu.mtracker.commons.error.OperationAbortedError;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+//@@author kum-wh
+public class AddEtfParserTest extends GeneralInstrumentParserTest {
+ public static final int PARAMETER_SIZE = 5;
+
+ public static final String USER_INPUT_NO_REMARK = "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + "50.0"
+ + SEPARATOR_SPECIFIER + " ";
+
+ public static final String USER_INPUT_All_PARAM = "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + "50.0"
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ public static final String[] EXPECTED_PARAMS_NO_REMARK = {
+ "TTTXXX",
+ "23.4",
+ "positive",
+ "50.0",
+ "",
+ };
+
+ public static final String[] EXPECTED_PARAMS_ALL_PARAM = {
+ "TTTXXX",
+ "23.4",
+ "positive",
+ "50.0",
+ "fooRemarks"
+ };
+
+ public static final String[] EXPECTED_PARAMS_INVALID_PAST_RETURN = {
+ "TTTXXX",
+ "23.4",
+ "positive",
+ "-101.0",
+ "fooRemarks"
+ };
+
+ public static final String USER_INPUT_TRY_INVALID_NAME = SEPARATOR_SPECIFIER.repeat(2) + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + "50.0"
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+
+ public static final String USER_INPUT_TRY_INVALID_PRICE = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "2sd3.4"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + "50.0"
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ public static final String USER_INPUT_TRY_INVALID_SENTIMENT = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "foobar"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER.repeat(2) + "positive"
+ + SEPARATOR_SPECIFIER + "50.0"
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ public static final String USER_INPUT_TRY_EMPTY_PAST_RETURN = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + ""
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ //@@author KVignesh122
+ public static final String USER_INPUT_TRY_ABORT_AT_NAME = SEPARATOR_SPECIFIER.repeat(2) + ABORT;
+
+ public static final String USER_INPUT_TRY_ABORT_AT_PRICE = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "2sd3.4"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER + ABORT;
+
+ public static final String USER_INPUT_TRY_ABORT_AT_SENTIMENT = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER.repeat(2) + ABORT;
+
+ public static final String USER_INPUT_TRY_ABORT_AT_PAST_RETURN = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + ABORT;
+
+ public static final String USER_INPUT_TRY_ABORT_AT_REMARK = "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + "50.0"
+ + SEPARATOR_SPECIFIER + ABORT;
+
+ //@@author kum-wh
+ void testEtfParameters(String input, String[] expectedEtfParameters) throws OperationAbortedError {
+ simulateConsoleInput(input);
+ AddEtfParser testEtfParser = new AddEtfParser();
+ verifyInstrumentParameters(testEtfParser, expectedEtfParameters);
+ }
+
+ @Override
+ public int getParameterSize() {
+ return PARAMETER_SIZE;
+ }
+
+ @Test
+ void addEtfParams_noRemark_expectSuccess() throws OperationAbortedError {
+ testEtfParameters(USER_INPUT_NO_REMARK, EXPECTED_PARAMS_NO_REMARK);
+ }
+
+ @Test
+ void addEtfParams_noPastReturn_expectSuccess() throws OperationAbortedError {
+ testEtfParameters(USER_INPUT_TRY_EMPTY_PAST_RETURN, EXPECTED_PARAMS_INVALID_PAST_RETURN);
+ }
+
+ @Test
+ void addEtfParams_allParameters_expectSuccess() throws OperationAbortedError {
+ testEtfParameters(USER_INPUT_All_PARAM, EXPECTED_PARAMS_ALL_PARAM);
+ }
+
+ @Test
+ void addEftParams_InvalidName_expectSuccess() throws OperationAbortedError {
+ testEtfParameters(USER_INPUT_TRY_INVALID_NAME, EXPECTED_PARAMS_ALL_PARAM);
+ }
+
+ @Test
+ void addEftParams_InvalidPrice_expectSuccess() throws OperationAbortedError {
+ testEtfParameters(USER_INPUT_TRY_INVALID_PRICE, EXPECTED_PARAMS_ALL_PARAM);
+ }
+
+ @Test
+ void addEtfParams_InvalidSentiment_expectSuccess() throws OperationAbortedError {
+ testEtfParameters(USER_INPUT_TRY_INVALID_SENTIMENT, EXPECTED_PARAMS_ALL_PARAM);
+ }
+
+ //@@author KVignesh122
+ @Test
+ void addEtfParams_abortAtName_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testEtfParameters(USER_INPUT_TRY_ABORT_AT_NAME, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addEtfParams_abortAtPrice_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testEtfParameters(USER_INPUT_TRY_ABORT_AT_PRICE, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addEtfParams_abortAtSentiment_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testEtfParameters(USER_INPUT_TRY_ABORT_AT_SENTIMENT, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addEtfParams_abortAtReturn_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testEtfParameters(USER_INPUT_TRY_ABORT_AT_PAST_RETURN, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addEtfParams_abortAtRemark_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testEtfParameters(USER_INPUT_TRY_ABORT_AT_REMARK, NO_PARAMS_EXPECTED));
+ }
+}
diff --git a/src/test/java/seedu/mtracker/console/AddForexParserTest.java b/src/test/java/seedu/mtracker/console/AddForexParserTest.java
new file mode 100644
index 0000000000..43f4cb0a85
--- /dev/null
+++ b/src/test/java/seedu/mtracker/console/AddForexParserTest.java
@@ -0,0 +1,231 @@
+package seedu.mtracker.console;
+
+import org.junit.jupiter.api.Test;
+import seedu.mtracker.commons.error.OperationAbortedError;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+//@@author KVignesh122
+class AddForexParserTest extends GeneralInstrumentParserTest {
+ public static final int PARAMETER_SIZE = 7;
+
+ public static final String[] EXPECTED_PARAMS_NO_REMARK = {
+ "TTTXXX",
+ "1.11",
+ "positive",
+ "1.15",
+ "1.30",
+ FUTURE_DATE.toString(),
+ ""
+ };
+
+ public static final String[] EXPECTED_PARAMS_WITH_REMARK = {
+ "TTTXXX",
+ "0.81",
+ "negative",
+ "0.79",
+ "0.70",
+ FUTURE_DATE.toString(),
+ "fooRemarks"
+ };
+
+ public static final String USER_INPUT_NO_REMARK = "TTTXXX"
+ + SEPARATOR_SPECIFIER + "1.11"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + "1.15"
+ + SEPARATOR_SPECIFIER + "1.30"
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + " ";
+
+ public static final String USER_INPUT_TRY_ABORT_AT_REMARK = "TTTXXX"
+ + SEPARATOR_SPECIFIER + "1.11"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + "1.15"
+ + SEPARATOR_SPECIFIER + "1.30"
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + ABORT;
+
+ public static final String USER_INPUT_WITH_REMARK = "TTTXXX"
+ + SEPARATOR_SPECIFIER + "0.81"
+ + SEPARATOR_SPECIFIER + "negative"
+ + SEPARATOR_SPECIFIER + "0.79"
+ + SEPARATOR_SPECIFIER + "0.70"
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ public static final String USER_INPUT_TRY_INVALID_NAME = SEPARATOR_SPECIFIER.repeat(2) + "TTXX"
+ + SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "1.11"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + "1.15"
+ + SEPARATOR_SPECIFIER + "1.30"
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + " ";
+
+ public static final String USER_INPUT_TRY_ABORT_AT_NAME = SEPARATOR_SPECIFIER.repeat(2) + DONT_ABORT
+ + SEPARATOR_SPECIFIER + ABORT;
+
+ public static final String USER_INPUT_TRY_INVALID_PRICE = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "lol"
+ + SEPARATOR_SPECIFIER + "0.81"
+ + SEPARATOR_SPECIFIER + "negative"
+ + SEPARATOR_SPECIFIER + "foobar"
+ + SEPARATOR_SPECIFIER + "0.79"
+ + SEPARATOR_SPECIFIER.repeat(2) + "0.70"
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ public static final String USER_INPUT_TRY_ABORT_AT_CURRENT_PRICE = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "lol"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER + ABORT;
+
+ public static final String USER_INPUT_TRY_INVALID_SENTIMENT = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "0.81"
+ + SEPARATOR_SPECIFIER + "foobar"
+ + SEPARATOR_SPECIFIER.repeat(2) + "negative"
+ + SEPARATOR_SPECIFIER + "0.79"
+ + SEPARATOR_SPECIFIER + "0.70"
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ public static final String USER_INPUT_TRY_INVALID_EXPIRY = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "0.81"
+ + SEPARATOR_SPECIFIER + "foobar"
+ + SEPARATOR_SPECIFIER + "negative"
+ + SEPARATOR_SPECIFIER + "0.79"
+ + SEPARATOR_SPECIFIER + "0.70"
+ + SEPARATOR_SPECIFIER.repeat(2) + "testDate"
+ + SEPARATOR_SPECIFIER + "31/12/2021"
+ + SEPARATOR_SPECIFIER + "2021.01.01"
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ public static final String USER_INPUT_TRY_PAST_EXPIRY = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "0.81"
+ + SEPARATOR_SPECIFIER + "foobar"
+ + SEPARATOR_SPECIFIER + "negative"
+ + SEPARATOR_SPECIFIER + "0.79"
+ + SEPARATOR_SPECIFIER + "0.70"
+ + SEPARATOR_SPECIFIER + PAST_DATE
+ + SEPARATOR_SPECIFIER + PAST_DATE
+ + SEPARATOR_SPECIFIER + FUTURE_DATE
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ public static final String USER_INPUT_TRY_ABORT_AT_SENTIMENT = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "0.81"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER.repeat(2) + ABORT;
+
+ public static final String USER_INPUT_TRY_ABORT_AT_ENTRY_PRICE = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "lol"
+ + SEPARATOR_SPECIFIER + "0.81"
+ + SEPARATOR_SPECIFIER + "negative"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER + ABORT;
+
+ public static final String USER_INPUT_TRY_ABORT_AT_EXIT_PRICE = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "lol"
+ + SEPARATOR_SPECIFIER + "0.81"
+ + SEPARATOR_SPECIFIER + "negative"
+ + SEPARATOR_SPECIFIER + "0.81"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER + ABORT;
+
+ public static final String USER_INPUT_TRY_ABORT_AT_EXPIRY = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "0.81"
+ + SEPARATOR_SPECIFIER + "foobar"
+ + SEPARATOR_SPECIFIER.repeat(2) + "negative"
+ + SEPARATOR_SPECIFIER + "0.79"
+ + SEPARATOR_SPECIFIER + "0.70"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER + ABORT;
+
+ void testForexParameters(String input, String[] expectedForexParameters) throws OperationAbortedError {
+ simulateConsoleInput(input);
+ AddForexParser testForexParser = new AddForexParser();
+ verifyInstrumentParameters(testForexParser, expectedForexParameters);
+ }
+
+ @Override
+ public int getParameterSize() {
+ return PARAMETER_SIZE;
+ }
+
+ @Test
+ void addForexParams_allValidParameters_expectSuccess() throws OperationAbortedError {
+ testForexParameters(USER_INPUT_NO_REMARK, EXPECTED_PARAMS_NO_REMARK);
+ }
+
+ @Test
+ void addForexParams_allValidParametersWithRemark_expectSuccess() throws OperationAbortedError {
+ testForexParameters(USER_INPUT_WITH_REMARK, EXPECTED_PARAMS_WITH_REMARK);
+ }
+
+ @Test
+ void addForexParams_tryInvalidNameMultipleTimes_expectSuccess() throws OperationAbortedError {
+ testForexParameters(USER_INPUT_TRY_INVALID_NAME, EXPECTED_PARAMS_NO_REMARK);
+ }
+
+ @Test
+ void addForexParams_tryInvalidPriceMultipleTimes_expectSuccess() throws OperationAbortedError {
+ testForexParameters(USER_INPUT_TRY_INVALID_PRICE, EXPECTED_PARAMS_WITH_REMARK);
+ }
+
+ @Test
+ void addForexParams_tryInvalidSentimentMultipleTimes_expectSuccess() throws OperationAbortedError {
+ testForexParameters(USER_INPUT_TRY_INVALID_SENTIMENT, EXPECTED_PARAMS_WITH_REMARK);
+ }
+
+ @Test
+ void addForexParams_tryInvalidDateMultipleTimes_expectSuccess() throws OperationAbortedError {
+ testForexParameters(USER_INPUT_TRY_INVALID_EXPIRY, EXPECTED_PARAMS_WITH_REMARK);
+ }
+
+ @Test
+ void addForexParams_tryPastDateMultipleTimes_expectSuccess() throws OperationAbortedError {
+ testForexParameters(USER_INPUT_TRY_PAST_EXPIRY, EXPECTED_PARAMS_WITH_REMARK);
+ }
+
+ @Test
+ void addForexParams_abortAtName_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testForexParameters(USER_INPUT_TRY_ABORT_AT_NAME, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addForexParams_abortAtCurrentPrice_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testForexParameters(USER_INPUT_TRY_ABORT_AT_CURRENT_PRICE, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addForexParams_abortAtSentiment_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testForexParameters(USER_INPUT_TRY_ABORT_AT_SENTIMENT, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addForexParams_abortAtExpiry_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testForexParameters(USER_INPUT_TRY_ABORT_AT_EXPIRY, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addForexParams_abortAtRemark_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testForexParameters(USER_INPUT_TRY_ABORT_AT_REMARK, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addForexParams_abortAtEntryPrice_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testForexParameters(USER_INPUT_TRY_ABORT_AT_ENTRY_PRICE, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addForexParams_abortAtExitPrice_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testForexParameters(USER_INPUT_TRY_ABORT_AT_EXIT_PRICE, EXPECTED_PARAMS_NO_REMARK));
+ }
+}
diff --git a/src/test/java/seedu/mtracker/console/AddStockParserTest.java b/src/test/java/seedu/mtracker/console/AddStockParserTest.java
new file mode 100644
index 0000000000..cdd440c866
--- /dev/null
+++ b/src/test/java/seedu/mtracker/console/AddStockParserTest.java
@@ -0,0 +1,136 @@
+package seedu.mtracker.console;
+
+import org.junit.jupiter.api.Test;
+import seedu.mtracker.commons.error.OperationAbortedError;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+//@@author theodorekwok
+class AddStockParserTest extends GeneralInstrumentParserTest {
+ public static final int PARAMETER_SIZE = 4;
+
+ public static final String USER_INPUT_NO_REMARK = "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + " ";
+
+ public static final String USER_INPUT_WITH_REMARK = "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+
+ public static final String[] EXPECTED_PARAMS_NO_REMARK = {
+ "TTTXXX",
+ "23.4",
+ "positive",
+ "",
+ };
+
+ public static final String[] EXPECTED_PARAMS_WITH_REMARK = {
+ "TTTXXX",
+ "23.4",
+ "positive",
+ "fooRemarks"
+ };
+
+ public static final String USER_INPUT_TRY_INVALID_NAME = SEPARATOR_SPECIFIER.repeat(2) + " "
+ + SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + " ";
+
+ public static final String USER_INPUT_TRY_INVALID_PRICE = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "2sd3.4"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ public static final String USER_INPUT_TRY_INVALID_SENTIMENT = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "foobar"
+ + SEPARATOR_SPECIFIER.repeat(2) + DONT_ABORT
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + "fooRemarks";
+
+ //@@author KVignesh122
+ public static final String USER_INPUT_TRY_ABORT_AT_NAME = ABORT;
+
+ public static final String USER_INPUT_TRY_ABORT_AT_PRICE = "TTTXXX"
+ + SEPARATOR_SPECIFIER + DONT_ABORT
+ + SEPARATOR_SPECIFIER + ABORT;
+
+ public static final String USER_INPUT_TRY_ABORT_AT_SENTIMENT = SEPARATOR_SPECIFIER + "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "foobar"
+ + SEPARATOR_SPECIFIER.repeat(2) + DONT_ABORT
+ + SEPARATOR_SPECIFIER + ABORT;
+
+ public static final String USER_INPUT_TRY_ABORT_AT_REMARK = "TTTXXX"
+ + SEPARATOR_SPECIFIER + "23.4"
+ + SEPARATOR_SPECIFIER + "positive"
+ + SEPARATOR_SPECIFIER + ABORT;
+
+ //@@author theodorekwok
+ void testStockParameters(String input, String[] expectedStockParameters) throws OperationAbortedError {
+ simulateConsoleInput(input);
+ AddStockParser testStockParser = new AddStockParser();
+ verifyInstrumentParameters(testStockParser, expectedStockParameters);
+ }
+
+ @Override
+ public int getParameterSize() {
+ return PARAMETER_SIZE;
+ }
+
+ @Test
+ void addStockParams_allValidParameters_expectSuccess() throws OperationAbortedError {
+ testStockParameters(USER_INPUT_NO_REMARK, EXPECTED_PARAMS_NO_REMARK);
+ }
+
+ @Test
+ void addStockParams_allValidParametersWithRemark_expectSuccess() throws OperationAbortedError {
+ testStockParameters(USER_INPUT_WITH_REMARK, EXPECTED_PARAMS_WITH_REMARK);
+ }
+
+ @Test
+ void addStockParams_tryInvalidNameMultipleTimes_expectSuccess() throws OperationAbortedError {
+ testStockParameters(USER_INPUT_TRY_INVALID_NAME, EXPECTED_PARAMS_NO_REMARK);
+ }
+
+ @Test
+ void addStockParams_tryInvalidPriceMultipleTimes_expectSuccess() throws OperationAbortedError {
+ testStockParameters(USER_INPUT_TRY_INVALID_PRICE, EXPECTED_PARAMS_WITH_REMARK);
+ }
+
+ @Test
+ void addStockParams_tryInvalidSentimentMultipleTimes_expectSuccess() throws OperationAbortedError {
+ testStockParameters(USER_INPUT_TRY_INVALID_SENTIMENT, EXPECTED_PARAMS_WITH_REMARK);
+ }
+
+ //@@author KVignesh122
+ @Test
+ void addStockParams_abortAtName_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testStockParameters(USER_INPUT_TRY_ABORT_AT_NAME, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addStockParams_abortAtPrice_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testStockParameters(USER_INPUT_TRY_ABORT_AT_PRICE, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addStockParams_abortAtSentiment_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testStockParameters(USER_INPUT_TRY_ABORT_AT_SENTIMENT, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void addStockParams_abortAtRemark_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> testStockParameters(USER_INPUT_TRY_ABORT_AT_REMARK, NO_PARAMS_EXPECTED));
+ }
+}
diff --git a/src/test/java/seedu/mtracker/console/EditInstrumentParserTest.java b/src/test/java/seedu/mtracker/console/EditInstrumentParserTest.java
new file mode 100644
index 0000000000..3c990fca16
--- /dev/null
+++ b/src/test/java/seedu/mtracker/console/EditInstrumentParserTest.java
@@ -0,0 +1,125 @@
+package seedu.mtracker.console;
+
+import org.junit.jupiter.api.Test;
+import seedu.mtracker.commons.error.OperationAbortedError;
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.model.subinstrument.Stock;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+//@@author kum-wh
+public class EditInstrumentParserTest extends GeneralInstrumentParserTest {
+
+ public static final String USER_INPUT_EDIT_NAME_AND_REMARK = "TTTXXX" + SEPARATOR_SPECIFIER + "Test Remark";
+ public static final String[] EXPECTED_KEYS_NAME_AND_REMARK = {"name", "remarks"};
+ public static final String[] EXPECTED_OUTPUT_NAME_AND_REMARK = {"TTTXXX", "Test Remark"};
+ public static final HashSet PARAM_INPUT_NAME_AND_REMARK = new HashSet<>(Arrays.asList("name", "remarks"));
+
+ public static final String USER_INPUT_EDIT_CURRENT_PRICE_AND_SENTIMENT = "100" + SEPARATOR_SPECIFIER + "neutral";
+ public static final String[] EXPECTED_KEYS_CURRENT_PRICE_AND_SENTIMENT = {"current-price", "sentiment"};
+ public static final String[] EXPECTED_OUTPUT_CURRENT_PRICE_AND_SENTIMENT = {"100", "neutral"};
+ public static final HashSet PARAM_INPUT_CURRENT_PRICE_AND_SENTIMENT =
+ new HashSet<>(Arrays.asList("current-price", "sentiment"));
+
+ public static final String USER_INPUT_EDIT_ENTRY_PRICE_AND_EXIT_PRICE = "10" + SEPARATOR_SPECIFIER + "100";
+ public static final String[] EXPECTED_KEYS_ENTRY_PRICE_AND_EXIT_PRICE = {"entry-price", "exit-price"};
+ public static final String[] EXPECTED_OUTPUT_ENTRY_PRICE_AND_EXIT_PRICE = {"10", "100"};
+ public static final HashSet PARAM_INPUT_ENTRY_PRICE_AND_EXIT_PRICE =
+ new HashSet<>(Arrays.asList("entry-price", "exit-price"));
+
+ public static final String USER_INPUT_ABORT = ABORT;
+ public static final HashSet PARAM_INPUT_NAME = new HashSet<>(List.of("name"));
+ public static final HashSet PARAM_INPUT_CURRENT_PRICE = new HashSet<>(List.of("current-price"));
+ public static final HashSet PARAM_INPUT_SENTIMENT = new HashSet<>(List.of("sentiment"));
+ public static final HashSet PARAM_INPUT_REMARK = new HashSet<>(List.of("remarks"));
+
+ public static final String TEST_NAME = "Test";
+ public static final double TEST_PRICE = 1.0;
+ public static final String TEST_SENTIMENT = "neutral";
+ public static final String TEST_REMARK = "";
+ public static final Instrument TEST_STOCK = new Stock(TEST_NAME, TEST_PRICE, TEST_SENTIMENT, TEST_REMARK);
+ public static final int TEST_INDEX = 0;
+ public static final int SIZE_ZERO = 0;
+
+ @Override
+ public int getParameterSize() {
+ return SIZE_ZERO;
+ }
+
+ HashMap initialiseTestResources(String[] expectedKeys, String[] expectedValues) {
+ HashMap expectedResult = new HashMap<>();
+ assert (expectedKeys.length == expectedValues.length);
+ for (int i = 0; i < expectedKeys.length; i++) {
+ expectedResult.put(expectedKeys[i], expectedValues[i]);
+ }
+ return expectedResult;
+ }
+
+ void testEditInstrumentParameters(String input, HashSet expectedParameters,
+ String[] expectedKeys, String[] expectedValues) throws OperationAbortedError {
+ simulateConsoleInput(input);
+ HashMap expectedHash = initialiseTestResources(expectedKeys, expectedValues);
+ EditInstrumentParser editInstrumentParser = new EditInstrumentParser();
+ editInstrumentParser.createEditCommand(expectedParameters, TEST_STOCK, TEST_INDEX);
+ HashMap outputHash = EditInstrumentParser.getEditedParametersHash();
+ assertEquals(outputHash, expectedHash);
+ }
+
+ @Test
+ void editInstrumentParam_nameAndRemark_expectSuccess() throws OperationAbortedError {
+ testEditInstrumentParameters(USER_INPUT_EDIT_NAME_AND_REMARK,
+ PARAM_INPUT_NAME_AND_REMARK,
+ EXPECTED_KEYS_NAME_AND_REMARK,
+ EXPECTED_OUTPUT_NAME_AND_REMARK);
+ }
+
+ @Test
+ void editInstrumentParam_currentPriceAndSentiment_expectSuccess() throws OperationAbortedError {
+ testEditInstrumentParameters(USER_INPUT_EDIT_CURRENT_PRICE_AND_SENTIMENT,
+ PARAM_INPUT_CURRENT_PRICE_AND_SENTIMENT,
+ EXPECTED_KEYS_CURRENT_PRICE_AND_SENTIMENT,
+ EXPECTED_OUTPUT_CURRENT_PRICE_AND_SENTIMENT);
+ }
+
+ @Test
+ void editInstrumentParam_entryAndExitPrice_expectSuccess() throws OperationAbortedError {
+ testEditInstrumentParameters(USER_INPUT_EDIT_ENTRY_PRICE_AND_EXIT_PRICE,
+ PARAM_INPUT_ENTRY_PRICE_AND_EXIT_PRICE,
+ EXPECTED_KEYS_ENTRY_PRICE_AND_EXIT_PRICE,
+ EXPECTED_OUTPUT_ENTRY_PRICE_AND_EXIT_PRICE);
+ }
+
+ @Test
+ void editInstrumentParam_abortAtName_expectException() {
+ assertThrows(OperationAbortedError.class,() ->
+ testEditInstrumentParameters(USER_INPUT_ABORT, PARAM_INPUT_NAME,
+ NO_PARAMS_EXPECTED, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void editInstrumentParam_abortAtCurrentPrice_expectException() {
+ assertThrows(OperationAbortedError.class, () ->
+ testEditInstrumentParameters(USER_INPUT_ABORT, PARAM_INPUT_CURRENT_PRICE,
+ NO_PARAMS_EXPECTED, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void editInstrumentParam_abortAtSentiments_expectException() {
+ assertThrows(OperationAbortedError.class, () ->
+ testEditInstrumentParameters(USER_INPUT_ABORT, PARAM_INPUT_SENTIMENT,
+ NO_PARAMS_EXPECTED, NO_PARAMS_EXPECTED));
+ }
+
+ @Test
+ void editInstrumentParam_abortAtRemark_expectException() {
+ assertThrows(OperationAbortedError.class, () ->
+ testEditInstrumentParameters(USER_INPUT_ABORT, PARAM_INPUT_REMARK,
+ NO_PARAMS_EXPECTED, NO_PARAMS_EXPECTED));
+ }
+}
diff --git a/src/test/java/seedu/mtracker/console/GeneralInstrumentParserTest.java b/src/test/java/seedu/mtracker/console/GeneralInstrumentParserTest.java
new file mode 100644
index 0000000000..be5ce8c434
--- /dev/null
+++ b/src/test/java/seedu/mtracker/console/GeneralInstrumentParserTest.java
@@ -0,0 +1,48 @@
+package seedu.mtracker.console;
+
+import seedu.mtracker.commons.error.OperationAbortedError;
+
+import java.io.ByteArrayInputStream;
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+//@@author KVignesh122
+public abstract class GeneralInstrumentParserTest {
+
+ public static final String ABORT = "abort";
+ public static final String DONT_ABORT = "don't abort";
+
+ public static final String[] NO_PARAMS_EXPECTED = {};
+
+ protected static final String SEPARATOR_SPECIFIER = "%1$s";
+ protected static final int DAYS_DIFFERENCE = 1;
+ protected static final LocalDate FUTURE_DATE = LocalDate.now().plusDays(DAYS_DIFFERENCE);
+ protected static final LocalDate PAST_DATE = LocalDate.now().minusDays(DAYS_DIFFERENCE);
+
+ void verifyInstrumentParameters(AddInstrumentParser testInstrumentParser, String[] expectedParameters)
+ throws OperationAbortedError {
+ testInstrumentParser.initParameters();
+ testInstrumentParser.getInstrumentParameters();
+ checkParameters(testInstrumentParser, expectedParameters);
+ }
+
+ public abstract int getParameterSize();
+
+ //@@author theodorekwok
+ String formatConsoleInput(String input) {
+ return String.format(input, System.lineSeparator());
+ }
+
+ void simulateConsoleInput(String input) {
+ String formattedInput = formatConsoleInput(input);
+ ByteArrayInputStream inputStreamBytes = new ByteArrayInputStream(formattedInput.getBytes());
+ System.setIn(inputStreamBytes);
+ }
+
+ void checkParameters(AddInstrumentParser testInstrumentParser, String[] expectedParameters) {
+ for (int i = 0; i < getParameterSize(); i++) {
+ assertEquals(testInstrumentParser.getParameters().get(i), expectedParameters[i]);
+ }
+ }
+}
diff --git a/src/test/java/seedu/mtracker/console/InputParserTest.java b/src/test/java/seedu/mtracker/console/InputParserTest.java
new file mode 100644
index 0000000000..8acd168052
--- /dev/null
+++ b/src/test/java/seedu/mtracker/console/InputParserTest.java
@@ -0,0 +1,144 @@
+package seedu.mtracker.console;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.mtracker.commands.DeleteCommand;
+import seedu.mtracker.commands.DoneCommand;
+import seedu.mtracker.commons.error.AlreadyDoneError;
+import seedu.mtracker.commons.error.InvalidBoundsError;
+import seedu.mtracker.commons.error.InvalidEmptyIndexError;
+import seedu.mtracker.commons.error.InvalidIndexError;
+import seedu.mtracker.commons.error.OperationAbortedError;
+import seedu.mtracker.model.Instrument;
+import seedu.mtracker.model.subinstrument.Stock;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+//@@author theodorekwok
+class InputParserTest {
+ public static final int VALID_INDEX = 1;
+
+ public static final String[] NO_INDEX_DELETE_INPUT = { "delete" };
+ public static final String[] INVALID_INDEX_DELETE_INPUT = { "delete", "23r2fc" };
+ public static final String[] OUT_OF_BOUNDS_INDEX_DELETE_INPUT = { "delete", "23" };
+ public static final String[] VALID_INDEX_DELETE_INPUT = { "delete", String.valueOf(VALID_INDEX)};
+
+ public static final String[] NO_INDEX_DONE_INPUT = { "done" };
+ public static final String[] INVALID_INDEX_DONE_INPUT = { "done", "23r2fc" };
+ public static final String[] OUT_OF_BOUNDS_INDEX_DONE_INPUT = { "done", "23" };
+ public static final String[] VALID_INDEX_DONE_INPUT = { "done", String.valueOf(VALID_INDEX)};
+
+ public static final String TEST_NAME = "Test";
+ public static final double TEST_PRICE = 34.5;
+ public static final String TEST_SENTIMENT = "negative";
+ public static final String TEST_REMARK = "";
+ public static final Instrument TEST_STOCK = new Stock(TEST_NAME, TEST_PRICE, TEST_SENTIMENT, TEST_REMARK);
+ public static final int INDEX_OFFSET = 1;
+ public static ArrayList INSTRUMENTS;
+
+ public static final String ABORT = "abort";
+ public static final String ADD_WORKSPACE = "add";
+ public static final String EDIT_WORKSPACE = "edit";
+
+ private InputParser parser;
+
+ @BeforeEach
+ void initialiseTestResources() {
+ parser = new InputParser();
+ INSTRUMENTS = new ArrayList<>();
+ INSTRUMENTS.add(TEST_STOCK);
+ }
+
+ @Test
+ void getIndexNumber_validIndexProvided_expectSuccess() throws InvalidEmptyIndexError, InvalidIndexError {
+ parser.getIndexNumber(VALID_INDEX_DELETE_INPUT);
+ assertEquals(parser.getInstrumentNumber(), VALID_INDEX - INDEX_OFFSET);
+ }
+
+ @Test
+ void getIndexNumber_noIndexProvided_expectException() {
+ assertThrows(InvalidEmptyIndexError.class,
+ () -> parser.getIndexNumber(NO_INDEX_DELETE_INPUT));
+ }
+
+ @Test
+ void getIndexNumber_invalidIndexProvided_expectException() {
+ assertThrows(InvalidIndexError.class,
+ () -> parser.getIndexNumber(INVALID_INDEX_DELETE_INPUT));
+ }
+
+ @Test
+ void getDeleteInstrumentCommand_noIndexProvided_expectException() {
+ assertThrows(InvalidEmptyIndexError.class,
+ () -> parser
+ .getDeleteInstrumentCommand(NO_INDEX_DELETE_INPUT, INSTRUMENTS));
+ }
+
+ @Test
+ void getDeleteInstrumentCommand_invalidIndexProvided_expectException() {
+ assertThrows(InvalidIndexError.class,
+ () -> parser
+ .getDeleteInstrumentCommand(INVALID_INDEX_DELETE_INPUT, INSTRUMENTS));
+ }
+
+ @Test
+ void getDeleteInstrumentCommand_outOfBoundsIndexProvided_expectException() {
+ assertThrows(InvalidBoundsError.class,
+ () -> parser
+ .getDeleteInstrumentCommand(OUT_OF_BOUNDS_INDEX_DELETE_INPUT, INSTRUMENTS));
+ }
+
+ @Test
+ void getDeleteInstrumentCommand_validIndexProvided_expectSuccess()
+ throws InvalidEmptyIndexError, InvalidBoundsError, InvalidIndexError {
+ DeleteCommand command = parser
+ .getDeleteInstrumentCommand(VALID_INDEX_DELETE_INPUT, INSTRUMENTS);
+ assertEquals(command.getIndex(), VALID_INDEX - INDEX_OFFSET);
+ }
+
+ //@@author williamlamjy
+ @Test
+ void getDoneInstrumentCommand_noIndexProvided_expectException() {
+ assertThrows(InvalidEmptyIndexError.class,
+ () -> parser
+ .getDoneInstrumentCommand(NO_INDEX_DONE_INPUT, INSTRUMENTS));
+ }
+
+ @Test
+ void getDoneInstrumentCommand_invalidIndexProvided_expectException() {
+ assertThrows(InvalidIndexError.class,
+ () -> parser
+ .getDoneInstrumentCommand(INVALID_INDEX_DONE_INPUT, INSTRUMENTS));
+ }
+
+ @Test
+ void getDoneInstrumentCommand_outOfBoundsIndexProvided_expectException() {
+ assertThrows(InvalidBoundsError.class,
+ () -> parser
+ .getDoneInstrumentCommand(OUT_OF_BOUNDS_INDEX_DONE_INPUT, INSTRUMENTS));
+ }
+
+ @Test
+ void getDoneInstrumentCommand_validIndexProvided_expectSuccess()
+ throws AlreadyDoneError, InvalidEmptyIndexError, InvalidBoundsError, InvalidIndexError {
+ DoneCommand command = parser
+ .getDoneInstrumentCommand(VALID_INDEX_DONE_INPUT, INSTRUMENTS);
+ assertEquals(command.getIndex(), VALID_INDEX - INDEX_OFFSET);
+ }
+
+ //@@author KVignesh122
+ @Test
+ void abortOperation_validAbortInAdd_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> InputParser.checkIfAbort(ABORT, ADD_WORKSPACE));
+ }
+
+ @Test
+ void abortOperation_validAbortInEdit_expectException() {
+ assertThrows(OperationAbortedError.class,
+ () -> InputParser.checkIfAbort(ABORT, EDIT_WORKSPACE));
+ }
+}
diff --git a/src/test/java/seedu/mtracker/filemanager/ForexDecoderTest.java b/src/test/java/seedu/mtracker/filemanager/ForexDecoderTest.java
new file mode 100644
index 0000000000..00096c2e06
--- /dev/null
+++ b/src/test/java/seedu/mtracker/filemanager/ForexDecoderTest.java
@@ -0,0 +1,140 @@
+package seedu.mtracker.filemanager;
+
+import org.junit.jupiter.api.Test;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyEntryPriceInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyExitPriceInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyExpiryInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEntryPriceSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidExitPriceSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidExpirySavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidRemarkInFileError;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+//@@author williamlamjy
+class ForexDecoderTest extends InstrumentDecoderTest {
+
+ public static final String[] INVALID_FOREX_EXPIRY_TEXT_SEGMENT = {
+ TEST_TYPE,
+ TEST_NAME,
+ TEST_PRICE_STRING,
+ TEST_SENTIMENT,
+ TEST_DONE_STRING,
+ TEST_ENTRY_PRICE_STRING,
+ TEST_EXIT_PRICE_STRING,
+ FUTURE_INVALID_DATE_STRING,
+ TEST_REMARK
+ };
+
+ public static final String[] INVALID_FOREX_EMPTY_EXPIRY_TEXT_SEGMENT = {
+ TEST_TYPE,
+ TEST_NAME,
+ TEST_PRICE_STRING,
+ TEST_SENTIMENT,
+ TEST_DONE_STRING,
+ TEST_ENTRY_PRICE_STRING,
+ TEST_EXIT_PRICE_STRING
+ };
+
+ public static final String[] INVALID_FOREX_ENTRY_PRICE_TEXT_SEGMENT = {
+ TEST_TYPE,
+ TEST_NAME,
+ TEST_PRICE_STRING,
+ TEST_SENTIMENT,
+ TEST_DONE_STRING,
+ TEST_INVALID_ENTRY_PRICE_STRING,
+ TEST_EXIT_PRICE_STRING,
+ FUTURE_DATE_STRING,
+ TEST_REMARK
+ };
+
+ public static final String[] INVALID_FOREX_EMPTY_ENTRY_PRICE_TEXT_SEGMENT = {
+ TEST_TYPE,
+ TEST_NAME,
+ TEST_PRICE_STRING,
+ TEST_SENTIMENT,
+ TEST_DONE_STRING
+ };
+
+ public static final String[] INVALID_FOREX_EXIT_PRICE_TEXT_SEGMENT = {
+ TEST_TYPE,
+ TEST_NAME,
+ TEST_PRICE_STRING,
+ TEST_SENTIMENT,
+ TEST_DONE_STRING,
+ TEST_ENTRY_PRICE_STRING,
+ TEST_INVALID_EXIT_PRICE_STRING,
+ FUTURE_DATE_STRING,
+ TEST_REMARK
+ };
+
+ public static final String[] INVALID_FOREX_EMPTY_EXIT_PRICE_TEXT_SEGMENT = {
+ TEST_TYPE,
+ TEST_NAME,
+ TEST_PRICE_STRING,
+ TEST_SENTIMENT,
+ TEST_DONE_STRING,
+ TEST_ENTRY_PRICE_STRING
+ };
+
+ public static final String[] INVALID_FOREX_REMARK_WRONG_FORMAT_TEXT_SEGMENT = {
+ TEST_TYPE,
+ TEST_NAME,
+ TEST_PRICE_STRING,
+ TEST_SENTIMENT,
+ TEST_DONE_STRING,
+ TEST_ENTRY_PRICE_STRING,
+ TEST_EXIT_PRICE_STRING,
+ FUTURE_DATE_STRING
+ };
+
+
+ @Test
+ void decodeSpecificAttributes_invalidExpiry_expectException() {
+ assertThrows(InvalidExpirySavedInFileError.class,
+ () -> ForexDecoder
+ .validateAndDecodeSpecificAttributes(INVALID_FOREX_EXPIRY_TEXT_SEGMENT));
+ }
+
+ @Test
+ void decodeSpecificAttributes_emptyExpiry_expectException() {
+ assertThrows(InvalidEmptyExpiryInFileError.class,
+ () -> ForexDecoder
+ .validateAndDecodeSpecificAttributes(INVALID_FOREX_EMPTY_EXPIRY_TEXT_SEGMENT));
+ }
+
+ @Test
+ void decodeSpecificAttributes_invalidEntryPrice_expectException() {
+ assertThrows(InvalidEntryPriceSavedInFileError.class,
+ () -> ForexDecoder
+ .validateAndDecodeSpecificAttributes(INVALID_FOREX_ENTRY_PRICE_TEXT_SEGMENT));
+ }
+
+ @Test
+ void decodeSpecificAttributes_emptyEntryPrice_expectException() {
+ assertThrows(InvalidEmptyEntryPriceInFileError.class,
+ () -> ForexDecoder
+ .validateAndDecodeSpecificAttributes(INVALID_FOREX_EMPTY_ENTRY_PRICE_TEXT_SEGMENT));
+ }
+
+ @Test
+ void decodeSpecificAttributes_invalidExitPrice_expectException() {
+ assertThrows(InvalidExitPriceSavedInFileError.class,
+ () -> ForexDecoder
+ .validateAndDecodeSpecificAttributes(INVALID_FOREX_EXIT_PRICE_TEXT_SEGMENT));
+ }
+
+ @Test
+ void decodeSpecificAttributes_emptyExitPrice_expectException() {
+ assertThrows(InvalidEmptyExitPriceInFileError.class,
+ () -> ForexDecoder
+ .validateAndDecodeSpecificAttributes(INVALID_FOREX_EMPTY_EXIT_PRICE_TEXT_SEGMENT));
+ }
+
+ @Test
+ void decodeSpecificAttributes_remarkFormatNotCorrect_expectException() {
+ assertThrows(InvalidRemarkInFileError.class,
+ () -> ForexDecoder
+ .validateAndDecodeSpecificAttributes(INVALID_FOREX_REMARK_WRONG_FORMAT_TEXT_SEGMENT));
+ }
+}
diff --git a/src/test/java/seedu/mtracker/filemanager/InstrumentDecoderTest.java b/src/test/java/seedu/mtracker/filemanager/InstrumentDecoderTest.java
new file mode 100644
index 0000000000..a8d3bd74f4
--- /dev/null
+++ b/src/test/java/seedu/mtracker/filemanager/InstrumentDecoderTest.java
@@ -0,0 +1,191 @@
+package seedu.mtracker.filemanager;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.mtracker.commons.error.fileerror.InvalidCurrPriceSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyCurrPriceInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyNameInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptySentimentInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidEmptyStatusInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidInstrumentInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidNameSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidSentimentSavedInFileError;
+import seedu.mtracker.commons.error.fileerror.InvalidStatusSavedInFileError;
+import seedu.mtracker.model.InstrumentManager;
+
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+//@@author williamlamjy
+class InstrumentDecoderTest {
+
+ public static final String TEST_TYPE = "forex";
+ public static final String TEST_INVALID_TYPE = "nft";
+ public static final String TEST_NAME = "USDSGD";
+ public static final String TEST_INVALID_FOREX_NAME = "Test";
+ public static final String TEST_PRICE_STRING = "34.5";
+ public static final String TEST_INVALID_PRICE_STRING = "0";
+ public static final String TEST_SENTIMENT = "negative";
+ public static final String TEST_INVALID_SENTIMENT = "+";
+ public static final String TEST_INVALID_STATUS = "not valid status";
+ public static final String TEST_DONE_STRING = "false";
+ public static final String TEST_ENTRY_PRICE_STRING = "30.0";
+ public static final String TEST_INVALID_ENTRY_PRICE_STRING = "-1";
+ public static final String TEST_EXIT_PRICE_STRING = "31.0";
+ public static final String TEST_INVALID_EXIT_PRICE_STRING = "-1";
+ public static final int DAYS_DIFFERENCE = 1;
+ public static final LocalDate FUTURE_DATE = LocalDate.now().plusDays(DAYS_DIFFERENCE);
+ public static final String FUTURE_DATE_STRING = FUTURE_DATE.toString();
+ public static final String FUTURE_INVALID_DATE_STRING = "2 November";
+ public static final String TEST_REMARK = "";
+
+ public static final String[] INVALID_INSTRUMENT_TYPE_TEXT_SEGMENT = {
+ TEST_INVALID_TYPE,
+ TEST_NAME,
+ TEST_PRICE_STRING,
+ TEST_SENTIMENT,
+ TEST_DONE_STRING,
+ TEST_ENTRY_PRICE_STRING,
+ TEST_EXIT_PRICE_STRING,
+ FUTURE_DATE_STRING,
+ TEST_REMARK
+ };
+
+ public static final String[] INVALID_FOREX_NAME_TEXT_SEGMENT = {
+ TEST_TYPE,
+ TEST_INVALID_FOREX_NAME,
+ TEST_PRICE_STRING,
+ TEST_SENTIMENT,
+ TEST_DONE_STRING,
+ TEST_ENTRY_PRICE_STRING,
+ TEST_EXIT_PRICE_STRING,
+ FUTURE_DATE_STRING,
+ TEST_REMARK
+ };
+
+ public static final String[] INVALID_FOREX_PRICE_TEXT_SEGMENT = {
+ TEST_TYPE,
+ TEST_NAME,
+ TEST_INVALID_PRICE_STRING,
+ TEST_SENTIMENT,
+ TEST_DONE_STRING,
+ TEST_ENTRY_PRICE_STRING,
+ TEST_EXIT_PRICE_STRING,
+ FUTURE_DATE_STRING,
+ TEST_REMARK
+ };
+
+ public static final String[] INVALID_FOREX_SENTIMENT_TEXT_SEGMENT = {
+ TEST_TYPE,
+ TEST_NAME,
+ TEST_PRICE_STRING,
+ TEST_INVALID_SENTIMENT,
+ TEST_DONE_STRING,
+ TEST_ENTRY_PRICE_STRING,
+ TEST_EXIT_PRICE_STRING,
+ FUTURE_DATE_STRING,
+ TEST_REMARK
+ };
+
+ public static final String[] INVALID_EMPTY_NAME_TEXT_SEGMENT = {
+ TEST_TYPE
+ };
+
+ public static final String[] INVALID_EMPTY_CURRENT_PRICE_TEXT_SEGMENT = {
+ TEST_TYPE,
+ TEST_NAME
+ };
+
+ public static final String[] INVALID_EMPTY_SENTIMENT_TEXT_SEGMENT = {
+ TEST_TYPE,
+ TEST_NAME,
+ TEST_PRICE_STRING
+ };
+
+ public static final String[] INVALID_EMPTY_STATUS_TEXT_SEGMENT = {
+ TEST_TYPE,
+ TEST_NAME,
+ TEST_PRICE_STRING,
+ TEST_SENTIMENT
+ };
+
+ public static final String[] INVALID_STATUS_TEXT_SEGMENT = {
+ TEST_TYPE,
+ TEST_NAME,
+ TEST_PRICE_STRING,
+ TEST_SENTIMENT,
+ TEST_INVALID_STATUS
+ };
+
+ private InstrumentManager instrumentManager;
+
+ @BeforeEach
+ void initialiseTestResources() {
+ instrumentManager = InstrumentManager.getInstance();
+ }
+
+ @Test
+ void decodeGeneralAttributes_invalidName_expectException() {
+ assertThrows(InvalidNameSavedInFileError.class,
+ () -> InstrumentDecoder
+ .validateAndDecodeGeneralAttributes(INVALID_FOREX_NAME_TEXT_SEGMENT));
+ }
+
+ @Test
+ void decodeGeneralAttributes_invalidPrice_expectException() {
+ assertThrows(InvalidCurrPriceSavedInFileError.class,
+ () -> InstrumentDecoder
+ .validateAndDecodeGeneralAttributes(INVALID_FOREX_PRICE_TEXT_SEGMENT));
+ }
+
+ @Test
+ void decodeGeneralAttributes_invalidSentiment_expectException() {
+ assertThrows(InvalidSentimentSavedInFileError.class,
+ () -> InstrumentDecoder
+ .validateAndDecodeGeneralAttributes(INVALID_FOREX_SENTIMENT_TEXT_SEGMENT));
+ }
+
+ @Test
+ void addSavedInstrumentToList_invalidType_expectException() {
+ assertThrows(InvalidInstrumentInFileError.class,
+ () -> InstrumentDecoder
+ .addSavedInstrumentToList(instrumentManager, INVALID_INSTRUMENT_TYPE_TEXT_SEGMENT));
+ }
+
+ //@@author theodorekwok
+ @Test
+ void addSavedInstrumentToList_emptyName_expectException() {
+ assertThrows(InvalidEmptyNameInFileError.class,
+ () -> InstrumentDecoder
+ .addSavedInstrumentToList(instrumentManager, INVALID_EMPTY_NAME_TEXT_SEGMENT));
+ }
+
+ @Test
+ void addSavedInstrumentToList_emptyPrice_expectException() {
+ assertThrows(InvalidEmptyCurrPriceInFileError.class,
+ () -> InstrumentDecoder
+ .addSavedInstrumentToList(instrumentManager, INVALID_EMPTY_CURRENT_PRICE_TEXT_SEGMENT));
+ }
+
+ @Test
+ void addSavedInstrumentToList_emptySentiment_expectException() {
+ assertThrows(InvalidEmptySentimentInFileError.class,
+ () -> InstrumentDecoder
+ .addSavedInstrumentToList(instrumentManager, INVALID_EMPTY_SENTIMENT_TEXT_SEGMENT));
+ }
+
+ @Test
+ void addSavedInstrumentToList_emptyStatus_expectException() {
+ assertThrows(InvalidEmptyStatusInFileError.class,
+ () -> InstrumentDecoder
+ .addSavedInstrumentToList(instrumentManager, INVALID_EMPTY_STATUS_TEXT_SEGMENT));
+ }
+
+ @Test
+ void addSavedInstrumentToList_invalidStatus_expectException() {
+ assertThrows(InvalidStatusSavedInFileError.class,
+ () -> InstrumentDecoder
+ .addSavedInstrumentToList(instrumentManager, INVALID_STATUS_TEXT_SEGMENT));
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 892cb6cae7..ef7e467030 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,9 +1,24 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
+Unable to find a saved file. Creating a new one now...
+________________________________________________________________________________
+ _________ __
+ | _ _ | [ | _
+ _ .--..--.|_/ | | \_| .--. ,--. .---. | | / ] .---. _ .--.
+[ `.-. .-. | | | [ `/'`\]`'_\ : / /'`\] | '' < / /__\\[ `/'`\]
+ | | | | | | _| |_ | | /| | |,| \__. | |`\ \| \__., | |
+[___||__||__]|_____|[___] \'-;__/'.___.'[__| \_]'.__.'[___]
-What is your name?
-Hello James Gosling
+Hello! I am mTracker, your personal assistant bot that
+helps you keep track of the markets.
+What should I do for you now?
+________________________________________________________________________________
+mTracker$main> Oops, I do not understand you...
+mTracker$main> ______ _______ _
+( ___ \ |\ /|( ____ \( )
+| ( ) )( \ / )| ( \/| |
+| (__/ / \ (_) / | (__ | |
+| __ ( \ / | __) | |
+| ( \ \ ) ( | ( (_)
+| )___) ) | | | (____/| _
+|/ \___/ \_/ (_______/(_)
+Thank you for using mTracker.
+MAY THE MARKETS BE WITH YOU!!!
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index f6ec2e9f95..bd150983c1 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -1 +1,2 @@
-James Gosling
\ No newline at end of file
+foo
+bye
\ No newline at end of file