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 --------|:----:|:--------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | 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} +![](images/mTracker_logo_cropped.png) + +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 +![foo](images/mTracker_logo_cropped.png) ## 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