diff --git a/README.md b/README.md index 8715d4d91..07ce6d819 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,38 @@ Prerequisites: JDK 11, update Intellij to the most recent version. 1. If there are any further prompts, accept the defaults. 1. Configure the project to use **JDK 11** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk).
In the same dialog, set the **Project language level** field to the `SDK default` option. -3. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: - ``` - Hello from - ____ _ - | _ \ _ _| | _____ - | | | | | | | |/ / _ \ - | |_| | |_| | < __/ - |____/ \__,_|_|\_\___| - ``` + 3. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: + + ``` + Kowalski + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣠⣤⣴⠶⠶⣦⣤⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⡾⠟⠋⠉⠀⠀⠀⠀⠀⠀⠈⠉⠛⠿⣶⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⢿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣧⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⠋⠀⠀⠀⠀⠀⠀⠀⢀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣷⡀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⢸⡏⠀⠀⠀⠀⠀⣠⣾⠟⠛⠛⠛⠻⢶⣄⠀⠀⠀⠀⠀⠀⣴⡾⠛⠛⢿⣧⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⣾⠁⠀⠀⠀⢀⣾⠏⠀⠀⠀⠀⠀⠀⠀⠙⣷⡀⠀⠀⠀⢸⡟⠀⠀⠀⠈⣿⡆⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⠀⣼⠏⠀⠀⠀⠀⠀⣠⣤⡀⠀⠘⣷⣤⣶⢶⣿⣇⠀⣖⣶⡆⠘⣿⡀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⢠⣿⠀⠀⠀⠀⠀⠸⣧⣿⣿⠀⣼⠏⠁⠀⠀⠀⠹⣧⠙⠛⠁⠀⣿⣧⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⠈⣿⠀⠀⠀⠀⠀⠀⠈⠉⠀⠀⢻⣿⣶⠶⣶⣶⣿⠏⠀⠀⠀⣰⣿⡏⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⢰⡟⠀⠀⠀⠀⠹⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠛⠋⠉⠀⠀⠀⣠⣾⠋⢹⡆⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⢀⣿⠃⠀⠀⠀⠀⠀⠙⢷⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠻⢷⣄⢻⣦⠀⠀⠀ + ⠀⠀⠀⠀⠀⢠⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣻⣶⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣷⡙⢷⣄⠀ + ⠀⠀⠀⠀⢠⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣴⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⠃⠈⢻⣆ + ⠀⠀⠀⢠⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠻⣦⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⠏⠀⠀⠀⣿ + ⠀⠀⠀⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡟⠀⠀⠀⢠⣿ + ⠀⠀⢰⡟⠀⠀⠀⠀⠀⠀⠠⣤⣄⠀⠀⠀⠀⠀⠀⠀⠀⠹⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⡇⠀⠀⠀⣾⠃ + ⠀⠀⢸⣷⠀⠀⠀⠀⠀⠀⠀⠈⠙⢿⣦⣀⠀⠀⠀⠀⠀⠀⢹⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⡇⠀⣠⣾⠏⠀ + ⢀⣴⡿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⢠⡿⠻⢷⣄⡀⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠿⠿⣿⠃⠀⠀ + ⣿⡏⠀⢹⣧⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠙⠿⣦⣤⣤⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⢿⣦⠀⠀ + ⠙⢿⣦⣀⠻⣧⡀⠀⠀⠀⠀⠀⢠⣼⣷⣄⠀⣀⣀⡀⠈⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⠏⢰⡿⢶⡆ + ⠀⠀⠉⠙⠛⠿⠿⣦⣀⢠⣶⣶⡿⠁⠀⠹⣿⠋⠉⢿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⡿⠃⠀⠀⣠⡾⠃ + ⠀⠀⠀⠀⠀⠀⠀⠈⠻⢿⣿⡦⠀⠀⠀⠀⠀⠀⠀⢸⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣿⣋⣀⣤⣴⠟⠋⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣦⣀⠀⠀⠀⠀⣰⡿⠁⠀⠀⠀⠀⠀⢀⣀⣠⣴⠿⠛⠉⠙⠛⠉⠉⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⠻⠶⠶⠟⠛⠛⠛⠛⠛⠛⠛⠛⠋⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠘⠦⣠⠀⠀⠠⠀⡀⠀⠀⣘⢦⠀⢤⡄⠀⠈⠡⡄⡀⠀⡀⣄⠈⠀⠀⠐⠀⠀⠀⠈⠀⠀⠀⠀ + ⠀⠀⠀⠀⠐⠶⠠⠄⠠⠀⠀⠤⠀⠤⠄⠀⡰⡉⠈⠱⠘⠀⡄⣀⠀⠀⠀⠠⠀⠭⠰⠶⠤⠆⠰⡶⠘⢤⠄⠀⢀⠀ + KOWALSKI ANALYSIS! + ``` \ No newline at end of file diff --git a/data/Kowalski.txt b/data/Kowalski.txt new file mode 100644 index 000000000..59b16c06a --- /dev/null +++ b/data/Kowalski.txt @@ -0,0 +1,3 @@ +T | X | Code for CS1010 Assignment +D | 0 | Write up your analysis report for Skipper | Friday 2359 +E | 0 | Attend Snowball Olympics | Tomorrow 9am - Friday 6pm diff --git a/docs/README.md b/docs/README.md index 8077118eb..49e528eff 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,271 @@ -# User Guide +# Kowalski Bot +If you've watched **[Penguins of Madagascar](https://youtu.be/Z-5SrndEVrI?si=_ivcZTA_PTUoJhI8)**, you'll know **_[Kowalski](https://youtu.be/f3CVuo_HFLM?si=v1hv_y0UU4YP8HYb)_**. -## Features +He is often referred to as Skipper's Lieutenant, the second-in-command. -### Feature-ABC +If you're someone who needs a Kowalski by your side at all times, this bot is for you! -Description of the feature. -### Feature-XYZ +## Overview -Description of the feature. +Kowalski is a bot for you to keep track of your everyday tasks. +Be it keeping track of your homework assignments, +your daily chores, Kowalski's got your back! + +Kowalski has got 3 types of tasks - ToDo task, Deadline task, and Event task. + +### Task types +- ToDo: Task with ```Name``` +- Deadline: Task with ```Name``` and ```Deadline``` +- Event: Task with ```Name```, ```From``` and ```To``` + + +### Kowalski.txt + +Kowalski is able to save the tasks which you add by storing them in a text file called **Kowalski.txt**. + +If you are using this bot for the **_first time_**, this bot will +automatically create a directory called **data** which will then store your **Kowalski.txt** inside of it. + +E.g. ```C:\Users\Skipper\Desktop\Kowalski_Bot\data\Kowalski.txt``` +assuming you are storing the jar file in ```C:\Users\Skipper\Desktop\Kowalski_Bot``` + +## Running Kowalski Bot +#### Prerequisites: +- Java _(version >= 11)_ installed +- Having the latest JAR file release of Kowalski +- ~~[Kowalski's energy](https://youtu.be/xv9ek6RP4r8?si=AhwSaukCiyUihn-X)~~ + +#### Steps: +1. Open terminal and change directory to the directory in which you've stored the JAR file. +2. Run `java -jar ./Kowalski.jar` + +To exit the application, type the keyword `bye`. +To restart the application, repeat steps 1 & 2. + +## Features + +### 1. List all tasks ```list``` + +Lists all tasks which the user has input. + +### 2. Find matching tasks ```find``` + +Finds and filters tasks containing a keyword, and prints them out in a list. + +### 3. Remove a task ```delete``` + +Removes a task from the list of tasks. + +### 4. Mark a task ```mark``` + +Marks a task as complete. + +### 5. Unmark a task ```unmark``` + +Marks a task as **_incomplete_**. + +### 5. Add a ToDo task ```todo``` + +Adds in a task for the user, containing only the name. + +### 6. Add a Deadline task ```deadline``` + +Adds in a deadline for the user, containing a name and deadline of the task. + +### 7. Add an Event task ```event``` + +Adds in an event for the user, containing a name, start date/time and end date/time. ## Usage -### `Keyword` - Describe action +### Adding a task + +The format for adding specific tasks are as follows. The command keyword is not case sensitive, but the /*vars* are. + + +**Formats**: +- Todo: ```todo NAME``` +- Deadline: ```deadline NAME /by STRING``` +- Event: ```event NAME /from STRING /to STRING``` + + +***Example usage***: + +- ```todo Code for CS1010 Assignment``` +- ```deadline Write up your analysis report for Skipper /by Friday 2359``` +- ```event Attend Snowball Olympics /from Tomorrow 9am /to Friday 6pm``` + + +***Expected outcome***: + +If the arguments were entered correctly, the task should be added successfully. + +***Example output***: + +_For Todo Tasks:_ +``` +Skipper you've got this work to do: + [T][ ] Code for CS1010 Assignment +Now you have 1 task in the list. +____________________________________________________________ +Kowalski analysing inputs... +Change recorded in the Text file! +____________________________________________________________ +``` +_For Deadline Tasks:_ +``` +Skipper, I have recorded this deadline: + [D][ ] Write up your analysis report for Skipper (by: Friday 2359) +Now you have 2 tasks in the list. +____________________________________________________________ +Kowalski analysing inputs... +Change recorded in the Text file! +____________________________________________________________ +``` +_For Event Tasks:_ +``` +Skipper I've noted this event in my calendar: + [E][ ] Attend Snowball Olympics (from: Tomorrow 9am to: Friday 6pm) +Now you have 3 tasks in the list. +____________________________________________________________ +Kowalski analysing inputs... +Change recorded in the Text file! +____________________________________________________________ +``` -Describe the action and its outcome. -Example of usage: +### ```mark``` / ```unmark``` : Mark/Unmark tasks -`keyword (optional arguments)` +**Format**: ```mark/unmark NUMBER``` -Expected outcome: +Firstly, the task number must be retrieved. This can be done easily using the command ```list```. -Description of the outcome. +Next, use ```mark x``` or ```unmark x``` to set the state of the task to the desired state. +***Example usage***: + +- ```mark 1``` +- ```unmark 3``` + +***Expected outcome***: + +If a valid number is entered, a response marking the task will be shown. + +***Example output***: + +_After marking task:_ +``` +Way to go Skipper! I've marked this task as done: + [T][X] Code for CS1010 Assignment +____________________________________________________________ +Kowalski analysing inputs... +Change recorded in the Text file! +____________________________________________________________ ``` -expected output +_After unmarking task:_ + ``` +C'mon Skipper, you're much better than that! I've marked this task as undone: + [E][ ] Attend Snowball Olympics (from: Tomorrow 9am to: Friday 6pm) +____________________________________________________________ +Kowalski analysing inputs... +Change recorded in the Text file! +____________________________________________________________ +``` + + +### List: ```list```: Listing all tasks + +**Format**: ```list``` + +By typing list, the user can print out all existing tasks into the command line. + +***Example usage***: + +- ```list``` + +*Expected outcome*: + +All tasks will be printed out in the console. + +*Example output*: + +``` +1.[T][X] Code for CS1010 Assignment +2.[D][ ] Write up your analysis report for Skipper (by: Friday 2359) +3.[E][ ] Attend Snowball Olympics (from: Tomorrow 9am to: Friday 6pm) +____________________________________________________________ +``` + + +### Find: ```find``` : Finding keywords + +**Format**: ```find STRING``` + +The command looks through all existing tasks, and prints them out if their name contains the string query. + +*Example usage*: + +- ```find lecture``` +- ```find homework``` + +*Expected outcome*: + +All tasks containing the keywords in their name will be printed out. + +*Example output*: + +__Input:__``` find report``` + +``` +Skipper here are the matching tasks: +1.[D][ ] Write up your analysis report for Skipper (by: Friday 2359) +____________________________________________________________ +``` + +### Delete: ```delete``` : Deleting a task + +**Format**: ```delete NUMBER``` + +As with *mark/unmark*, the number of the task to be deleted must be obtained. + +Next, typing the command in the given format will delete said task from the list. + +Once deleted, said task **CANNOT** be retrieved. + +***Example usage***: + +- ```delete 4``` + +***Expected outcome***: + +Task of the given number (in the list) will be deleted. + +***Example output***: + +``` +Damn Skipper, you've got some courage removing this task: + [T][ ] Admire favourite snack +Now you have 3 tasks in the list. +____________________________________________________________ +Kowalski analysing inputs... +Change recorded in the Text file! +____________________________________________________________ +``` + +### Bye: ```bye``` : Exit the program + +**Format**: ```bye``` + +***Expected outcome***: + +Exits the program and updates the stored tasks in **_Kowalski.txt_** file. + +***Expected output***: +``` +Bye Skipper! Hope to serve you again for your next mission! +____________________________________________________________ +``` + + diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334c..000000000 --- a/src/main/java/Duke.java +++ /dev/null @@ -1,10 +0,0 @@ -public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - } -} diff --git a/src/main/java/Kowalski/Kowalski.java b/src/main/java/Kowalski/Kowalski.java new file mode 100644 index 000000000..2ebe8d342 --- /dev/null +++ b/src/main/java/Kowalski/Kowalski.java @@ -0,0 +1,30 @@ +package Kowalski; + +import Kowalski.UI.Ui; +import Kowalski.commands.Parser; +import Kowalski.commands.Storage; +import Kowalski.commands.TaskList; + +import java.io.IOException; +import java.util.Scanner; + +/** + * Kowalski is a bot for users to keep track of your everyday tasks. + * It is able to store and retrieve tasks which you need to do, tasks with deadlines and events important to users. + * It allows you to easily add, remove tasks, or even mark and unmark tasks. + * It also allows users to find previous tasks based on their inputs. + */ +public class Kowalski { + public static Scanner in = new Scanner (System.in); + + public static void main(String[] args) throws IOException { + Ui.printIntro(); + Storage.readTextFile(TaskList.currentTask); + String userCommand = TaskList.processInput(in.next()); + while (!(userCommand.equals("bye"))){ + Parser.parseUserCommand(TaskList.processInput(userCommand), TaskList.currentTask, in); + userCommand = in.next(); + } + Ui.printEndConversation(); + } +} \ No newline at end of file diff --git a/src/main/java/Kowalski/UI/Ui.java b/src/main/java/Kowalski/UI/Ui.java new file mode 100644 index 000000000..6092acfc5 --- /dev/null +++ b/src/main/java/Kowalski/UI/Ui.java @@ -0,0 +1,81 @@ +package Kowalski.UI; + +public class Ui { + private static final String DIVIDING_LINE = "____________________________________________________________"; + private static final String KOWALSKI_INTRODUCTION = "Welcome Skipper! I'm Kowalski, reporting for Duty!"; + private static final String KOWALSKI_STARTING_QN = "What can I do for you today?"; + private static final String KOWALSKI_ECHO = "Enter commands, and I will echo them back to you, " + + "as well as add them to your list."; + private static final String KOWALSKI_LIST_COMMAND = "Type 'list' to see your to-do list."; + private static final String KOWALSKI_MARK_COMMAND = "Type 'mark' to mark a task as done."; + private static final String KOWALSKI_UNMARK_COMMAND = "Type 'unmark' to mark a task as not done."; + private static final String KOWALSKI_TODO_TASK_COMMAND = "Type 'todo ' to add a task to the list."; + private static final String KOWALSKI_DEADLINE_TASK_COMMAND = "Type 'deadline /by ' to add a task " + + "with a deadline to the list."; + private static final String KOWALSKI_EVENT_TASK_COMMAND = "Type 'event /from /to ' to add an " + + "event to the list."; + private static final String KOWALSKI_DELETE_TASK_COMMAND = "Type 'delete ' to delete a task from your list."; + private static final String KOWALSKI_BYE_COMMAND = "Type 'bye' to end the conversation."; + private static final String ZERO_TASK_MESSAGE = "Now you have 0 tasks in the list."; + private static final String ONE_TASK_MESSAGE = "Now you have 1 task in the list."; + private static final String KOWALSKI_BYE_MESSAGE = "Bye Skipper! Hope to serve you again for your next mission!"; + private static final String NOW_YOU_HAVE = "Now you have "; + private static final String TASKS_IN_THE_LIST = " tasks in the list."; + /** + * Prints out the message introducing the functionalities of Kowalski Bot + */ + public static void printIntro(){ + System.out.println(DIVIDING_LINE); + System.out.println(KOWALSKI_INTRODUCTION + System.lineSeparator()); + printHelpCommands(); + System.out.println(System.lineSeparator() + KOWALSKI_STARTING_QN ); + printDivider(); + } + + /** + * Prints out a list of commands to guide the user to accurately use the Kowalski Bot + */ + public static void printHelpCommands(){ + System.out.println(KOWALSKI_ECHO); + System.out.println(KOWALSKI_LIST_COMMAND); + System.out.println(KOWALSKI_MARK_COMMAND); + System.out.println(KOWALSKI_UNMARK_COMMAND); + System.out.println(KOWALSKI_TODO_TASK_COMMAND); + System.out.println(KOWALSKI_DEADLINE_TASK_COMMAND); + System.out.println(KOWALSKI_EVENT_TASK_COMMAND); + System.out.println(KOWALSKI_DELETE_TASK_COMMAND); + System.out.println(KOWALSKI_BYE_COMMAND); + } + + /** + * Prints a dividing line between statements for added clarity + */ + public static void printDivider(){ + System.out.println(DIVIDING_LINE); + } + + /** + * Prints out an accurate message for the number of tasks in the list. + * @param numberOfTasks : represents the total current task count + */ + public static void printCurrentTaskMessage(int numberOfTasks){ + switch (numberOfTasks){ + case 0: + System.out.println(ZERO_TASK_MESSAGE); + break; + case 1: + System.out.println(ONE_TASK_MESSAGE); + break; + default: + System.out.println(NOW_YOU_HAVE + numberOfTasks + TASKS_IN_THE_LIST); + } + } + + /** + * Prints out the message to end conversation with the user + */ + public static void printEndConversation(){ + System.out.println(KOWALSKI_BYE_MESSAGE); + printDivider(); + } +} diff --git a/src/main/java/Kowalski/commands/KowalskiException.java b/src/main/java/Kowalski/commands/KowalskiException.java new file mode 100644 index 000000000..17ee5f1f5 --- /dev/null +++ b/src/main/java/Kowalski/commands/KowalskiException.java @@ -0,0 +1,4 @@ +package Kowalski.commands; + +public class KowalskiException extends Exception{ +} diff --git a/src/main/java/Kowalski/commands/Parser.java b/src/main/java/Kowalski/commands/Parser.java new file mode 100644 index 000000000..b9deca213 --- /dev/null +++ b/src/main/java/Kowalski/commands/Parser.java @@ -0,0 +1,252 @@ +package Kowalski.commands; + +import Kowalski.UI.Ui; +import Kowalski.tasks.Deadline; +import Kowalski.tasks.Event; +import Kowalski.tasks.Task; +import Kowalski.tasks.Todo; + +import java.util.InputMismatchException; +import java.util.List; +import java.util.Scanner; + +public class Parser { + private static final String BYE = "bye"; + private static final String LIST = "list"; + private static final String FIND = "find"; + private static final String DELETE = "delete"; + private static final String UNMARK = "unmark"; + private static final String MARK = "mark"; + private static final String TODO = "todo"; + private static final String DEADLINE = "deadline"; + private static final String EVENT = "event"; + private static final String BY = "/by"; + private static final String FROM = "/from"; + private static final String TO = "/to"; + private static final String INPUT_MISMATCH_EXCEPTION_MESSAGE = "Skipper input a god damn number!"; + private static final String INDEX_OUT_OF_BOUNDS_EXCEPTION_MESSAGE = "Invalid Task Number! Skipper stop acting like Private!"; + private static final String KOWALSKI_EXCEPTION_MESSAGE = "Skipper your inputs are wrong! Try again!"; + private static final String DEFAULT_ERROR_MESSAGE = "Skipper pull up your socks!"; + private static final String TASK_DELETE_MESSAGE = "Damn Skipper, you've got some courage removing this task:"; + private static final String MARK_AS_DONE_MESSAGE = "Way to go Skipper! I've marked this task as done:"; + private static final String MARK_AS_NOT_DONE_MESSAGE = "C'mon Skipper, you're much better than that! I've marked this task as undone:"; + private static final String TODO_MESSAGE = "Skipper you've got this work to do:"; + private static final String DEADLINE_MESSAGE = "Skipper, I have recorded this deadline:"; + private static final String EVENT_MESSAGE = "Skipper I've noted this event in my calendar:"; + private static final String TWO_SPACE_GAP = " "; + + /** + * Used to check if the user has accurately input the deadline by stating the "/by" + * @param deadlineDetails : Contains the details of the deadline task + * @throws KowalskiException In the event that the input has no "/by" + */ + public static void checkDeadlineInput(String deadlineDetails) throws KowalskiException{ + if (!(deadlineDetails.contains(BY))) { + throw new KowalskiException(); + } + } + + /** + * Used to check if the user has accurately input the deadline by stating the "/from" and "/to" + * @param eventDetails : Contains the details of the event task + * @throws KowalskiException In the event that the input has no "/from" or "/to" or both + */ + public static void checkEventInput(String eventDetails) throws KowalskiException{ + if (!((eventDetails.contains(FROM)) && (eventDetails.contains(TO)))) { + throw new KowalskiException(); + } + } + + /** + * Cleans up the user input and forms new Deadline Task + * @param deadlineDetails : User input for details of the deadline task + * @return new deadline task created + */ + private static Task getNewDeadlineTask(String deadlineDetails) { + String[] deadlineArray = deadlineDetails.split(BY); + for (int i = 0; i < deadlineArray.length; i++) { + deadlineArray[i] = deadlineArray[i].trim(); + } + return new Deadline(deadlineArray[0], deadlineArray[1]); + } + + /** + * Cleans up the user input and forms new event Task + * @param eventDetails : User input for details of the event task + * @return new event task created + */ + private static Task getNewEventTask(String eventDetails) { + String[] eventArray = eventDetails.split(FROM); + String eventInformation = eventArray[0].trim(); + String [] fromAndTo = eventArray[1].split(TO); + String eventFrom = fromAndTo[0].trim(); + String eventTo = fromAndTo[1].trim(); + + return new Event(eventInformation, eventFrom, eventTo); + } + + /** + * Parses user command and initiates actions according to the first word input by the user. + * Able to initiate actions for commands - bye, list, find, delete, unmark, mark, todo, deadline, event + * @param UserCommand first word input by the user + * @param currentTask is an ArrayList storing the current tasks + * @param in is of scanner class to enable usage of the scanning library's functions + */ + public static void parseUserCommand(String UserCommand, List currentTask, Scanner in) { + + int taskNumber; + int lastTaskIndex; + + switch (UserCommand){ + case BYE: + break; + + case LIST: + TaskList.printCurrentTaskItems(); + Ui.printDivider(); + break; + + case FIND: + String wordToMatch = in.nextLine(); + if (!wordToMatch.isEmpty()) { + TaskList.findMatch(wordToMatch.trim().toLowerCase()); + } else { + System.out.println(KOWALSKI_EXCEPTION_MESSAGE); + } + Ui.printDivider(); + break; + + case DELETE: + try { + taskNumber = in.nextInt(); + } catch (InputMismatchException e) { + System.out.println(INPUT_MISMATCH_EXCEPTION_MESSAGE); + break; + } + + try { + System.out.println(TASK_DELETE_MESSAGE); + System.out.println(TWO_SPACE_GAP + currentTask.get(taskNumber - 1)); + Ui.printCurrentTaskMessage(currentTask.size()-1); + TaskList.removeTask(taskNumber-1); + Storage.writeText(currentTask); + } catch (IndexOutOfBoundsException e){ + System.out.println(INDEX_OUT_OF_BOUNDS_EXCEPTION_MESSAGE); + } + Ui.printDivider(); + break; + + case UNMARK: + try { + taskNumber = in.nextInt(); + } catch (InputMismatchException e) { + System.out.println(INPUT_MISMATCH_EXCEPTION_MESSAGE); + Ui.printDivider(); + break; + } + + try{ + currentTask.get(taskNumber - 1).markAsNotDone(); + System.out.println(MARK_AS_NOT_DONE_MESSAGE); + System.out.println(TWO_SPACE_GAP + currentTask.get(taskNumber - 1)); + Storage.writeText(currentTask); + } catch (IndexOutOfBoundsException e) { + System.out.println(INDEX_OUT_OF_BOUNDS_EXCEPTION_MESSAGE); + Ui.printDivider(); + break; + } + Ui.printDivider(); + break; + + case MARK: + try { + taskNumber = in.nextInt(); + } catch (InputMismatchException e) { + System.out.println(INPUT_MISMATCH_EXCEPTION_MESSAGE); + Ui.printDivider(); + break; + } + + try{ + currentTask.get(taskNumber - 1).markAsDone(); + System.out.println(MARK_AS_DONE_MESSAGE); + System.out.println(TWO_SPACE_GAP + currentTask.get(taskNumber - 1)); + Storage.writeText(currentTask); + } catch (IndexOutOfBoundsException e) { + System.out.println(INDEX_OUT_OF_BOUNDS_EXCEPTION_MESSAGE); + Ui.printDivider(); + break; + } + Ui.printDivider(); + break; + + case TODO: + String toDoDetails = in.nextLine(); + + Task newToDoTask = new Todo(toDoDetails.trim()); + TaskList.addTask(newToDoTask); + lastTaskIndex = currentTask.size() - 1; + + System.out.println(TODO_MESSAGE); + System.out.println(TWO_SPACE_GAP + currentTask.get(lastTaskIndex)); + Ui.printCurrentTaskMessage(currentTask.size()); + Storage.writeText(currentTask); + Ui.printDivider(); + break; + + case DEADLINE: + String deadlineDetails = in.nextLine(); + + try{ + checkDeadlineInput(deadlineDetails); + + //Adding the new deadline task into currentTask List after processing and cleaning inputs + Task newDeadlineTask = getNewDeadlineTask(deadlineDetails); + TaskList.addTask(newDeadlineTask); + lastTaskIndex = currentTask.size() - 1; + + //Printing the appropriate information for the User + System.out.println(DEADLINE_MESSAGE); + System.out.println(TWO_SPACE_GAP + currentTask.get( lastTaskIndex)); + Ui.printCurrentTaskMessage(currentTask.size()); + Storage.writeText(currentTask); + Ui.printDivider(); + } catch (KowalskiException e){ + System.out.println(KOWALSKI_EXCEPTION_MESSAGE); + Ui.printDivider(); + } + break; + + case EVENT: + String eventDetails = in.nextLine(); + + try{ + checkEventInput(eventDetails); + + //Adding the new event task into currentTask List after processing and cleaning inputs + Task newEventTask = getNewEventTask(eventDetails); + TaskList.addTask(newEventTask); + lastTaskIndex = currentTask.size() - 1; + + //Printing the appropriate information for the User + System.out.println(EVENT_MESSAGE); + System.out.println(TWO_SPACE_GAP + currentTask.get(lastTaskIndex)); + Ui.printCurrentTaskMessage(currentTask.size()); + Storage.writeText(currentTask); + Ui.printDivider(); + } catch (KowalskiException e) { + System.out.println(KOWALSKI_EXCEPTION_MESSAGE); + Ui.printDivider(); + } + break; + + + default: + System.out.println(DEFAULT_ERROR_MESSAGE+ System.lineSeparator()); + Ui.printHelpCommands(); + Ui.printDivider(); + break; + } + } + +} diff --git a/src/main/java/Kowalski/commands/Storage.java b/src/main/java/Kowalski/commands/Storage.java new file mode 100644 index 000000000..af6ff85b0 --- /dev/null +++ b/src/main/java/Kowalski/commands/Storage.java @@ -0,0 +1,161 @@ +package Kowalski.commands; + +import Kowalski.UI.Ui; +import Kowalski.tasks.Deadline; +import Kowalski.tasks.Event; +import Kowalski.tasks.Task; +import Kowalski.tasks.Todo; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +public class Storage { + private static final String TEXT_FILE_FOLDER = "data"; + private static final String FULL_FILE_PATH = "data/Kowalski.txt"; + private static final String TODO = "T"; + private static final String DEADLINE = "D"; + private static final String EVENT = "E"; + private static final String COMPLETED_TASK_CROSS = "X"; + private static final String FROM_AND_TO_SEPARATOR = " - "; + private static final String TEXT_FILE_CORRUPTED_ERROR = "Kowalski Analysis Error: Text File Corrupted!"; + private static final String CREATE_NEW_TEXT_FILE = "Creating new Kowalski.txt file"; + private static final String RETRIEVING_PREVIOUS_DATA_MESSAGE = "Kowalski retrieving previous data..."; + private static final String DATA_RETRIEVAL_SUCCESS_MESSAGE = "Kowalski Data Retrieval Success!"; + private static final String DATA_RETRIEVAL_FAIL_MESSAGE = "Kowalski Data Retrieval Failed!"; + private static final String ANALYSING_INPUT_MESSAGE = "Kowalski analysing inputs..." ; + private static final String CHANGE_RECORDED_IN_TEXT_FILE = "Change recorded in the Text file!"; + private static final String ISSUE_WITH_DIRECTORY_OR_TEXT_FILE = "Kowalski Analysis failed - Issue with directory/text file!"; + private static final String FAILURE_TO_CREATE_DIRECTORY = "Skipper, I am unable to create the data directory for you!" ; + private static final String NEWLINE = "\n" ; + + /** + * This function takes in the lines of code from the Kowalski.txt and processes them to + * list out all the previous tasks which we had saved. + * @param fileInput: String containing each line of input from Kowalski.txt + * @param currentTask ArrayList containing the current tasks + */ + public static void restoreTaskList(String fileInput, List currentTask){ + String [] inputArray = fileInput.split("\\s*\\|\\s*"); + switch (inputArray[0].trim()){ + case TODO: + Task newToDoTask = new Todo(inputArray[2].trim()); + if (inputArray[1].trim().equals(COMPLETED_TASK_CROSS)) { + newToDoTask.markAsDone(); + } else { + newToDoTask.markAsNotDone(); + } + currentTask.add(newToDoTask); + break; + + case DEADLINE: + Task newDeadlineTask = new Deadline(inputArray[2].trim(), inputArray[3].trim()); + if (inputArray[1].trim().equals(COMPLETED_TASK_CROSS)) { + newDeadlineTask.markAsDone(); + } else { + newDeadlineTask.markAsNotDone(); + } + currentTask.add(newDeadlineTask); + break; + case EVENT: + String [] fromAndTo = inputArray[3].trim().split(FROM_AND_TO_SEPARATOR); + Task newEventTask = new Event(inputArray[2].trim(), fromAndTo[0].trim(), fromAndTo[1].trim()); + if (inputArray[1].trim().equals(COMPLETED_TASK_CROSS)) { + newEventTask.markAsDone(); + } else { + newEventTask.markAsNotDone(); + } + currentTask.add(newEventTask); + break; + default: + System.out.println(TEXT_FILE_CORRUPTED_ERROR); + break; + } + } + + /** + * Read text file accesses Kowalski.txt and calls the restoreTaskList function + * @param currentTask ArrayList containing the current tasks + * @throws IOException when unable to get the Kowalski file or has any input errors + */ + public static void readTextFile(List currentTask) throws IOException{ + try { + createTextFileFolder(Paths.get(TEXT_FILE_FOLDER)); + Path filePath = Paths.get(FULL_FILE_PATH); + if (!Files.exists(filePath)){ + System.out.println(CREATE_NEW_TEXT_FILE); + Files.createFile(filePath); + } + FileReader fileReader = new FileReader(FULL_FILE_PATH); + BufferedReader line = new BufferedReader(fileReader); + System.out.println(RETRIEVING_PREVIOUS_DATA_MESSAGE); + while (line.ready()) { + restoreTaskList(line.readLine(), currentTask); + } + System.out.println(DATA_RETRIEVAL_SUCCESS_MESSAGE); + Ui.printDivider(); + } catch (IOException e){ + System.out.println(DATA_RETRIEVAL_FAIL_MESSAGE); + throw e; + } + } + + /** + * Function which is called to generate an arrayList "lines" which updates according to the users' inputs. + * Calls the writeTextFile function to update Kowalski.txt + * @param currentTask ArrayList containing the current tasks + */ + public static void writeText(List currentTask){ + List lines = new ArrayList<>(); + for (Task task:currentTask){ + lines.add(task.textFileInputString()); + } + Ui.printDivider(); + System.out.println(ANALYSING_INPUT_MESSAGE); + writeTextFile(lines); + } + + /** + * Accesses the Kowalski.txt and updates it in the correct format. + * @param lines: Arraylist containing the processed current tasks in the CurrentTask + */ + public static void writeTextFile(List lines) { + try { + Path parentPath = Paths.get(TEXT_FILE_FOLDER); + createTextFileFolder(parentPath); + + FileWriter writer = new FileWriter(FULL_FILE_PATH); + for (String line : lines) { + writer.write(line + NEWLINE); + + } + System.out.println(CHANGE_RECORDED_IN_TEXT_FILE); + writer.close(); + } catch (IOException e){ + System.out.println(ISSUE_WITH_DIRECTORY_OR_TEXT_FILE); + } + } + + /** + * Used to create the data folder to store the Kowalski.txt file + * @param parentPath: Path file containing the path we intend to make Kowalski.txt in + * @throws IOException whenever the input for the path or creation of the directory is improper + */ + public static void createTextFileFolder(Path parentPath ) throws IOException{ + try { + Files.createDirectories(parentPath); + } catch (FileAlreadyExistsException ignored){ + //Ignore this error if file exists + } catch (IOException e){ + System.out.println(FAILURE_TO_CREATE_DIRECTORY); + throw e; + } + } +} diff --git a/src/main/java/Kowalski/commands/TaskList.java b/src/main/java/Kowalski/commands/TaskList.java new file mode 100644 index 000000000..c316a3f8c --- /dev/null +++ b/src/main/java/Kowalski/commands/TaskList.java @@ -0,0 +1,75 @@ +package Kowalski.commands; + +import Kowalski.tasks.Task; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class TaskList { + public static List currentTask = new ArrayList<>(); + public static final String DOT = "."; + public static final String NO_MATCHES_MESSAGE = "My analysis shows no matches Skipper!"; + public static final String MATCHES_FOUND_MESSAGE = "Skipper here are the matching tasks:"; + + /** + * Used to process the different variations of the users inputs + * @param userInput : String which the user inputs + * @return String which is in lowercase and clear of any unnecessary whitespace + */ + public static String processInput(String userInput){ + return (userInput.trim()).toLowerCase(); + } + + /** + * Used in the "list" command to print all the Current Tasks in the proper format + */ + public static void printCurrentTaskItems(){ + for (int i = 1; i <= currentTask.size(); i++){ + System.out.println(i + DOT + currentTask.get(i-1)); + } + } + + /** + * Used in the "find" command to print all the matching Tasks in the proper format + * @param matchList ArrayList containing the matching tasks + */ + public static void printMatchingTasks(List matchList){ + for (int i = 1; i <= matchList.size(); i++){ + System.out.println(i + DOT + matchList.get(i-1)); + } + } + + /** + * Method to add tasks in the ArrayList currentTask + * @param task an instance of the task which needs to be added + */ + public static void addTask(Task task) { + currentTask.add(task);; + } + + /** + * Method to remove tasks in the ArrayList currentTask + * @param index index of the task to be removed from currentTask + */ + public static void removeTask(int index){ + currentTask.remove(index); + } + + /** + * Method to return the task with descriptions matching the user's input word + * @param input user's input word + */ + public static void findMatch(String input){ + ListmatchList = currentTask.stream() + .filter(task -> task.getDescription().toLowerCase().contains(input)) + .collect(Collectors.toList()); + + if (matchList.isEmpty()){ + System.out.println(NO_MATCHES_MESSAGE); + } else { + System.out.println(MATCHES_FOUND_MESSAGE); + printMatchingTasks(matchList); + } + } +} diff --git a/src/main/java/Kowalski/tasks/Deadline.java b/src/main/java/Kowalski/tasks/Deadline.java new file mode 100644 index 000000000..597925af8 --- /dev/null +++ b/src/main/java/Kowalski/tasks/Deadline.java @@ -0,0 +1,32 @@ +package Kowalski.tasks; + +public class Deadline extends Task { + + protected String deadline; + + public Deadline(String description, String by) { + super(description); + this.deadline = by; + } + + /** + * This function returns the deadline of the "deadline" event + * @return String of the Deadline + */ + public String getDeadline() { + return deadline; + } + + @Override + public String textFileInputString(){ + return String.format("D | %s | %s | %s", + isDone? "X" : "0", + getDescription().trim(), + getDeadline()); + } + + @Override + public String toString() { + return "[D]" + super.toString() + " (by: " + deadline + ")"; + } +} diff --git a/src/main/java/Kowalski/tasks/Event.java b/src/main/java/Kowalski/tasks/Event.java new file mode 100644 index 000000000..e4aa458c8 --- /dev/null +++ b/src/main/java/Kowalski/tasks/Event.java @@ -0,0 +1,42 @@ +package Kowalski.tasks; + +public class Event extends Task { + protected String startDayAndTime; + protected String endDayAndTime; + + public Event (String description, String startDayAndTime, String endDayAndTime) { + super(description); + this.startDayAndTime = startDayAndTime; + this.endDayAndTime = endDayAndTime; + } + + /** + * Method obtains the start date and time of the event + * @return String of the start date and time + */ + public String getStartDayAndTime() { + return startDayAndTime; + } + + /** + * Method obtains the end date and time of the event + * @return String of the end date and time + */ + public String getEndDayAndTime() { + return endDayAndTime; + } + + @Override + public String textFileInputString(){ + return String.format("E | %s | %s | %s - %s", + isDone? "X" : "0", + getDescription().trim(), + getStartDayAndTime(), + getEndDayAndTime()); + } + + @Override + public String toString() { + return "[E]" + super.toString() + " (from: " + startDayAndTime + " to: " + endDayAndTime + ")"; + } +} diff --git a/src/main/java/Kowalski/tasks/Task.java b/src/main/java/Kowalski/tasks/Task.java new file mode 100644 index 000000000..72004cc99 --- /dev/null +++ b/src/main/java/Kowalski/tasks/Task.java @@ -0,0 +1,59 @@ +package Kowalski.tasks; + +public class Task { + protected String description; + protected boolean isDone; + + public Task(String description) { + this.description = description; + this.isDone = false; + } + + /** + * This method provides the description of a certain task + * @return details of task + */ + public String getDescription() { + return description; + } + + /** + * This method returns the symbol to store the status of a task in the text file + * @return "X" or " " depending on whether the task is done + */ + public String getStatusIcon() { + + return (isDone ? "X" : " "); // mark done task with X + } + + /** + * Marks a task as incomplete + */ + public void markAsNotDone(){ + + this.isDone = false; + } + + /** + * Marks a task as complete + */ + public void markAsDone(){ + + this.isDone = true; + } + + /** + * Returns the information of the task in a proper format to be stored in the text file + * @return various information regarding a task in the proper format + */ + public String textFileInputString() { + return String.format("%s | %s", + isDone? "X" : "0", + getDescription().trim()); + } + + @Override + public String toString() { + return "[" + getStatusIcon() + "] " + getDescription(); + } +} diff --git a/src/main/java/Kowalski/tasks/Todo.java b/src/main/java/Kowalski/tasks/Todo.java new file mode 100644 index 000000000..3d4519840 --- /dev/null +++ b/src/main/java/Kowalski/tasks/Todo.java @@ -0,0 +1,21 @@ +package Kowalski.tasks; + +public class Todo extends Task { + + public Todo(String description){ + super(description); + } + + + @Override + public String textFileInputString(){ + return String.format("T | %s | %s", + isDone? "X" : "0", + getDescription().trim()); + } + + @Override + public String toString() { + return "[T]" + super.toString(); + } +} diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..6d10568b7 --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Kowalski.Kowalski +