diff --git a/.gitignore b/.gitignore index 2873e189e..84acdadd4 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ bin/ /text-ui-test/ACTUAL.TXT text-ui-test/EXPECTED-UNIX.TXT +*.txt diff --git a/docs/README.md b/docs/README.md index 8077118eb..58151f63d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,263 @@ -# User Guide +# Aragorn +________________________________ +Aragorn is a simple chatbot that helps you tracks your tasks! +## Quick Start +1. Ensure you have Java 11 or above installed on your computer. +2. Download the latest joe.jar release [here](https://github.com/vimalapugazhan/ip/releases). +3. Copy the file to a folder you want as the home directory of the file. +4. From the command terminal, `cd` into the home directory of the jar file and run the command `java -jar Aragorn.jar` to launch the application. +5. Type a command in the command box and press Enter to execute it. Refer to **Features** below for details of each available command. ## Features -### Feature-ABC +- ### Add task -Description of the feature. + - Add 3 different types of tasks; ToDo, Deadline and Event. -### Feature-XYZ +- ### Delete task -Description of the feature. + - Remove a task from the list. -## Usage +- ### Mark and Unmark -### `Keyword` - Describe action + - Mark a task complete. + - Unmark a completed task. -Describe the action and its outcome. +- ### Find task -Example of usage: + - Find tasks containing inputted keyword. -`keyword (optional arguments)` +- ### Save and Load + + - Automatically saves the list when a command is run and loads it the next time the program is launched. + +## Commands + +### `todo` - Add ToDo task + +Usage: + +`todo ` + +Expected outcome: + +``` +todo Read book + __________________________________________________________ + Got it. I've added this task: + [T][ ] Read book + + You have 1 / 1 remaining tasks in the list. + __________________________________________________________ +``` +________________________________________ +### `deadline` - Add Deadline task + +Usage: + +`deadline /by ` + +Expected outcome: + +``` +deadline CG2113 Quiz /by Friday 12pm + __________________________________________________________ + Got it. I've added this task: + [D][ ] CG2113 Quiz (by: Friday 12pm) + + You have 2 / 2 remaining tasks in the list. + __________________________________________________________ +``` +________________________________________ +### `event` - Add Event task + +Usage: + +`event /from /by ` + +Expected outcome: + +``` +event Birthday party /from Sunday 5pm /to 10pm + __________________________________________________________ + Got it. I've added this task: + [E][ ] Birthday party (from: Sunday 5pm to: 10pm) + + You have 3 / 3 remaining tasks in the list. + __________________________________________________________ +``` +________________________________________ +### `list` - Display list + +Usage: + +`list` Expected outcome: -Description of the outcome. +``` +list + __________________________________________________________ + + Here are the tasks in your list: + 1. [T][ ] Read book + 2. [D][ ] CG2113 Quiz (by: Friday 12pm) + 3. [E][ ] Birthday party (from: Sunday 5pm to: 10pm) + + + You have 3 / 3 remaining tasks in the list. + __________________________________________________________ +``` +________________________________________ +### `mark` - Mark task as complete + +Usage: + +`mark ` + +Expected outcome: + +``` +mark 2 + __________________________________________________________ + Nice! I've marked this task as done: + [D][X] CG2113 Quiz (by: Friday 12pm) + + You have 2 / 3 remaining tasks in the list. + __________________________________________________________ +``` +________________________________________ +### `unmark` - Unmark a completed task + +Usage: + +`unmark ` + +Expected outcome: + +``` +unmark 2 + __________________________________________________________ + OK, I've marked this task as incomplete: + [D][ ] CG2113 Quiz (by: Friday 12pm) + + You have 3 / 3 remaining tasks in the list. + __________________________________________________________ +``` +________________________________________ +### `find` - Display a list of task containing keyword + +Usage: + +`find ` + +Expected outcome: + +``` +list + __________________________________________________________ + + Here are the tasks in your list: + 1. [T][ ] Read book + 2. [D][X] CG2113 Quiz (by: Friday 12pm) + 3. [E][ ] Birthday party (from: Sunday 5pm to: 10pm) + 4. [E][ ] 2113 meeting (from: Tuesday 6pm to: 7pm) + + + You have 3 / 4 remaining tasks in the list. + __________________________________________________________ + +find 2113 + __________________________________________________________ + + Here are the tasks in your list: + 1. [D][X] CG2113 Quiz (by: Friday 12pm) + 2. [E][ ] 2113 meeting (from: Tuesday 6pm to: 7pm) + + + 2 matching tasks found! + __________________________________________________________ + +``` +________________________________________ +### `delete` - Delete task from list + +Usage: + +`delete ` + +Expected outcome: + +``` +delete 2 + __________________________________________________________ + I've deleted this task from the list: + [D][X] CG2113 Quiz (by: Friday 12pm) + + You have 3 / 3 remaining tasks in the list. + __________________________________________________________ + +list + __________________________________________________________ + + Here are the tasks in your list: + 1. [T][ ] Read book + 2. [E][ ] Birthday party (from: Sunday 5pm to: 10pm) + 3. [E][ ] 2113 meeting (from: Tuesday 6pm to: 7pm) + + + You have 3 / 3 remaining tasks in the list. + __________________________________________________________ + +``` +________________________________________ +### `help` - Display command list + +Usage: + +`help` + +Expected outcome: + +``` +help + __________________________________________________________ + Here is a list of commands: + + "list": Displays list of tasks. + + "todo ": Adds a Todo task to the list. + + "deadline /by ": Adds a task and its deadline to the list. + + "event /from /to ": Adds an event and its start and end conditions to the list. + + "mark ": Marks the corresponding task in the list as completed. + + "unmark ": Marks the corresponding task in the list as incomplete. + + "delete ": Removes the corresponding task from the list. + + "find ": Displays the tasks containing the keyword. + + "help": Displays this list of commands. + + "bye": Closes the program. + __________________________________________________________ +``` +________________________________________ +### `bye` - Exits the program + +Example of usage: + +`bye` + +Expected outcome: ``` -expected output +bye + __________________________________________________________ + Farewell. May we meet again! + __________________________________________________________ ``` +________________________________________ 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/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..dc2687a13 --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: main.Aragorn + diff --git a/src/main/java/exceptions/AragornException.java b/src/main/java/exceptions/AragornException.java new file mode 100644 index 000000000..b10e3ed07 --- /dev/null +++ b/src/main/java/exceptions/AragornException.java @@ -0,0 +1,7 @@ +package exceptions; + +public class AragornException extends Exception{ + public AragornException(String text) { + super(text); + } +} diff --git a/src/main/java/main/Aragorn.java b/src/main/java/main/Aragorn.java new file mode 100644 index 000000000..c3a48d0a3 --- /dev/null +++ b/src/main/java/main/Aragorn.java @@ -0,0 +1,46 @@ +package main; + +import java.io.IOException; +import java.util.ArrayList; +import exceptions.AragornException; +import utilities.commands.CommandExecuter; +import utilities.commands.CommandIdentifier; +import ui.Constants; +import utilities.file.FileReader; +import utilities.file.FileWriter; +import tasks.Task; +import java.util.Scanner; +/** + * A task tracker that allows users to add, delete, find, list, nark, and saves the list. + */ +public class Aragorn { + + private static final ArrayList list = new ArrayList<>(); + + public static void main(String[] args) throws AragornException { + System.out.println(Constants.HELLOMESSAGE); + try { + FileReader.readFile(); + } catch (IOException e) { + System.out.println(Constants.FILEREADERROR); + } + Scanner in = new Scanner(System.in); + while(true) { + String userInput; + userInput = in.nextLine(); + String commandType = CommandIdentifier.commandIdentifier(userInput); + if (commandType.equals(Constants.BYE)) { + System.out.println(Constants.BYEMESSAGE); + FileWriter.writeFile(); + return; + } + CommandExecuter.executeCommand(userInput, commandType); + FileWriter.writeFile(); + } + } + + public static ArrayList getList() { + return list; + } +} + diff --git a/src/main/java/tasks/Deadline.java b/src/main/java/tasks/Deadline.java new file mode 100644 index 000000000..f497f34d3 --- /dev/null +++ b/src/main/java/tasks/Deadline.java @@ -0,0 +1,29 @@ +package tasks; + +/** + * Deadline task with details on the description, deadline condition and whether it's complete. + */ +public class Deadline extends Task { + + protected String deadline; + + public Deadline(String description, boolean isDone, String date) { + super(description, isDone); + deadline = date; + } + + @Override + public String getDescription() { + return description + " (by: " + deadline + ")"; + } + + @Override + public String toFileString() { + return String.format("deadline|%s|%s|%s", isDone ? "1" : "0", description, deadline); + } + + @Override + public String taskString() { + return "[D]" + "[" + getStatusIcon() + "] " + getDescription(); + } +} diff --git a/src/main/java/tasks/Event.java b/src/main/java/tasks/Event.java new file mode 100644 index 000000000..4006acc82 --- /dev/null +++ b/src/main/java/tasks/Event.java @@ -0,0 +1,32 @@ +package tasks; + +/** + * Event task with details on the description, start and end conditions and whether it's complete. + */ +public class Event extends Task { + + protected String start; + protected String end; + + + public Event(String description, boolean isDone, String startTime, String endTime) { + super(description, isDone); + start = startTime; + end = endTime; + } + + @Override + public String getDescription() { + return description + " (from: " + start + " to: " + end + ")"; + } + + @Override + public String toFileString() { + return String.format("event|%s|%s|%s|%s", isDone ? "1" : "0", description, start, end); + } + + @Override + public String taskString() { + return "[E]" + "[" + getStatusIcon() + "] " + getDescription(); + } +} diff --git a/src/main/java/tasks/Task.java b/src/main/java/tasks/Task.java new file mode 100644 index 000000000..71b4f1f37 --- /dev/null +++ b/src/main/java/tasks/Task.java @@ -0,0 +1,47 @@ +package tasks; + +/** + * General task with details on the description and whether it's complete. + */ +public class Task { + protected String description; + protected boolean isDone; + + + public Task(String description, boolean isDone) { + this.description = description; + this.isDone = isDone; + } + + public String getStatusIcon() { + return this.isDone ? "X" : " "; // mark done task with X + } + + public void markAsDone() { + this.setDone(true); + } + + public void markAsUndone() { + this.setDone(false); + } + + public String getDescription() { + return description; + } + + public String getDescriptionOnly() { + return description; + } + + public void setDone(boolean done) { + isDone = done; + } + + public String toFileString() { + return String.format("task|%s|%s", isDone ? "1" : "0", getDescription()); + } + + public String taskString() { + return "[" + getStatusIcon() + "] " + getDescription(); + } +} diff --git a/src/main/java/tasks/ToDo.java b/src/main/java/tasks/ToDo.java new file mode 100644 index 000000000..77eec95d8 --- /dev/null +++ b/src/main/java/tasks/ToDo.java @@ -0,0 +1,22 @@ +package tasks; + +/** + * Todo task with details on the description and whether it's complete. + */ +public class ToDo extends Task { + + public ToDo(String userInput, boolean isDone) { + super(userInput, isDone); + } + + @Override + public String toFileString() { + return String.format("todo|%s|%s", isDone ? "1" : "0", getDescription()); + } + + @Override + public String taskString() { + return "[T]" + "[" + getStatusIcon() + "] " + getDescription(); + } +} + diff --git a/src/main/java/ui/Constants.java b/src/main/java/ui/Constants.java new file mode 100644 index 000000000..d58593580 --- /dev/null +++ b/src/main/java/ui/Constants.java @@ -0,0 +1,99 @@ +package ui; + +import tasks.Task; + +/** + * Class containing all print statements to avoid using magic string literals. + */ +public final class Constants { + public static final String LINE = " __________________________________________________________\n"; + + public static final String GREET = " Hello! I am Aragorn son of Arathorn, and am called Elessar, the Elfstone, DĂșnadan,\n" + + " the heir of Isildur Elendil's son of Gondor.\n" + + " What can I do for you?\n"; + public static final String EXIT = " Farewell. May we meet again!\n"; + public static final String TAB = " "; + public static final String COMMANDLIST = " Here is a list of commands:\n" + + "\n" + + " \"list\": Displays list of tasks.\n" + + "\n" + + " \"todo \": Adds a Todo task to the list.\n" + + "\n" + + " \"deadline /by \": Adds a task and its deadline to the list.\n" + + "\n" + + " \"event /from /to \": Adds an event and its start and end conditions to the list.\n" + + "\n" + + " \"mark \": Marks the corresponding task in the list as completed.\n" + + "\n" + + " \"unmark \": Marks the corresponding task in the list as incomplete.\n" + + "\n" + + " \"delete \": Removes the corresponding task from the list.\n" + + "\n" + + " \"find \": Displays the tasks containing the keyword.\n" + + "\n" + + " \"help\": Displays this list of commands.\n" + + "\n" + + " \"bye\": Closes the program.\n"; + public static final String FILEREADERROR = LINE + " File read error\n" + LINE; + public static final String FILEERROR = LINE + " File Error!\n" + LINE ; + public static final String FILEWRITEERROR = "Error writing file: "; + public static final String CREATEDIRECTORYERROR = "Unable to create directory: "; + public static final String ALREADYUNMARKED = LINE + " This task has already been unmarked.\n" + LINE; + public static final String ALREADYMARKED = LINE + " This task has already been marked.\n" + LINE; + public static final String ADDEDTASK = LINE + " Got it. I've added this task:"; + public static final String INVALIDFORMAT = LINE + " Invalid format!\n" + LINE; + public static final String NOFILE = "No file found! Creating new file."; + public static final String FILEPATH = "./ip/data/AragornList.txt"; + public static final String CURRENTLIST = " Here are the tasks in your list: "; + public static final String EMPTYLIST = LINE + " List is empty. Add tasks to view them here.\n" + LINE; + public static final String EMPTYINPUT = LINE + " Input is empty. Use \"help\" command to view the list of commands\n" + LINE; + public static final String INVALIDINPUT = LINE + " Your input is invalid. Use the \"help\" command to view the list of commands.\n" + LINE; + public static final String HELLOMESSAGE =LINE + GREET + LINE + "\n" + COMMANDLIST + LINE; + public static final String HELPMESSAGE = LINE + COMMANDLIST + LINE; + public static final String BYEMESSAGE = LINE + TAB + EXIT + LINE; + public static final String TASKTYPEERROR = LINE + " Task type error: "; + public static final String INVALIDINDEXFORMAT = LINE + " Invalid format\n" + LINE; + public static final String INVALIDINDEX = LINE + " Task index is not in the list\n" + LINE; + public static final String INVALIDTASK = LINE + " Invalid Task\n" + LINE; + public static final String DELETETASK = LINE + TAB + "I've deleted this task from the list:\n" + TAB + " "; + public static final String MARKTASK = LINE + TAB + "Nice! I've marked this task as done:\n" + TAB + " "; + public static final String UNMARKTASK = LINE + TAB + "OK, I've marked this task as incomplete:\n" + TAB + " "; + public static final String NEWLINE = "\n"; + public static final String COMPLETE = "X"; + public static final String INCOMPLETE = " "; + public static final String TODO = "TODO"; + public static final String DEADLINE = "DEADLINE"; + public static final String EVENT = "EVENT"; + public static final String LIST = "LIST"; + public static final String MARK = "MARK"; + public static final String UNMARK = "UNMARK"; + public static final String DELETE = "DELETE"; + public static final String FIND = "FIND"; + public static final String HELP = "HELP"; + public static final String BYE = "BYE"; + public static final String INVALID = "INVALID"; + public static final String DOT = ". "; + public static final String ONE = "1"; + public static final String BAR = "\\|"; + public static final String BYREGEX = "/by"; + public static final String FROMREGEX = "/from"; + public static final String TOREGEX = "/to"; + public static final String EMPTYDESCRIPTION = LINE + " Task description is empty!\n" + LINE; + public static final String EMPTYDEADLINE = LINE + " Deadline condition is empty!\n" + LINE; + public static final String EMPTYEVENTSTART = LINE + " Start condition is empty!\n" + LINE; + public static final String EMPTYEVENTEND = LINE + " End condition is empty!\n" + LINE; + public static final String EMPTYFIND = LINE + " No matched found. :(\n" + LINE; + public static final String FINDTASK = " matching tasks found!"; + + public static void printRemainingTasks(int remainingTasks, int size) { + System.out.println(" You have " + remainingTasks + " / " + size + " remaining tasks in the list.\n" + LINE); + } + public static void printAddTask(Task list) { + System.out.println(ADDEDTASK); + System.out.println(TAB + list.taskString() + NEWLINE); + } + public static Task taskTypeError(String taskType) { + System.out.println(TASKTYPEERROR + taskType); + return null; + } +} diff --git a/src/main/java/utilities/commands/AddTask.java b/src/main/java/utilities/commands/AddTask.java new file mode 100644 index 000000000..2bea192b7 --- /dev/null +++ b/src/main/java/utilities/commands/AddTask.java @@ -0,0 +1,73 @@ +package utilities.commands; + +import main.Aragorn; +import tasks.Deadline; +import tasks.Event; +import tasks.ToDo; +import ui.Constants; +import utilities.parser.InputParser; + +/** + * Methods to add todo, deadline, event task to the list. + */ +public class AddTask { + + /** + * Adds an event task into the task list and prints the number of tasks remaining. + * + * @param input Parsed input containing event description, start condition and end condition. + * @param remainingTasks Number of tasks that remain incomplete. + */ + protected static void eventCommand(InputParser input, int remainingTasks) { + try { + if (input.getSplitInput()[0].isEmpty() || input.getSplitInput()[1].isEmpty() || input.getSplitInput()[2].isEmpty()) { + return; + } + Event newEvent = new Event(input.getSplitInput()[0].trim(), false, input.getSplitInput()[1].trim(), input.getSplitInput()[2].trim()); + Aragorn.getList().add(newEvent); + Constants.printAddTask(newEvent); + remainingTasks += 1; + Constants.printRemainingTasks(remainingTasks, Aragorn.getList().size()); + } catch (NullPointerException e) { + return; + } + } + + /** + * Adds a deadline task into the task list and prints the number of tasks remaining. + * + * @param input Parsed input containing deadline task description and the deadline date. + * @param remainingTasks Number of tasks that remain incomplete. + */ + protected static void deadlineCommand(InputParser input, int remainingTasks) { + try { + if (input.getSplitInput()[0].isEmpty() || input.getSplitInput()[1].isEmpty()) { + return; + } + Deadline newDeadline = new Deadline(input.getSplitInput()[0].trim(), false, input.getSplitInput()[1].trim()); + Aragorn.getList().add(newDeadline); + Constants.printAddTask(newDeadline); + remainingTasks += 1; + Constants.printRemainingTasks(remainingTasks, Aragorn.getList().size()); + } catch (NullPointerException e) { + return; + } + } + + /** + * Adds a todo task into the task list and prints the number of tasks remaining. + * + * @param input Parsed input containing todo task description. + * @param remainingTasks Number of tasks that remain incomplete. + */ + protected static void todoCommand(InputParser input, int remainingTasks) { + if (input.getSplitInput()[0].trim().isEmpty()) { + return; + } + ToDo newTask = new ToDo(input.getSplitInput()[0].trim(), false); + Aragorn.getList().add(newTask); + Constants.printAddTask(newTask); + remainingTasks += 1; + Constants.printRemainingTasks(remainingTasks, Aragorn.getList().size()); + } +} diff --git a/src/main/java/utilities/commands/CommandExecuter.java b/src/main/java/utilities/commands/CommandExecuter.java new file mode 100644 index 000000000..6c7063b04 --- /dev/null +++ b/src/main/java/utilities/commands/CommandExecuter.java @@ -0,0 +1,77 @@ +package utilities.commands; + +import main.Aragorn; +import tasks.Task; +import ui.Constants; +import utilities.parser.InputParser; + +public class CommandExecuter { + + /** + * Receives the user input and executes the corresponding command. + * + * @param userInput User input typed on the console containing the task details. + * @param commandType The command inputted by user. + */ + public static void executeCommand(String userInput, String commandType) { + + try { + InputParser input = new InputParser(userInput.trim(), commandType); + int remainingTasks = 0; + for (Task i : Aragorn.getList()) { + if (i.getStatusIcon().equals(Constants.INCOMPLETE)) { + remainingTasks += 1; + } + } + + switch (commandType) { + case Constants.LIST: + DisplayList.listCommand(Aragorn.getList()); + Constants.printRemainingTasks(remainingTasks, Aragorn.getList().size()); + break; + + case Constants.UNMARK: + UnmarkTask.unmarkTask(input, remainingTasks); + break; + + case Constants.MARK: + MarkTask.markTask(input, remainingTasks); + break; + + case Constants.DELETE: + DeleteTask.deleteTask(input, remainingTasks); + break; + + case Constants.TODO: + AddTask.todoCommand(input, remainingTasks); + break; + + case Constants.DEADLINE: + AddTask.deadlineCommand(input, remainingTasks); + break; + + case Constants.EVENT: + AddTask.eventCommand(input, remainingTasks); + break; + + case Constants.HELP: + System.out.println(Constants.HELPMESSAGE); + break; + + case Constants.FIND: + FindTask.findTasks(input, Aragorn.getList()); + break; + + case Constants.INVALID: + if (userInput.trim().isEmpty()) { + System.out.println(Constants.EMPTYINPUT); + } else { + System.out.println(Constants.INVALIDINPUT); + } + break; + } + } catch (NumberFormatException | StringIndexOutOfBoundsException e) { + System.out.println(Constants.INVALIDINDEXFORMAT); + } + } +} diff --git a/src/main/java/utilities/commands/CommandIdentifier.java b/src/main/java/utilities/commands/CommandIdentifier.java new file mode 100644 index 000000000..c39389728 --- /dev/null +++ b/src/main/java/utilities/commands/CommandIdentifier.java @@ -0,0 +1,41 @@ +package utilities.commands; + +import ui.Constants; + +public class CommandIdentifier { + + /** + * Identifies the command inputted from the user input. + * + * @param userInput Input from the user containing the command and details. + * @return commandType + */ + public static String commandIdentifier(String userInput) { + String commandType; + + if (userInput.trim().equals(Constants.BYE.toLowerCase())) { + commandType = Constants.BYE; + } else if (userInput.trim().equals(Constants.LIST.toLowerCase())) { + commandType = Constants.LIST; + } else if (userInput.trim().startsWith(Constants.UNMARK.toLowerCase())) { + commandType = Constants.UNMARK; + } else if (userInput.trim().startsWith(Constants.MARK.toLowerCase())) { + commandType = Constants.MARK; + } else if (userInput.trim().startsWith(Constants.DELETE.toLowerCase())) { + commandType = Constants.DELETE; + } else if (userInput.trim().startsWith(Constants.TODO.toLowerCase())) { + commandType = Constants.TODO; + } else if (userInput.trim().startsWith(Constants.DEADLINE.toLowerCase())) { + commandType = Constants.DEADLINE; + } else if (userInput.trim().startsWith(Constants.EVENT.toLowerCase())) { + commandType = Constants.EVENT; + } else if (userInput.trim().equals(Constants.HELP.toLowerCase())) { + commandType = Constants.HELP; + } else if (userInput.trim().startsWith(Constants.FIND.toLowerCase())) { + commandType = Constants.FIND; + } else { + commandType = Constants.INVALID; + } + return commandType; + } +} diff --git a/src/main/java/utilities/commands/DeleteTask.java b/src/main/java/utilities/commands/DeleteTask.java new file mode 100644 index 000000000..9fa86a83d --- /dev/null +++ b/src/main/java/utilities/commands/DeleteTask.java @@ -0,0 +1,31 @@ +package utilities.commands; + +import main.Aragorn; +import ui.Constants; +import utilities.parser.InputParser; + +public class DeleteTask { + + /** + * Deletes a task from the task list and prints the number of tasks remaining. + * + * @param input Parsed input containing index of the task being deleted. + * @param remainingTasks Number of tasks that remain incomplete. + */ + protected static void deleteTask(InputParser input, int remainingTasks) { + try { + int index = Integer.parseInt(input.getSplitInput()[0]); + String icon = Aragorn.getList().get(index).getStatusIcon(); + if (icon.equals(Constants.INCOMPLETE)) { + remainingTasks -= 1; + } + System.out.println(Constants.DELETETASK + Aragorn.getList().get(index).taskString() + Constants.NEWLINE); + Aragorn.getList().remove(index); + Constants.printRemainingTasks(remainingTasks, Aragorn.getList().size()); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(Constants.INVALIDTASK); + } catch (IndexOutOfBoundsException e) { + System.out.println(Constants.INVALIDINDEX); + } + } +} diff --git a/src/main/java/utilities/commands/DisplayList.java b/src/main/java/utilities/commands/DisplayList.java new file mode 100644 index 000000000..b3e4574d9 --- /dev/null +++ b/src/main/java/utilities/commands/DisplayList.java @@ -0,0 +1,28 @@ +package utilities.commands; + + +import tasks.Task; +import ui.Constants; + +import java.util.ArrayList; + +public class DisplayList { + + /** + * Displays the inputted array list to show details of task and the number of tasks remaining. + * + * @param list Array list containing the details of tasks. + */ + public static void listCommand(ArrayList list) { + if (list.isEmpty()) { + System.out.println(Constants.EMPTYLIST); + return; + } + System.out.println(Constants.LINE); + System.out.println(Constants.CURRENTLIST); + for (int i = 0; i < list.size(); i += 1) { + System.out.println(Constants.TAB + (i + 1) + Constants.DOT + list.get(i).taskString()); + } + System.out.println(Constants.NEWLINE); + } +} diff --git a/src/main/java/utilities/commands/FindTask.java b/src/main/java/utilities/commands/FindTask.java new file mode 100644 index 000000000..888aa4dac --- /dev/null +++ b/src/main/java/utilities/commands/FindTask.java @@ -0,0 +1,41 @@ +package utilities.commands; + +import tasks.Task; +import ui.Constants; +import utilities.parser.InputParser; + +import java.util.ArrayList; + +public class FindTask { + + /** + * Finds tasks matching the keywords inputted and displays them on another list. + * + * @param input Parsed input containing the keywords for the find function. + * @param list Array list with the tasks containing the keyword. + */ + public static void findTasks(InputParser input, ArrayList list) { + + String keyword = input.getSplitInput()[0]; + ArrayList tasksFound = new ArrayList<>(); + if (list.isEmpty()) { + System.out.println(Constants.EMPTYFIND); + return; + } + + for (Task task : list) { + if (task.getDescriptionOnly().contains(keyword)) { + tasksFound.add(task); + } + } + + if (tasksFound.isEmpty()) { + System.out.println(Constants.EMPTYFIND); + return; + } + + DisplayList.listCommand(tasksFound); + System.out.println(Constants.TAB + tasksFound.size() + Constants.FINDTASK); + System.out.println(Constants.LINE); + } +} \ No newline at end of file diff --git a/src/main/java/utilities/commands/MarkTask.java b/src/main/java/utilities/commands/MarkTask.java new file mode 100644 index 000000000..c40d77aa1 --- /dev/null +++ b/src/main/java/utilities/commands/MarkTask.java @@ -0,0 +1,35 @@ +package utilities.commands; + +import main.Aragorn; +import ui.Constants; +import utilities.parser.InputParser; + +public class MarkTask { + + /** + * Marks a task from the task list as done and prints the number of tasks remaining. + * + * @param input Parsed input containing index of the task being marked as complete. + * @param remainingTasks Number of tasks that remain incomplete. + */ + protected static void markTask(InputParser input, int remainingTasks) { + try { + int index = Integer.parseInt(input.getSplitInput()[0]); + String icon = Aragorn.getList().get(index).getStatusIcon(); + if (icon.equals(Constants.COMPLETE)) { + System.out.println(Constants.ALREADYMARKED); + return; + } + Aragorn.getList().get(index).markAsDone(); + remainingTasks -= 1; + System.out.println(Constants.MARKTASK + Aragorn.getList().get(index).taskString() + Constants.NEWLINE); + Constants.printRemainingTasks(remainingTasks, Aragorn.getList().size()); + } catch (NullPointerException e) { + System.out.println(Constants.INVALIDINDEX); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(Constants.INVALIDTASK); + } catch (IndexOutOfBoundsException e) { + System.out.println(Constants.INVALIDTASK); + } + } +} diff --git a/src/main/java/utilities/commands/UnmarkTask.java b/src/main/java/utilities/commands/UnmarkTask.java new file mode 100644 index 000000000..2d9c9d4b5 --- /dev/null +++ b/src/main/java/utilities/commands/UnmarkTask.java @@ -0,0 +1,34 @@ +package utilities.commands; + +import main.Aragorn; +import ui.Constants; +import utilities.parser.InputParser; + +public class UnmarkTask { + + /** + * Unmarks a completed task from the task list and prints the number of tasks remaining. + * + * @param input Parsed input containing index of the task being unmarked. + * @param remainingTasks Number of tasks that remain incomplete. + */ + protected static void unmarkTask(InputParser input, int remainingTasks) { + try { + int index = Integer.parseInt(input.getSplitInput()[0]); + String icon = Aragorn.getList().get(index).getStatusIcon(); + if (icon.equals(Constants.INCOMPLETE)) { + System.out.println(Constants.ALREADYUNMARKED); + return; + } + Aragorn.getList().get(index).markAsUndone(); + remainingTasks += 1; + System.out.println(Constants.UNMARKTASK + Aragorn.getList().get(index).taskString() + Constants.NEWLINE); + Constants.printRemainingTasks(remainingTasks, Aragorn.getList().size()); + } catch (NullPointerException e) { + System.out.println(Constants.INVALIDINDEX); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(Constants.INVALIDTASK); + } + } + +} diff --git a/src/main/java/utilities/file/FileHandler.java b/src/main/java/utilities/file/FileHandler.java new file mode 100644 index 000000000..49422c77d --- /dev/null +++ b/src/main/java/utilities/file/FileHandler.java @@ -0,0 +1,58 @@ +package utilities.file; + +import ui.Constants; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.Files; +import java.nio.file.FileAlreadyExistsException; +import java.util.List; + +public class FileHandler { + + /** + * Reads existing file or creates new one when absent. + * + * @return lines read from file + * @throws IOException When I/O error occurs. + */ + protected static List readFile() throws IOException { + Path textFile = Paths.get(Constants.FILEPATH); + createDirectories(textFile.getParent()); + if (!Files.exists(textFile)) { + Files.createFile(textFile); + } + return Files.readAllLines(textFile); + } + + /** + * Writes tasks details to an existing file. + * + * @param entries Lines containing task details that will be written to the file. + * @throws IOException When I/O error occurs. + */ + protected static void writeFile(List entries) throws IOException { + Path textFile = Paths.get(Constants.FILEPATH); + createDirectories(textFile.getParent()); + + try (FileWriter fw = new FileWriter(Constants.FILEPATH)) { + for (String entry : entries) { + fw.write(entry + Constants.NEWLINE); + } + } catch (IOException e) { + System.out.println(Constants.FILEWRITEERROR + e.getMessage()); + throw e; + } + } + + protected static void createDirectories(Path directory) throws IOException { + try { + Files.createDirectories(directory); + } catch (FileAlreadyExistsException ignored) { + } catch (IOException e) { + System.out.println(Constants.CREATEDIRECTORYERROR + e.getMessage()); + throw e; + } + } +} diff --git a/src/main/java/utilities/file/FileReader.java b/src/main/java/utilities/file/FileReader.java new file mode 100644 index 000000000..dc3f80f5c --- /dev/null +++ b/src/main/java/utilities/file/FileReader.java @@ -0,0 +1,78 @@ +package utilities.file; + +import main.Aragorn; +import tasks.Deadline; +import tasks.Event; +import tasks.Task; +import tasks.ToDo; +import ui.Constants; +import utilities.commands.DisplayList; +import java.io.IOException; +import java.util.List; + +public class FileReader { + + /** + * Stores the reformatted entries read from the file into main Array list. + * + * @throws IOException When I/O error occurs. + */ + public static void readFile() throws IOException { + try { + List entries = FileHandler.readFile(); + Aragorn.getList().clear(); + + for (String entry : entries) { + Task task = formatEntry(entry); + Aragorn.getList().add(task); + } + + if (Aragorn.getList().isEmpty()) { + return; + } + + DisplayList.listCommand(Aragorn.getList()); + System.out.println(Constants.LINE); + } catch (IOException e) { + System.out.println(Constants.NOFILE); + } + } + + /** + * Splits the unformatted entry at the "|" characters to obtain + * formatted task details and stores them in a String Array. + * + * @param entry Unformatted entry read in from the file. + * @return Array containing the formatted task entry. + */ + private static Task formatEntry(String entry) { + Task newTask; + try { + String[] formattedEntry = entry.split(Constants.BAR); + String taskType = formattedEntry[0].toUpperCase(); + boolean done = formattedEntry[1].equals(Constants.ONE); + switch (taskType) { + case Constants.TODO: + newTask = new ToDo(formattedEntry[2], done); + + break; + + case Constants.DEADLINE: + newTask = new Deadline(formattedEntry[2], done, formattedEntry[3]); + break; + + case Constants.EVENT: + newTask = new Event(formattedEntry[2], done, formattedEntry[3], formattedEntry[4]); + break; + + default: + return Constants.taskTypeError(taskType); + } + + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(Constants.FILEERROR); + return null; + } + return newTask; + } +} diff --git a/src/main/java/utilities/file/FileWriter.java b/src/main/java/utilities/file/FileWriter.java new file mode 100644 index 000000000..6028e5833 --- /dev/null +++ b/src/main/java/utilities/file/FileWriter.java @@ -0,0 +1,26 @@ +package utilities.file; + +import main.Aragorn; +import tasks.Task; +import ui.Constants; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class FileWriter { + + /** + * Adds formatted entries separated by "|" to a list which is then written into the text file. + */ + public static void writeFile() { + try { + List formattedEntries = new ArrayList<>(); + for (Task newTask : Aragorn.getList()) { + formattedEntries.add(newTask.toFileString()); + } + FileHandler.writeFile(formattedEntries); + } catch (IOException e) { + System.out.println(Constants.FILEWRITEERROR + e.getMessage() + Constants.NEWLINE + Constants.LINE); + } + } +} diff --git a/src/main/java/utilities/parser/DeadlineParser.java b/src/main/java/utilities/parser/DeadlineParser.java new file mode 100644 index 000000000..3711ceaaa --- /dev/null +++ b/src/main/java/utilities/parser/DeadlineParser.java @@ -0,0 +1,40 @@ +package utilities.parser; + +import exceptions.AragornException; +import ui.Constants; + +public class DeadlineParser { + + private static final String[] splitInput = new String[3]; + + /** + * Splits the input into the deadline description and the deadline condition. + * + * @param userInput Input from the user containing the details of the deadline task. + */ + protected DeadlineParser (String userInput) { + String[] splitDeadline; + try { + splitDeadline = userInput.split(Constants.BYREGEX, 2); + splitInput[0] = splitDeadline[0].substring(8).trim(); + if (splitInput[0].trim().isEmpty()) { + throw new AragornException(Constants.EMPTYDESCRIPTION); + } + splitInput[1] = splitDeadline[1].trim(); + if (splitInput[1].trim().isEmpty()) { + throw new AragornException(Constants.EMPTYDEADLINE); + } + splitInput[2] = null; + } catch (AragornException e) { + System.out.println(e.getMessage()); + } catch (NullPointerException e) { + return; + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(Constants.INVALIDFORMAT); + } + } + + protected String[] getSplitInput() { + return splitInput; + } +} diff --git a/src/main/java/utilities/parser/EventParser.java b/src/main/java/utilities/parser/EventParser.java new file mode 100644 index 000000000..eab32d5bd --- /dev/null +++ b/src/main/java/utilities/parser/EventParser.java @@ -0,0 +1,46 @@ +package utilities.parser; + +import exceptions.AragornException; +import ui.Constants; + +public class EventParser { + + private static final String[] splitInput = new String[3]; + + /** + * Splits the input into the event description and the start condition and end condition. + * + * @param userInput Input from the user containing the details of the event task. + */ + protected EventParser (String userInput) { + String[] splitEvent; + String[] splitDeadline; + try { + splitDeadline = userInput.split(Constants.FROMREGEX, 2); + + splitInput[0] = splitDeadline[0].substring(5).trim(); + if (splitInput[0].trim().isEmpty()) { + throw new AragornException(Constants.EMPTYDESCRIPTION); + } + splitEvent = splitDeadline[1].split(Constants.TOREGEX, 2); + splitInput[1] = splitEvent[0].trim(); + if (splitInput[1].trim().isEmpty()) { + throw new AragornException(Constants.EMPTYEVENTSTART); + } + splitInput[2] = splitEvent[1].trim(); + if (splitInput[2].trim().isEmpty()) { + throw new AragornException(Constants.EMPTYEVENTEND); + } + } catch (AragornException e) { + System.out.println(e.getMessage()); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(Constants.INVALIDFORMAT); + } catch (NullPointerException e) { + return; + } + } + + protected String[] getSplitInput() { + return splitInput; + } +} diff --git a/src/main/java/utilities/parser/InputParser.java b/src/main/java/utilities/parser/InputParser.java new file mode 100644 index 000000000..64a7b1f3c --- /dev/null +++ b/src/main/java/utilities/parser/InputParser.java @@ -0,0 +1,53 @@ +package utilities.parser; + +import ui.Constants; + +public class InputParser { + private String[] splitInput = new String[3]; + + /** + * Parses the input into parts depending on the type of command. + * + * @param userInput Input from the user containing the details of the command. + * @param commandType The type of command inputted. + */ + public InputParser(String userInput, String commandType) { + switch (commandType) { + case Constants.MARK: + this.splitInput[0] = String.valueOf(Integer.parseInt(userInput.substring(5).trim()) - 1); + this.splitInput[1] = null; + this.splitInput[2] = null; + break; + + case Constants.UNMARK: + case Constants.DELETE: + this.splitInput[0] = String.valueOf(Integer.parseInt(userInput.substring(7).trim()) - 1); + this.splitInput[1] = null; + this.splitInput[2] = null; + break; + + case Constants.FIND: + this.splitInput[0] = userInput.substring(5).trim(); + this.splitInput[1] = null; + this.splitInput[2] = null; + break; + + case Constants.TODO: + this.splitInput = new ToDoParser(userInput).getSplitInput(); + break; + + case Constants.DEADLINE: + this.splitInput = new DeadlineParser(userInput).getSplitInput(); + break; + + case Constants.EVENT: + this.splitInput = new EventParser(userInput).getSplitInput(); + break; + } + } + + public String[] getSplitInput() { + return splitInput; + } + +} diff --git a/src/main/java/utilities/parser/ToDoParser.java b/src/main/java/utilities/parser/ToDoParser.java new file mode 100644 index 000000000..4054ba9d5 --- /dev/null +++ b/src/main/java/utilities/parser/ToDoParser.java @@ -0,0 +1,30 @@ +package utilities.parser; + +import exceptions.AragornException; +import ui.Constants; + +public class ToDoParser { + private static final String[] splitInput = new String[3]; + + /** + * Splits the input into the event description. + * + * @param userInput Input from the user containing the details of the todo task. + */ + protected ToDoParser (String userInput) { + try { + splitInput[0] = userInput.substring(4).trim(); + if (splitInput[0].trim().isEmpty()){ + throw new AragornException(Constants.EMPTYDESCRIPTION); + } + splitInput[1] = null; + splitInput[2] = null; + } catch (AragornException e) { + System.out.println(e.getMessage()); + } + } + + protected String[] getSplitInput() { + return splitInput; + } +}