From 39c0d765d607a2e7176a52d86fb7649d6a2bb4da Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Thu, 24 Aug 2023 23:03:37 +0800 Subject: [PATCH 01/35] Changed Duke to Martin --- src/main/java/Duke.java | 10 ---------- src/main/java/Martin.java | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 10 deletions(-) delete mode 100644 src/main/java/Duke.java create mode 100644 src/main/java/Martin.java diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334cc..0000000000 --- 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/Martin.java b/src/main/java/Martin.java new file mode 100644 index 0000000000..b55c8ba96f --- /dev/null +++ b/src/main/java/Martin.java @@ -0,0 +1,39 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +public class Martin { + private static List addList = new ArrayList<>(); + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + + printMessage("Hello! I'm Martin\n What can I do for you?"); + + while (true) { + String input = scanner.nextLine(); + if (input.equalsIgnoreCase("bye")) { + printMessage("Bye. Hope to see you again soon!"); + break; + } else if (input.equalsIgnoreCase("list")) { + printTasks(); + } else { + addList.add(input); + printMessage("added: " + input); + } + } + } + + private static void printMessage(String message) { + System.out.println(" ____________________________________________________________"); + System.out.println(" " + message); + System.out.println(" ____________________________________________________________"); + } + + private static void printTasks() { + System.out.println(" ____________________________________________________________"); + for (int i = 0; i < addList.size(); i++) { + System.out.println(" " + (i + 1) + ". " + addList.get(i)); + } + System.out.println(" ____________________________________________________________"); + } +} From 4d307d87d9dc2246865facccce42d567818c6357 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Thu, 24 Aug 2023 23:28:25 +0800 Subject: [PATCH 02/35] Added isDone Functionality and Task.java --- src/main/java/Martin.java | 42 +++++++++++++++++++++++++++++++++++---- src/main/java/Task.java | 30 ++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 src/main/java/Task.java diff --git a/src/main/java/Martin.java b/src/main/java/Martin.java index b55c8ba96f..e158b01ab6 100644 --- a/src/main/java/Martin.java +++ b/src/main/java/Martin.java @@ -3,7 +3,7 @@ import java.util.Scanner; public class Martin { - private static List addList = new ArrayList<>(); + private static List tasks = new ArrayList<>(); public static void main(String[] args) { Scanner scanner = new Scanner(System.in); @@ -16,8 +16,12 @@ public static void main(String[] args) { break; } else if (input.equalsIgnoreCase("list")) { printTasks(); + } else if (input.startsWith("mark")) { + markTask(input); + } else if (input.startsWith("unmark")) { + unmarkTask(input); } else { - addList.add(input); + tasks.add(new Task(input)); printMessage("added: " + input); } } @@ -31,9 +35,39 @@ private static void printMessage(String message) { private static void printTasks() { System.out.println(" ____________________________________________________________"); - for (int i = 0; i < addList.size(); i++) { - System.out.println(" " + (i + 1) + ". " + addList.get(i)); + for (int i = 0; i < tasks.size(); i++) { + System.out.println(" " + (i + 1) + ". " + tasks.get(i)); } System.out.println(" ____________________________________________________________"); } + + private static void markTask(String command) { + try { + int taskNo = Integer.parseInt(command.split(" ")[1]); + Task task = tasks.get(taskNo - 1); + if (task.isDone()) { + printMessage("Task \"" + task.getDescription() + "\" is already done."); + } else { + task.markAsDone(); + printMessage("Nice! I've marked this task as done:\n " + task); + } + } catch (Exception e) { + printMessage("Invalid task number."); + } + } + + private static void unmarkTask(String command) { + try { + int taskNo = Integer.parseInt(command.split(" ")[1]); + Task task = tasks.get(taskNo - 1); + if (!task.isDone()) { + printMessage("Task \"" + task.getDescription() + "\" is not done yet."); + } else { + task.unmarkAsDone(); + printMessage("OK, I've marked this task as not done yet:\n " + task); + } + } catch (Exception e) { + printMessage("Invalid task number."); + } + } } diff --git a/src/main/java/Task.java b/src/main/java/Task.java new file mode 100644 index 0000000000..7c9847ec08 --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,30 @@ +public class Task { + private String description; + private boolean isDone; + + public Task(String description) { + this.description = description; + this.isDone = false; + } + + public String getDescription() { + return description; + } + + public boolean isDone() { + return isDone; + } + + public void markAsDone() { + isDone = true; + } + + public void unmarkAsDone() { + isDone = false; + } + + @Override + public String toString() { + return "[" + (isDone ? "X" : " ") + "] " + description; + } +} \ No newline at end of file From 5a7a3116a4b1e93ccd043e004bb95be2cfc7f794 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Thu, 24 Aug 2023 23:43:56 +0800 Subject: [PATCH 03/35] Added Tracking for Tasks: Todos, Events, Deadlines --- src/main/java/Deadline.java | 13 +++++++++++++ src/main/java/Event.java | 15 +++++++++++++++ src/main/java/Martin.java | 16 ++++++++++++++-- src/main/java/Task.java | 4 ++-- src/main/java/Todo.java | 10 ++++++++++ 5 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 src/main/java/Deadline.java create mode 100644 src/main/java/Event.java create mode 100644 src/main/java/Todo.java diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java new file mode 100644 index 0000000000..e9e7b96a12 --- /dev/null +++ b/src/main/java/Deadline.java @@ -0,0 +1,13 @@ +public class Deadline extends Task { + protected String by; + + public Deadline(String description, String by) { + super(description); + this.by = by; + } + + @Override + public String toString() { + return "[D][" + (isDone ? "X" : " ") + "] " + description + " (by: " + by + ")"; + } +} \ No newline at end of file diff --git a/src/main/java/Event.java b/src/main/java/Event.java new file mode 100644 index 0000000000..e4cc99e527 --- /dev/null +++ b/src/main/java/Event.java @@ -0,0 +1,15 @@ +public class Event extends Task { + protected String from; + protected String to; + + public Event(String description, String from, String to) { + super(description); + this.from = from; + this.to = to; + } + + @Override + public String toString() { + return "[E][" + (isDone ? "X" : " ") + "] " + description + " (from: " + from + " to: " + to + ")"; + } +} \ No newline at end of file diff --git a/src/main/java/Martin.java b/src/main/java/Martin.java index e158b01ab6..a3c775463b 100644 --- a/src/main/java/Martin.java +++ b/src/main/java/Martin.java @@ -20,9 +20,21 @@ public static void main(String[] args) { markTask(input); } else if (input.startsWith("unmark")) { unmarkTask(input); + } else if (input.startsWith("todo")) { + String description = input.substring(5); + tasks.add(new Todo(description)); + printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); + } else if (input.startsWith("deadline")) { + String[] parts = input.substring(9).split(" /by "); + tasks.add(new Deadline(parts[0], parts[1])); + printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); + } else if (input.startsWith("event")) { + String[] parts = input.substring(6).split(" /from "); + String[] timeParts = parts[1].split(" /to "); + tasks.add(new Event(parts[0], timeParts[0], timeParts[1])); + printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); } else { - tasks.add(new Task(input)); - printMessage("added: " + input); + printMessage("Please enter a valid command."); } } } diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 7c9847ec08..fa2ecb91bf 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -1,6 +1,6 @@ public class Task { - private String description; - private boolean isDone; + protected String description; + protected boolean isDone; public Task(String description) { this.description = description; diff --git a/src/main/java/Todo.java b/src/main/java/Todo.java new file mode 100644 index 0000000000..355252cf6c --- /dev/null +++ b/src/main/java/Todo.java @@ -0,0 +1,10 @@ +public class Todo extends Task { + public Todo(String description) { + super(description); + } + + @Override + public String toString() { + return "[T][" + (isDone ? "X" : " ") + "] " + description; + } +} \ No newline at end of file From 37a5502e1a638e9b88a2edd99e40403b2035d36d Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Thu, 24 Aug 2023 23:57:47 +0800 Subject: [PATCH 04/35] Abstraction of adding Task functions --- src/main/java/Martin.java | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/main/java/Martin.java b/src/main/java/Martin.java index a3c775463b..fbf9d5114e 100644 --- a/src/main/java/Martin.java +++ b/src/main/java/Martin.java @@ -21,18 +21,11 @@ public static void main(String[] args) { } else if (input.startsWith("unmark")) { unmarkTask(input); } else if (input.startsWith("todo")) { - String description = input.substring(5); - tasks.add(new Todo(description)); - printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); + addTodo(input); } else if (input.startsWith("deadline")) { - String[] parts = input.substring(9).split(" /by "); - tasks.add(new Deadline(parts[0], parts[1])); - printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); + addDeadline(input); } else if (input.startsWith("event")) { - String[] parts = input.substring(6).split(" /from "); - String[] timeParts = parts[1].split(" /to "); - tasks.add(new Event(parts[0], timeParts[0], timeParts[1])); - printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); + addEvent(input); } else { printMessage("Please enter a valid command."); } @@ -82,4 +75,23 @@ private static void unmarkTask(String command) { printMessage("Invalid task number."); } } + + private static void addTodo(String command) { + String description = command.substring(5); + tasks.add(new Todo(description)); + printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); + } + + private static void addDeadline(String command) { + String[] parts = command.substring(9).split(" /by "); + tasks.add(new Deadline(parts[0], parts[1])); + printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); + } + + private static void addEvent(String command) { + String[] parts = command.substring(6).split(" /from "); + String[] timeParts = parts[1].split(" /to "); + tasks.add(new Event(parts[0], timeParts[0], timeParts[1])); + printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); + } } From 7f325761052bda121c5f8ce6f7dcf790a3b8aedd Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Fri, 25 Aug 2023 00:32:37 +0800 Subject: [PATCH 05/35] Edited Duke to Martin in runtest files --- text-ui-test/runtest.bat | 2 +- text-ui-test/runtest.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 0873744649..0b58ac029d 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -15,7 +15,7 @@ IF ERRORLEVEL 1 ( REM no error here, errorlevel == 0 REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ..\bin Duke < input.txt > ACTUAL.TXT +java -classpath ..\bin Martin < input.txt > ACTUAL.TXT REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index c9ec870033..a9070b0b01 100644 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -20,7 +20,7 @@ then fi # run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ../bin Duke < input.txt > ACTUAL.TXT +java -classpath ../bin Martin < input.txt > ACTUAL.TXT # convert to UNIX format cp EXPECTED.TXT EXPECTED-UNIX.TXT From 1228c38f6ae00d83e3f30aedbe3b46359dc9bbf8 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Fri, 25 Aug 2023 00:33:40 +0800 Subject: [PATCH 06/35] Added Exceptions for empty tasks descriptions, invalid commands, invalid task numbers and already marked/unmarked tasks --- src/main/java/Martin.java | 95 ++++++++++++------- .../EmptyTaskDescriptionException.java | 7 ++ .../exceptions/InvalidCommandException.java | 7 ++ .../InvalidTaskNumberException.java | 7 ++ .../exceptions/TaskAlreadyDoneException.java | 7 ++ .../java/exceptions/TaskNotDoneException.java | 7 ++ 6 files changed, 97 insertions(+), 33 deletions(-) create mode 100644 src/main/java/exceptions/EmptyTaskDescriptionException.java create mode 100644 src/main/java/exceptions/InvalidCommandException.java create mode 100644 src/main/java/exceptions/InvalidTaskNumberException.java create mode 100644 src/main/java/exceptions/TaskAlreadyDoneException.java create mode 100644 src/main/java/exceptions/TaskNotDoneException.java diff --git a/src/main/java/Martin.java b/src/main/java/Martin.java index fbf9d5114e..000ad5719a 100644 --- a/src/main/java/Martin.java +++ b/src/main/java/Martin.java @@ -1,33 +1,38 @@ import java.util.ArrayList; import java.util.List; import java.util.Scanner; +import exceptions.*; public class Martin { private static List tasks = new ArrayList<>(); - public static void main(String[] args) { + public static void main(String[] args) throws InvalidCommandException, EmptyTaskDescriptionException { Scanner scanner = new Scanner(System.in); printMessage("Hello! I'm Martin\n What can I do for you?"); while (true) { String input = scanner.nextLine(); - if (input.equalsIgnoreCase("bye")) { - printMessage("Bye. Hope to see you again soon!"); - break; - } else if (input.equalsIgnoreCase("list")) { - printTasks(); - } else if (input.startsWith("mark")) { - markTask(input); - } else if (input.startsWith("unmark")) { - unmarkTask(input); - } else if (input.startsWith("todo")) { - addTodo(input); - } else if (input.startsWith("deadline")) { - addDeadline(input); - } else if (input.startsWith("event")) { - addEvent(input); - } else { - printMessage("Please enter a valid command."); + try { + if (input.equalsIgnoreCase("bye")) { + printMessage("Bye. Hope to see you again soon!"); + break; + } else if (input.equalsIgnoreCase("list")) { + printTasks(); + } else if (input.startsWith("mark")) { + markTask(input); + } else if (input.startsWith("unmark")) { + unmarkTask(input); + } else if (input.startsWith("todo")) { + addTodo(input); + } else if (input.startsWith("deadline")) { + addDeadline(input); + } else if (input.startsWith("event")) { + addEvent(input); + } else { + throw new InvalidCommandException("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); + } + } catch (Exception e) { + printMessage(e.getMessage()); } } } @@ -46,51 +51,75 @@ private static void printTasks() { System.out.println(" ____________________________________________________________"); } - private static void markTask(String command) { + private static void markTask(String command) throws InvalidTaskNumberException, TaskAlreadyDoneException { try { int taskNo = Integer.parseInt(command.split(" ")[1]); + if (taskNo <= 0 || taskNo > tasks.size()) { + throw new InvalidTaskNumberException("Invalid task number."); + } + Task task = tasks.get(taskNo - 1); if (task.isDone()) { - printMessage("Task \"" + task.getDescription() + "\" is already done."); - } else { - task.markAsDone(); - printMessage("Nice! I've marked this task as done:\n " + task); + throw new TaskAlreadyDoneException("Task \"" + task.getDescription() + "\" is already done."); } + + task.markAsDone(); + printMessage("Nice! I've marked this task as done:\n " + task); } catch (Exception e) { - printMessage("Invalid task number."); + printMessage(e.getMessage()); } } - private static void unmarkTask(String command) { + private static void unmarkTask(String command) throws InvalidTaskNumberException, TaskNotDoneException{ try { int taskNo = Integer.parseInt(command.split(" ")[1]); + if (taskNo <= 0 || taskNo > tasks.size()) { + throw new InvalidTaskNumberException("Invalid task number."); + } + Task task = tasks.get(taskNo - 1); if (!task.isDone()) { - printMessage("Task \"" + task.getDescription() + "\" is not done yet."); - } else { - task.unmarkAsDone(); - printMessage("OK, I've marked this task as not done yet:\n " + task); + throw new TaskNotDoneException("Task \"" + task.getDescription() + "\" is not done yet."); } + + task.unmarkAsDone(); + printMessage("OK, I've marked this task as not done yet:\n " + task); } catch (Exception e) { - printMessage("Invalid task number."); + printMessage(e.getMessage()); } } - private static void addTodo(String command) { + private static void addTodo(String command) throws EmptyTaskDescriptionException { + if (command.length() <= 4) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a todo cannot be empty."); + } + String description = command.substring(5); + if (description.isEmpty()) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a todo cannot be empty."); + } + tasks.add(new Todo(description)); printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); } - private static void addDeadline(String command) { + private static void addDeadline(String command) throws EmptyTaskDescriptionException { String[] parts = command.substring(9).split(" /by "); + if (parts.length < 2 || parts[0].trim().isEmpty() || parts[1].trim().isEmpty()) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a deadline or its date cannot be empty."); + } + tasks.add(new Deadline(parts[0], parts[1])); printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); } - private static void addEvent(String command) { + private static void addEvent(String command) throws EmptyTaskDescriptionException { String[] parts = command.substring(6).split(" /from "); String[] timeParts = parts[1].split(" /to "); + if (parts.length < 2 || parts[0].trim().isEmpty() || timeParts.length < 2 || timeParts[0].trim().isEmpty() || timeParts[1].trim().isEmpty()) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of an event or its time cannot be empty."); + } + tasks.add(new Event(parts[0], timeParts[0], timeParts[1])); printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); } diff --git a/src/main/java/exceptions/EmptyTaskDescriptionException.java b/src/main/java/exceptions/EmptyTaskDescriptionException.java new file mode 100644 index 0000000000..611a17555e --- /dev/null +++ b/src/main/java/exceptions/EmptyTaskDescriptionException.java @@ -0,0 +1,7 @@ +package exceptions; + +public class EmptyTaskDescriptionException extends Exception { + public EmptyTaskDescriptionException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/exceptions/InvalidCommandException.java b/src/main/java/exceptions/InvalidCommandException.java new file mode 100644 index 0000000000..465809084f --- /dev/null +++ b/src/main/java/exceptions/InvalidCommandException.java @@ -0,0 +1,7 @@ +package exceptions; + +public class InvalidCommandException extends Exception { + public InvalidCommandException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/exceptions/InvalidTaskNumberException.java b/src/main/java/exceptions/InvalidTaskNumberException.java new file mode 100644 index 0000000000..1babc3b009 --- /dev/null +++ b/src/main/java/exceptions/InvalidTaskNumberException.java @@ -0,0 +1,7 @@ +package exceptions; + +public class InvalidTaskNumberException extends Exception { + public InvalidTaskNumberException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/exceptions/TaskAlreadyDoneException.java b/src/main/java/exceptions/TaskAlreadyDoneException.java new file mode 100644 index 0000000000..47e0c26b3c --- /dev/null +++ b/src/main/java/exceptions/TaskAlreadyDoneException.java @@ -0,0 +1,7 @@ +package exceptions; + +public class TaskAlreadyDoneException extends Exception { + public TaskAlreadyDoneException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/exceptions/TaskNotDoneException.java b/src/main/java/exceptions/TaskNotDoneException.java new file mode 100644 index 0000000000..8bd20ada9d --- /dev/null +++ b/src/main/java/exceptions/TaskNotDoneException.java @@ -0,0 +1,7 @@ +package exceptions; + +public class TaskNotDoneException extends Exception { + public TaskNotDoneException(String message) { + super(message); + } +} \ No newline at end of file From a46f9a81d487b4663c871948fcdf349d66dff753 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Fri, 25 Aug 2023 00:43:43 +0800 Subject: [PATCH 07/35] Added Delete Function for tasks --- src/main/java/Martin.java | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main/java/Martin.java b/src/main/java/Martin.java index 000ad5719a..dddd526b36 100644 --- a/src/main/java/Martin.java +++ b/src/main/java/Martin.java @@ -1,10 +1,9 @@ import java.util.ArrayList; -import java.util.List; import java.util.Scanner; import exceptions.*; public class Martin { - private static List tasks = new ArrayList<>(); + private static ArrayList tasks = new ArrayList<>(); public static void main(String[] args) throws InvalidCommandException, EmptyTaskDescriptionException { Scanner scanner = new Scanner(System.in); @@ -18,6 +17,8 @@ public static void main(String[] args) throws InvalidCommandException, EmptyTask break; } else if (input.equalsIgnoreCase("list")) { printTasks(); + } else if (input.startsWith("delete")) { + deleteTask(input); } else if (input.startsWith("mark")) { markTask(input); } else if (input.startsWith("unmark")) { @@ -51,6 +52,21 @@ private static void printTasks() { System.out.println(" ____________________________________________________________"); } + private static void deleteTask(String command) throws InvalidTaskNumberException { + try { + int taskNo = Integer.parseInt(command.split(" ")[1]); + + if (taskNo <= 0 || taskNo > tasks.size()) { + throw new InvalidTaskNumberException("Invalid task number."); + } + + Task removedTask = tasks.remove(taskNo - 1); + printMessage("Noted. I've removed this task:\n " + removedTask + "\n Now you have " + tasks.size() + " tasks in the list."); + } catch (Exception e) { + throw new InvalidTaskNumberException("Invalid task number."); + } + } + private static void markTask(String command) throws InvalidTaskNumberException, TaskAlreadyDoneException { try { int taskNo = Integer.parseInt(command.split(" ")[1]); @@ -100,7 +116,7 @@ private static void addTodo(String command) throws EmptyTaskDescriptionException } tasks.add(new Todo(description)); - printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); + printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); } private static void addDeadline(String command) throws EmptyTaskDescriptionException { @@ -110,7 +126,7 @@ private static void addDeadline(String command) throws EmptyTaskDescriptionExcep } tasks.add(new Deadline(parts[0], parts[1])); - printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); + printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); } private static void addEvent(String command) throws EmptyTaskDescriptionException { @@ -121,6 +137,6 @@ private static void addEvent(String command) throws EmptyTaskDescriptionExceptio } tasks.add(new Event(parts[0], timeParts[0], timeParts[1])); - printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\nNow you have " + tasks.size() + " tasks in the list."); + printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); } } From b47fcbfceda79d68eb1c2fdc03e02f58b5ff1481 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sat, 2 Sep 2023 22:20:33 +0800 Subject: [PATCH 08/35] Added saving functionality, tasks are saved and remembered --- data/martin.txt | 0 src/main/java/Martin.java | 52 ++++++++++++++++++++++++++++++++++++++- src/main/java/Task.java | 30 ++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 data/martin.txt diff --git a/data/martin.txt b/data/martin.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/Martin.java b/src/main/java/Martin.java index dddd526b36..a7b3d9bdc1 100644 --- a/src/main/java/Martin.java +++ b/src/main/java/Martin.java @@ -1,14 +1,22 @@ import java.util.ArrayList; +import java.util.List; import java.util.Scanner; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.io.IOException; import exceptions.*; public class Martin { private static ArrayList tasks = new ArrayList<>(); public static void main(String[] args) throws InvalidCommandException, EmptyTaskDescriptionException { + loadFromFile(); + Scanner scanner = new Scanner(System.in); printMessage("Hello! I'm Martin\n What can I do for you?"); - + while (true) { String input = scanner.nextLine(); try { @@ -19,16 +27,22 @@ public static void main(String[] args) throws InvalidCommandException, EmptyTask printTasks(); } else if (input.startsWith("delete")) { deleteTask(input); + saveToFile(); } else if (input.startsWith("mark")) { markTask(input); + saveToFile(); } else if (input.startsWith("unmark")) { unmarkTask(input); + saveToFile(); } else if (input.startsWith("todo")) { addTodo(input); + saveToFile(); } else if (input.startsWith("deadline")) { addDeadline(input); + saveToFile(); } else if (input.startsWith("event")) { addEvent(input); + saveToFile(); } else { throw new InvalidCommandException("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); } @@ -139,4 +153,40 @@ private static void addEvent(String command) throws EmptyTaskDescriptionExceptio tasks.add(new Event(parts[0], timeParts[0], timeParts[1])); printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); } + + public static void saveToFile() { + List lines = new ArrayList<>(); + for (Task task : tasks) { + lines.add(task.toFileFormat()); + } + + Path path = Paths.get("./data/martin.txt"); + try { + if (!Files.exists(path)) { + Files.createDirectories(path.getParent()); + Files.createFile(path); + } + Files.write(path, lines, StandardOpenOption.TRUNCATE_EXISTING); + } catch (IOException e) { + printMessage("Error saving tasks to file."); + } + } + + public static void loadFromFile() { + Path path = Paths.get("./data/martin.txt"); + if (Files.exists(path)) { + try { + List lines = Files.readAllLines(path); + for (String line : lines) { + try { + tasks.add(Task.fromFileFormat(line)); + } catch (IllegalArgumentException e) { + printMessage("Data file might be corrupted. (i.e., content not in expected format.)"); + } + } + } catch (IOException e) { + printMessage("Error reading tasks from file."); + } + } + } } diff --git a/src/main/java/Task.java b/src/main/java/Task.java index fa2ecb91bf..ce5f747a52 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -23,6 +23,36 @@ public void unmarkAsDone() { isDone = false; } + /** + * Convert the task to its file format representation. + * @return String representation of task for file storage. + */ + public String toFileFormat() { + return (isDone ? "1" : "0") + " | " + description; + } + + /** + * Convert a file format string back to a Task. + * @param fileFormatString String representation from file. + * @return A new Task instance. + */ + public static Task fromFileFormat(String fileFormatString) { + String[] parts = fileFormatString.split("\\s*\\|\\s*"); + + if (parts.length != 2) { + throw new IllegalArgumentException("Invalid task format in file."); + } + + boolean isDone = "1".equals(parts[0]); + String description = parts[1]; + Task task = new Task(description); + if (isDone) { + task.markAsDone(); + } + + return task; + } + @Override public String toString() { return "[" + (isDone ? "X" : " ") + "] " + description; From 380082a58a14695c72e6ac65879d2a519c79257d Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sat, 2 Sep 2023 22:26:54 +0800 Subject: [PATCH 09/35] Removed wildcard import and added header messages to functions --- src/main/java/Martin.java | 43 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/main/java/Martin.java b/src/main/java/Martin.java index a7b3d9bdc1..93d8d6abea 100644 --- a/src/main/java/Martin.java +++ b/src/main/java/Martin.java @@ -6,7 +6,11 @@ import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.io.IOException; -import exceptions.*; +import exceptions.EmptyTaskDescriptionException; +import exceptions.InvalidCommandException; +import exceptions.InvalidTaskNumberException; +import exceptions.TaskAlreadyDoneException; +import exceptions.TaskNotDoneException; public class Martin { private static ArrayList tasks = new ArrayList<>(); @@ -52,12 +56,19 @@ public static void main(String[] args) throws InvalidCommandException, EmptyTask } } + /** + * Displays the given message within a framed UI box. + * @param message The message to be displayed. + */ private static void printMessage(String message) { System.out.println(" ____________________________________________________________"); System.out.println(" " + message); System.out.println(" ____________________________________________________________"); } + /** + * Prints all tasks currently in the list. + */ private static void printTasks() { System.out.println(" ____________________________________________________________"); for (int i = 0; i < tasks.size(); i++) { @@ -66,6 +77,10 @@ private static void printTasks() { System.out.println(" ____________________________________________________________"); } + /** + * Deletes the task at the specified index provided by the command. + * @param command The user input containing the task index to delete. + */ private static void deleteTask(String command) throws InvalidTaskNumberException { try { int taskNo = Integer.parseInt(command.split(" ")[1]); @@ -81,6 +96,10 @@ private static void deleteTask(String command) throws InvalidTaskNumberException } } + /** + * Marks the task at the specified index as done. + * @param command The user input containing the task index to mark. + */ private static void markTask(String command) throws InvalidTaskNumberException, TaskAlreadyDoneException { try { int taskNo = Integer.parseInt(command.split(" ")[1]); @@ -100,6 +119,10 @@ private static void markTask(String command) throws InvalidTaskNumberException, } } + /** + * Unmarks the task at the specified index, marking it as not done. + * @param command The user input containing the task index to unmark. + */ private static void unmarkTask(String command) throws InvalidTaskNumberException, TaskNotDoneException{ try { int taskNo = Integer.parseInt(command.split(" ")[1]); @@ -119,6 +142,10 @@ private static void unmarkTask(String command) throws InvalidTaskNumberException } } + /** + * Adds a new ToDo task to the task list. + * @param command The user input containing the task description. + */ private static void addTodo(String command) throws EmptyTaskDescriptionException { if (command.length() <= 4) { throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a todo cannot be empty."); @@ -133,6 +160,10 @@ private static void addTodo(String command) throws EmptyTaskDescriptionException printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); } + /** + * Adds a new Deadline task to the task list. + * @param command The user input containing the task description and its deadline. + */ private static void addDeadline(String command) throws EmptyTaskDescriptionException { String[] parts = command.substring(9).split(" /by "); if (parts.length < 2 || parts[0].trim().isEmpty() || parts[1].trim().isEmpty()) { @@ -143,6 +174,10 @@ private static void addDeadline(String command) throws EmptyTaskDescriptionExcep printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); } + /** + * Adds a new Event task to the task list. + * @param command The user input containing the event details. + */ private static void addEvent(String command) throws EmptyTaskDescriptionException { String[] parts = command.substring(6).split(" /from "); String[] timeParts = parts[1].split(" /to "); @@ -154,6 +189,9 @@ private static void addEvent(String command) throws EmptyTaskDescriptionExceptio printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); } + /** + * Saves all tasks in the list to an external file for persistence. + */ public static void saveToFile() { List lines = new ArrayList<>(); for (Task task : tasks) { @@ -172,6 +210,9 @@ public static void saveToFile() { } } + /** + * Loads tasks from an external file into the program on startup. + */ public static void loadFromFile() { Path path = Paths.get("./data/martin.txt"); if (Files.exists(path)) { From 3a1360fcfd3d6f9880a6292df65ed54620ad6588 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 3 Sep 2023 00:40:51 +0800 Subject: [PATCH 10/35] Added Date and Time formatting to Deadline class, also Added date function in main --- data/martin.txt | 2 + src/main/java/Deadline.java | 23 ++++++-- src/main/java/Martin.java | 54 +++++++++++++++++++ src/main/java/Task.java | 48 +++++++++++++---- .../InvalidDateFormatException.java | 7 +++ 5 files changed, 120 insertions(+), 14 deletions(-) create mode 100644 src/main/java/exceptions/InvalidDateFormatException.java diff --git a/data/martin.txt b/data/martin.txt index e69de29bb2..1e3dab1ba2 100644 --- a/data/martin.txt +++ b/data/martin.txt @@ -0,0 +1,2 @@ +1 | walk dog +D | 1 | return book | 02/12/2019 1800 diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index e9e7b96a12..3ad7c6b798 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -1,13 +1,30 @@ +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + public class Deadline extends Task { - protected String by; + private LocalDateTime by; - public Deadline(String description, String by) { + public Deadline(String description, LocalDateTime by) { super(description); this.by = by; } + public Deadline(String description, String by) { + super(description); + this.by = LocalDateTime.parse(by, DateTimeFormatter.ofPattern("d/M/yyyy HHmm")); + } + + public LocalDateTime getBy() { + return by; + } + @Override public String toString() { - return "[D][" + (isDone ? "X" : " ") + "] " + description + " (by: " + by + ")"; + return "[D]" + super.toString() + " (by: " + by.format(DateTimeFormatter.ofPattern("MMM dd yyyy HH:mm")) + ")"; + } + + @Override + public String toFileFormat() { + return "D | " + (isDone() ? "1" : "0") + " | " + getDescription() + " | " + by.format(DateTimeFormatter.ofPattern("dd/MM/yyyy HHmm")); } } \ No newline at end of file diff --git a/src/main/java/Martin.java b/src/main/java/Martin.java index 93d8d6abea..17b2a59617 100644 --- a/src/main/java/Martin.java +++ b/src/main/java/Martin.java @@ -5,12 +5,16 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.io.IOException; import exceptions.EmptyTaskDescriptionException; import exceptions.InvalidCommandException; import exceptions.InvalidTaskNumberException; import exceptions.TaskAlreadyDoneException; import exceptions.TaskNotDoneException; +import exceptions.InvalidDateFormatException; public class Martin { private static ArrayList tasks = new ArrayList<>(); @@ -47,6 +51,8 @@ public static void main(String[] args) throws InvalidCommandException, EmptyTask } else if (input.startsWith("event")) { addEvent(input); saveToFile(); + } else if (input.startsWith("date")) { + date(input.substring(4).trim()); } else { throw new InvalidCommandException("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); } @@ -165,6 +171,10 @@ private static void addTodo(String command) throws EmptyTaskDescriptionException * @param command The user input containing the task description and its deadline. */ private static void addDeadline(String command) throws EmptyTaskDescriptionException { + if (command.length() <= 8) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a deadline cannot be empty."); + } + String[] parts = command.substring(9).split(" /by "); if (parts.length < 2 || parts[0].trim().isEmpty() || parts[1].trim().isEmpty()) { throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a deadline or its date cannot be empty."); @@ -179,6 +189,10 @@ private static void addDeadline(String command) throws EmptyTaskDescriptionExcep * @param command The user input containing the event details. */ private static void addEvent(String command) throws EmptyTaskDescriptionException { + if (command.length() <= 5) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of an event cannot be empty."); + } + String[] parts = command.substring(6).split(" /from "); String[] timeParts = parts[1].split(" /to "); if (parts.length < 2 || parts[0].trim().isEmpty() || timeParts.length < 2 || timeParts[0].trim().isEmpty() || timeParts[1].trim().isEmpty()) { @@ -189,6 +203,44 @@ private static void addEvent(String command) throws EmptyTaskDescriptionExceptio printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); } + /** + * Prints the tasks that are scheduled on a specific date. + * + * @param date The specific date in which to filter tasks. + */ + private static void date(String dateStr) throws InvalidDateFormatException { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yyyy"); + LocalDate date; + + try { + date = LocalDate.parse(dateStr, formatter); + } catch (DateTimeParseException e) { + throw new InvalidDateFormatException(" Invalid date format. Please use the format 'd/M/yyyy'."); + } + + System.out.println(" ____________________________________________________________"); + System.out.println(" Tasks on " + date.format(DateTimeFormatter.ofPattern("M d yyyy")) + ":"); + int count = 0; + boolean hasTasks = false; + + for (Task task : tasks) { + if (task instanceof Deadline) { + Deadline d = (Deadline) task; + if (d.getBy().toLocalDate().equals(date)) { + System.out.println(" " + (count + 1) + ". " + task); + hasTasks = true; + } + } + count++; + } + + if (!hasTasks) { + System.out.println(" No tasks on this date."); + } + + System.out.println(" ____________________________________________________________"); +} + /** * Saves all tasks in the list to an external file for persistence. */ @@ -219,6 +271,8 @@ public static void loadFromFile() { try { List lines = Files.readAllLines(path); for (String line : lines) { + if (line.trim().isEmpty()) + continue; try { tasks.add(Task.fromFileFormat(line)); } catch (IllegalArgumentException e) { diff --git a/src/main/java/Task.java b/src/main/java/Task.java index ce5f747a52..41ced023f5 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -1,3 +1,6 @@ +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + public class Task { protected String description; protected boolean isDone; @@ -38,21 +41,44 @@ public String toFileFormat() { */ public static Task fromFileFormat(String fileFormatString) { String[] parts = fileFormatString.split("\\s*\\|\\s*"); - - if (parts.length != 2) { - throw new IllegalArgumentException("Invalid task format in file."); - } + + String type = parts[0]; + + switch (type) { - boolean isDone = "1".equals(parts[0]); - String description = parts[1]; - Task task = new Task(description); - if (isDone) { - task.markAsDone(); - } + case "D": // Deadline tasks + if (parts.length != 4) { + throw new IllegalArgumentException("Invalid Deadline format in file."); + } + + boolean isDoneDeadline = "1".equals(parts[1]); + String descriptionDeadline = parts[2]; + LocalDateTime by = LocalDateTime.parse(parts[3], DateTimeFormatter.ofPattern("dd/MM/yyyy HHmm")); + + Deadline deadline = new Deadline(descriptionDeadline, by); + if (isDoneDeadline) { + deadline.markAsDone(); + } - return task; + return deadline; + + default: + if (parts.length != 2) { + throw new IllegalArgumentException("Invalid task format in file."); + } + + boolean isDone = "1".equals(parts[0]); + String description = parts[1]; + Task task = new Task(description); + if (isDone) { + task.markAsDone(); + } + + return task; + } } + @Override public String toString() { return "[" + (isDone ? "X" : " ") + "] " + description; diff --git a/src/main/java/exceptions/InvalidDateFormatException.java b/src/main/java/exceptions/InvalidDateFormatException.java new file mode 100644 index 0000000000..39cd32e4dd --- /dev/null +++ b/src/main/java/exceptions/InvalidDateFormatException.java @@ -0,0 +1,7 @@ +package exceptions; + +public class InvalidDateFormatException extends Exception { + public InvalidDateFormatException(String message) { + super(message); + } +} \ No newline at end of file From 0930fb37775c0b635dfafcf75e3ce03b08107755 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 3 Sep 2023 01:44:34 +0800 Subject: [PATCH 11/35] Created Parser, Storage, UI and TaskList classes for better OOP --- data/martin.txt | 3 +- src/main/java/Martin.java | 302 ++++-------------------------------- src/main/java/Parser.java | 65 ++++++++ src/main/java/Storage.java | 70 +++++++++ src/main/java/TaskList.java | 204 ++++++++++++++++++++++++ src/main/java/Ui.java | 36 +++++ 6 files changed, 404 insertions(+), 276 deletions(-) create mode 100644 src/main/java/Parser.java create mode 100644 src/main/java/Storage.java create mode 100644 src/main/java/TaskList.java create mode 100644 src/main/java/Ui.java diff --git a/data/martin.txt b/data/martin.txt index 1e3dab1ba2..384a160491 100644 --- a/data/martin.txt +++ b/data/martin.txt @@ -1,2 +1 @@ -1 | walk dog -D | 1 | return book | 02/12/2019 1800 +0 | test diff --git a/src/main/java/Martin.java b/src/main/java/Martin.java index 17b2a59617..cfc3319aec 100644 --- a/src/main/java/Martin.java +++ b/src/main/java/Martin.java @@ -1,287 +1,41 @@ -import java.util.ArrayList; -import java.util.List; -import java.util.Scanner; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.io.IOException; -import exceptions.EmptyTaskDescriptionException; -import exceptions.InvalidCommandException; -import exceptions.InvalidTaskNumberException; -import exceptions.TaskAlreadyDoneException; -import exceptions.TaskNotDoneException; -import exceptions.InvalidDateFormatException; - public class Martin { - private static ArrayList tasks = new ArrayList<>(); - public static void main(String[] args) throws InvalidCommandException, EmptyTaskDescriptionException { - loadFromFile(); - - Scanner scanner = new Scanner(System.in); - - printMessage("Hello! I'm Martin\n What can I do for you?"); - - while (true) { - String input = scanner.nextLine(); - try { - if (input.equalsIgnoreCase("bye")) { - printMessage("Bye. Hope to see you again soon!"); - break; - } else if (input.equalsIgnoreCase("list")) { - printTasks(); - } else if (input.startsWith("delete")) { - deleteTask(input); - saveToFile(); - } else if (input.startsWith("mark")) { - markTask(input); - saveToFile(); - } else if (input.startsWith("unmark")) { - unmarkTask(input); - saveToFile(); - } else if (input.startsWith("todo")) { - addTodo(input); - saveToFile(); - } else if (input.startsWith("deadline")) { - addDeadline(input); - saveToFile(); - } else if (input.startsWith("event")) { - addEvent(input); - saveToFile(); - } else if (input.startsWith("date")) { - date(input.substring(4).trim()); - } else { - throw new InvalidCommandException("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); - } - } catch (Exception e) { - printMessage(e.getMessage()); - } - } - } - - /** - * Displays the given message within a framed UI box. - * @param message The message to be displayed. - */ - private static void printMessage(String message) { - System.out.println(" ____________________________________________________________"); - System.out.println(" " + message); - System.out.println(" ____________________________________________________________"); - } - - /** - * Prints all tasks currently in the list. - */ - private static void printTasks() { - System.out.println(" ____________________________________________________________"); - for (int i = 0; i < tasks.size(); i++) { - System.out.println(" " + (i + 1) + ". " + tasks.get(i)); - } - System.out.println(" ____________________________________________________________"); - } - - /** - * Deletes the task at the specified index provided by the command. - * @param command The user input containing the task index to delete. - */ - private static void deleteTask(String command) throws InvalidTaskNumberException { + private Storage storage; + private TaskList tasks; + private Ui ui; + private Parser parser; + + public Martin(String filePath) { + ui = new Ui(); + storage = new Storage(filePath); try { - int taskNo = Integer.parseInt(command.split(" ")[1]); - - if (taskNo <= 0 || taskNo > tasks.size()) { - throw new InvalidTaskNumberException("Invalid task number."); - } - - Task removedTask = tasks.remove(taskNo - 1); - printMessage("Noted. I've removed this task:\n " + removedTask + "\n Now you have " + tasks.size() + " tasks in the list."); + tasks = new TaskList(storage.loadFromFile()); + parser = new Parser(tasks); } catch (Exception e) { - throw new InvalidTaskNumberException("Invalid task number."); + ui.showLoadingError(); + tasks = new TaskList(); } } - /** - * Marks the task at the specified index as done. - * @param command The user input containing the task index to mark. - */ - private static void markTask(String command) throws InvalidTaskNumberException, TaskAlreadyDoneException { - try { - int taskNo = Integer.parseInt(command.split(" ")[1]); - if (taskNo <= 0 || taskNo > tasks.size()) { - throw new InvalidTaskNumberException("Invalid task number."); - } - - Task task = tasks.get(taskNo - 1); - if (task.isDone()) { - throw new TaskAlreadyDoneException("Task \"" + task.getDescription() + "\" is already done."); - } - - task.markAsDone(); - printMessage("Nice! I've marked this task as done:\n " + task); - } catch (Exception e) { - printMessage(e.getMessage()); - } - } - - /** - * Unmarks the task at the specified index, marking it as not done. - * @param command The user input containing the task index to unmark. - */ - private static void unmarkTask(String command) throws InvalidTaskNumberException, TaskNotDoneException{ - try { - int taskNo = Integer.parseInt(command.split(" ")[1]); - if (taskNo <= 0 || taskNo > tasks.size()) { - throw new InvalidTaskNumberException("Invalid task number."); - } + public void run() { + ui.showWelcome(); + boolean isExit = false; + while (!isExit) { + try { + String command = ui.readCommand(); + parser.parse(command); - Task task = tasks.get(taskNo - 1); - if (!task.isDone()) { - throw new TaskNotDoneException("Task \"" + task.getDescription() + "\" is not done yet."); + if ("bye".equalsIgnoreCase(command.trim())) { + isExit = true; + } + } catch (Exception e) { + ui.showError(e.getMessage()); + } finally { + storage.saveToFile(tasks.getTasks()); } - - task.unmarkAsDone(); - printMessage("OK, I've marked this task as not done yet:\n " + task); - } catch (Exception e) { - printMessage(e.getMessage()); } } - - /** - * Adds a new ToDo task to the task list. - * @param command The user input containing the task description. - */ - private static void addTodo(String command) throws EmptyTaskDescriptionException { - if (command.length() <= 4) { - throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a todo cannot be empty."); - } - - String description = command.substring(5); - if (description.isEmpty()) { - throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a todo cannot be empty."); - } - - tasks.add(new Todo(description)); - printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); - } - - /** - * Adds a new Deadline task to the task list. - * @param command The user input containing the task description and its deadline. - */ - private static void addDeadline(String command) throws EmptyTaskDescriptionException { - if (command.length() <= 8) { - throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a deadline cannot be empty."); - } - - String[] parts = command.substring(9).split(" /by "); - if (parts.length < 2 || parts[0].trim().isEmpty() || parts[1].trim().isEmpty()) { - throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a deadline or its date cannot be empty."); - } - - tasks.add(new Deadline(parts[0], parts[1])); - printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); - } - - /** - * Adds a new Event task to the task list. - * @param command The user input containing the event details. - */ - private static void addEvent(String command) throws EmptyTaskDescriptionException { - if (command.length() <= 5) { - throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of an event cannot be empty."); - } - - String[] parts = command.substring(6).split(" /from "); - String[] timeParts = parts[1].split(" /to "); - if (parts.length < 2 || parts[0].trim().isEmpty() || timeParts.length < 2 || timeParts[0].trim().isEmpty() || timeParts[1].trim().isEmpty()) { - throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of an event or its time cannot be empty."); - } - - tasks.add(new Event(parts[0], timeParts[0], timeParts[1])); - printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); - } - - /** - * Prints the tasks that are scheduled on a specific date. - * - * @param date The specific date in which to filter tasks. - */ - private static void date(String dateStr) throws InvalidDateFormatException { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yyyy"); - LocalDate date; - - try { - date = LocalDate.parse(dateStr, formatter); - } catch (DateTimeParseException e) { - throw new InvalidDateFormatException(" Invalid date format. Please use the format 'd/M/yyyy'."); - } - - System.out.println(" ____________________________________________________________"); - System.out.println(" Tasks on " + date.format(DateTimeFormatter.ofPattern("M d yyyy")) + ":"); - int count = 0; - boolean hasTasks = false; - - for (Task task : tasks) { - if (task instanceof Deadline) { - Deadline d = (Deadline) task; - if (d.getBy().toLocalDate().equals(date)) { - System.out.println(" " + (count + 1) + ". " + task); - hasTasks = true; - } - } - count++; - } - - if (!hasTasks) { - System.out.println(" No tasks on this date."); - } - - System.out.println(" ____________________________________________________________"); -} - /** - * Saves all tasks in the list to an external file for persistence. - */ - public static void saveToFile() { - List lines = new ArrayList<>(); - for (Task task : tasks) { - lines.add(task.toFileFormat()); - } - - Path path = Paths.get("./data/martin.txt"); - try { - if (!Files.exists(path)) { - Files.createDirectories(path.getParent()); - Files.createFile(path); - } - Files.write(path, lines, StandardOpenOption.TRUNCATE_EXISTING); - } catch (IOException e) { - printMessage("Error saving tasks to file."); - } + public static void main(String[] args) { + new Martin("data/martin.txt").run(); } - - /** - * Loads tasks from an external file into the program on startup. - */ - public static void loadFromFile() { - Path path = Paths.get("./data/martin.txt"); - if (Files.exists(path)) { - try { - List lines = Files.readAllLines(path); - for (String line : lines) { - if (line.trim().isEmpty()) - continue; - try { - tasks.add(Task.fromFileFormat(line)); - } catch (IllegalArgumentException e) { - printMessage("Data file might be corrupted. (i.e., content not in expected format.)"); - } - } - } catch (IOException e) { - printMessage("Error reading tasks from file."); - } - } - } } diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java new file mode 100644 index 0000000000..60ba11167a --- /dev/null +++ b/src/main/java/Parser.java @@ -0,0 +1,65 @@ +import exceptions.EmptyTaskDescriptionException; +import exceptions.InvalidCommandException; +import exceptions.InvalidDateFormatException; +import exceptions.InvalidTaskNumberException; +import exceptions.TaskAlreadyDoneException; +import exceptions.TaskNotDoneException; + +public class Parser { + + private TaskList taskList; + + public Parser(TaskList taskList) { + this.taskList = taskList; + } + + /** + * Parses the user input and triggers the appropriate action on TaskList. + * @param input The entire string of the user's input. + * @throws InvalidTaskNumberException + * @throws TaskAlreadyDoneException + * @throws TaskNotDoneException + * @throws InvalidDateFormatException + * @throws DukeException When the command is invalid. + */ + public void parse(String input) throws InvalidCommandException, EmptyTaskDescriptionException, InvalidTaskNumberException, TaskAlreadyDoneException, TaskNotDoneException, InvalidDateFormatException { + if (input == null || input.trim().isEmpty()) { + throw new InvalidCommandException("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); + } + + String[] parts = input.split(" ", 2); + String command = parts[0].toLowerCase(); + + switch (command) { + case "bye": + taskList.bye(); + break; + case "list": + taskList.printTasks(); + break; + case "delete": + taskList.deleteTask(input); + break; + case "mark": + taskList.markTask(input); + break; + case "unmark": + taskList.unmarkTask(input); + break; + case "todo": + taskList.addTodo(input); + break; + case "deadline": + taskList.addDeadline(input); + break; + case "event": + taskList.addEvent(input); + break; + case "date": + taskList.eventsOndate(input); + break; + default: + throw new InvalidCommandException("Unknown command: " + input); + } + } +} diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java new file mode 100644 index 0000000000..28439462c0 --- /dev/null +++ b/src/main/java/Storage.java @@ -0,0 +1,70 @@ +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; + +public class Storage { + + private String filePath; + private Ui ui; + + public Storage(String filePath) { + this.filePath = filePath; + this.ui = new Ui(); + } + + /** + * Saves the given list of tasks to the file. + * + * @param tasks the list of tasks to save. + */ + public void saveToFile(List tasks) { + List lines = new ArrayList<>(); + for (Task task : tasks) { + lines.add(task.toFileFormat()); + } + + Path path = Paths.get(filePath); + try { + if (!Files.exists(path)) { + Files.createDirectories(path.getParent()); + Files.createFile(path); + } + Files.write(path, lines, StandardOpenOption.TRUNCATE_EXISTING); + } catch (IOException e) { + ui.printMessage("Error saving tasks to file."); + } + } + + /** + * Loads tasks from the file. + * + * @return a list of tasks loaded from the file. + */ + public ArrayList loadFromFile() { + Path path = Paths.get(filePath); + ArrayList tasks = new ArrayList<>(); + + if (Files.exists(path)) { + try { + List lines = Files.readAllLines(path); + for (String line : lines) { + if (line.trim().isEmpty()) + continue; + try { + tasks.add(Task.fromFileFormat(line)); + } catch (IllegalArgumentException e) { + ui.printMessage("Data file might be corrupted. (i.e., content not in expected format.)"); + } + } + } catch (IOException e) { + ui.printMessage("Error reading tasks from file."); + } + } + + return tasks; + } +} diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java new file mode 100644 index 0000000000..8358294ebd --- /dev/null +++ b/src/main/java/TaskList.java @@ -0,0 +1,204 @@ +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import exceptions.EmptyTaskDescriptionException; +import exceptions.InvalidDateFormatException; +import exceptions.InvalidTaskNumberException; +import exceptions.TaskAlreadyDoneException; +import exceptions.TaskNotDoneException; + +public class TaskList { + private ArrayList tasks = new ArrayList<>(); + private Ui ui; + + public TaskList() { + this.tasks = new ArrayList<>(); + this.ui = new Ui(); + } + + public TaskList(ArrayList tasks) { + this.tasks = tasks; + this.ui = new Ui(); + } + + public ArrayList getTasks() { + return this.tasks; + } + + /** + * Exits the chatbot. + */ + public void bye() { + ui.printMessage("Bye. Hope to see you again soon!"); + } + + /** + * Prints all tasks currently in the list. + */ + public void printTasks() { + System.out.println(" ____________________________________________________________"); + for (int i = 0; i < tasks.size(); i++) { + System.out.println(" " + (i + 1) + ". " + tasks.get(i)); + } + System.out.println(" ____________________________________________________________"); + } + + /** + * Deletes the task at the specified index provided by the command. + * @param command The user input containing the task index to delete. + */ + public void deleteTask(String command) throws InvalidTaskNumberException { + try { + int taskNo = Integer.parseInt(command.split(" ")[1]); + + if (taskNo <= 0 || taskNo > tasks.size()) { + throw new InvalidTaskNumberException("Task number out of range."); + } + + Task removedTask = tasks.remove(taskNo - 1); + ui.printMessage("Noted. I've removed this task:\n " + removedTask + "\n Now you have " + tasks.size() + " tasks in the list."); + } catch (Exception e) { + throw new InvalidTaskNumberException("Invalid task number."); + } + } + + /** + * Marks the task at the specified index as done. + * @param command The user input containing the task index to mark. + */ + public void markTask(String command) throws InvalidTaskNumberException, TaskAlreadyDoneException { + try { + int taskNo = Integer.parseInt(command.split(" ")[1]); + if (taskNo <= 0 || taskNo > tasks.size()) { + throw new InvalidTaskNumberException("Invalid task number."); + } + + Task task = tasks.get(taskNo - 1); + if (task.isDone()) { + throw new TaskAlreadyDoneException("Task \"" + task.getDescription() + "\" is already done."); + } + + task.markAsDone(); + ui.printMessage("Nice! I've marked this task as done:\n " + task); + } catch (Exception e) { + ui.printMessage(e.getMessage()); + } + } + + /** + * Unmarks the task at the specified index, marking it as not done. + * @param command The user input containing the task index to unmark. + */ + public void unmarkTask(String command) throws InvalidTaskNumberException, TaskNotDoneException{ + try { + int taskNo = Integer.parseInt(command.split(" ")[1]); + if (taskNo <= 0 || taskNo > tasks.size()) { + throw new InvalidTaskNumberException("Invalid task number."); + } + + Task task = tasks.get(taskNo - 1); + if (!task.isDone()) { + throw new TaskNotDoneException("Task \"" + task.getDescription() + "\" is not done yet."); + } + + task.unmarkAsDone(); + ui.printMessage("OK, I've marked this task as not done yet:\n " + task); + } catch (Exception e) { + ui.printMessage(e.getMessage()); + } + } + + /** + * Adds a new ToDo task to the task list. + * @param command The user input containing the task description. + */ + public void addTodo(String command) throws EmptyTaskDescriptionException { + if (command.length() <= 4) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a todo cannot be empty."); + } + + String description = command.substring(5); + if (description.isEmpty()) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a todo cannot be empty."); + } + + tasks.add(new Todo(description)); + ui.printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); + } + + /** + * Adds a new Deadline task to the task list. + * @param command The user input containing the task description and its deadline. + */ + public void addDeadline(String command) throws EmptyTaskDescriptionException { + if (command.length() <= 8) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a deadline cannot be empty."); + } + + String[] parts = command.substring(9).split(" /by "); + if (parts.length < 2 || parts[0].trim().isEmpty() || parts[1].trim().isEmpty()) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a deadline or its date cannot be empty."); + } + + tasks.add(new Deadline(parts[0], parts[1])); + ui.printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); + } + + /** + * Adds a new Event task to the task list. + * @param command The user input containing the event details. + */ + public void addEvent(String command) throws EmptyTaskDescriptionException { + if (command.length() <= 5) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of an event cannot be empty."); + } + + String[] parts = command.substring(6).split(" /from "); + String[] timeParts = parts[1].split(" /to "); + if (parts.length < 2 || parts[0].trim().isEmpty() || timeParts.length < 2 || timeParts[0].trim().isEmpty() || timeParts[1].trim().isEmpty()) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of an event or its time cannot be empty."); + } + + tasks.add(new Event(parts[0], timeParts[0], timeParts[1])); + ui.printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); + } + + /** + * Prints the tasks that are scheduled on a specific date. + * + * @param dateStr The specific date in which to filter tasks. + */ + public void eventsOndate(String dateStr) throws InvalidDateFormatException { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yyyy"); + LocalDate date; + + try { + date = LocalDate.parse(dateStr, formatter); + } catch (DateTimeParseException e) { + throw new InvalidDateFormatException(" Invalid date format. Please use the format 'd/M/yyyy'."); + } + + System.out.println(" ____________________________________________________________"); + System.out.println(" Tasks on " + date.format(DateTimeFormatter.ofPattern("M d yyyy")) + ":"); + int count = 0; + boolean hasTasks = false; + + for (Task task : tasks) { + if (task instanceof Deadline) { + Deadline d = (Deadline) task; + if (d.getBy().toLocalDate().equals(date)) { + System.out.println(" " + (count + 1) + ". " + task); + hasTasks = true; + } + } + count++; + } + + if (!hasTasks) { + System.out.println(" No tasks on this date."); + } + + System.out.println(" ____________________________________________________________"); + } +} diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java new file mode 100644 index 0000000000..2157644811 --- /dev/null +++ b/src/main/java/Ui.java @@ -0,0 +1,36 @@ +import java.util.Scanner; + +public class Ui { + + private Scanner scanner; + + public Ui() { + this.scanner = new Scanner(System.in); + } + + public void showWelcome() { + printMessage("Hello! I'm Martin\n What can I do for you?"); + } + + public String readCommand() { + return scanner.nextLine().trim(); + } + + public void showLine() { + System.out.println("_______________________________"); + } + + public void showError(String message) { + printMessage("Error: " + message); + } + + public void showLoadingError() { + printMessage("Problem encountered while loading data!"); + } + + public void printMessage(String message) { + System.out.println(" ____________________________________________________________"); + System.out.println(" " + message); + System.out.println(" ____________________________________________________________"); + } +} From 002306a92b0dfdf6ef746a496036fc8b67f66d32 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 3 Sep 2023 02:51:50 +0800 Subject: [PATCH 12/35] Added Command interface and classes and organized files into packages --- data/martin.txt | 3 +- src/main/java/Parser.java | 65 ------ src/main/java/TaskList.java | 204 ------------------ .../EmptyTaskDescriptionException.java | 7 - .../exceptions/InvalidCommandException.java | 7 - .../InvalidDateFormatException.java | 7 - .../InvalidTaskNumberException.java | 7 - .../exceptions/TaskAlreadyDoneException.java | 7 - .../java/exceptions/TaskNotDoneException.java | 7 - src/main/java/{ => martin}/Martin.java | 9 +- src/main/java/{ => martin}/Storage.java | 3 + src/main/java/{ => martin}/Ui.java | 3 +- src/main/java/martin/commands/ByeCommand.java | 20 ++ src/main/java/martin/commands/Command.java | 10 + .../java/martin/commands/DateCommand.java | 61 ++++++ .../java/martin/commands/DeadlineCommand.java | 40 ++++ .../java/martin/commands/DeleteCommand.java | 40 ++++ .../java/martin/commands/EventCommand.java | 41 ++++ .../java/martin/commands/ListCommand.java | 29 +++ .../java/martin/commands/MarkCommand.java | 45 ++++ .../java/martin/commands/TodoCommand.java | 40 ++++ .../java/martin/commands/UnmarkCommand.java | 47 ++++ .../EmptyTaskDescriptionException.java | 7 + .../exceptions/InvalidCommandException.java | 7 + .../InvalidDateFormatException.java | 7 + .../InvalidTaskNumberException.java | 7 + .../martin/exceptions/MartinException.java | 7 + .../exceptions/TaskAlreadyDoneException.java | 7 + .../exceptions/TaskNotDoneException.java | 7 + src/main/java/martin/parser/ListCommand.java | 5 + src/main/java/martin/parser/Parser.java | 68 ++++++ src/main/java/{ => martin/task}/Deadline.java | 2 + src/main/java/{ => martin/task}/Event.java | 2 + src/main/java/{ => martin/task}/Task.java | 2 + src/main/java/martin/task/TaskList.java | 19 ++ src/main/java/{ => martin/task}/Todo.java | 2 + 36 files changed, 537 insertions(+), 314 deletions(-) delete mode 100644 src/main/java/Parser.java delete mode 100644 src/main/java/TaskList.java delete mode 100644 src/main/java/exceptions/EmptyTaskDescriptionException.java delete mode 100644 src/main/java/exceptions/InvalidCommandException.java delete mode 100644 src/main/java/exceptions/InvalidDateFormatException.java delete mode 100644 src/main/java/exceptions/InvalidTaskNumberException.java delete mode 100644 src/main/java/exceptions/TaskAlreadyDoneException.java delete mode 100644 src/main/java/exceptions/TaskNotDoneException.java rename src/main/java/{ => martin}/Martin.java (84%) rename src/main/java/{ => martin}/Storage.java (97%) rename src/main/java/{ => martin}/Ui.java (88%) create mode 100644 src/main/java/martin/commands/ByeCommand.java create mode 100644 src/main/java/martin/commands/Command.java create mode 100644 src/main/java/martin/commands/DateCommand.java create mode 100644 src/main/java/martin/commands/DeadlineCommand.java create mode 100644 src/main/java/martin/commands/DeleteCommand.java create mode 100644 src/main/java/martin/commands/EventCommand.java create mode 100644 src/main/java/martin/commands/ListCommand.java create mode 100644 src/main/java/martin/commands/MarkCommand.java create mode 100644 src/main/java/martin/commands/TodoCommand.java create mode 100644 src/main/java/martin/commands/UnmarkCommand.java create mode 100644 src/main/java/martin/exceptions/EmptyTaskDescriptionException.java create mode 100644 src/main/java/martin/exceptions/InvalidCommandException.java create mode 100644 src/main/java/martin/exceptions/InvalidDateFormatException.java create mode 100644 src/main/java/martin/exceptions/InvalidTaskNumberException.java create mode 100644 src/main/java/martin/exceptions/MartinException.java create mode 100644 src/main/java/martin/exceptions/TaskAlreadyDoneException.java create mode 100644 src/main/java/martin/exceptions/TaskNotDoneException.java create mode 100644 src/main/java/martin/parser/ListCommand.java create mode 100644 src/main/java/martin/parser/Parser.java rename src/main/java/{ => martin/task}/Deadline.java (97%) rename src/main/java/{ => martin/task}/Event.java (94%) rename src/main/java/{ => martin/task}/Task.java (99%) create mode 100644 src/main/java/martin/task/TaskList.java rename src/main/java/{ => martin/task}/Todo.java (91%) diff --git a/data/martin.txt b/data/martin.txt index 384a160491..2c4c4de310 100644 --- a/data/martin.txt +++ b/data/martin.txt @@ -1 +1,2 @@ -0 | test +1 | test +D | 0 | return book | 02/12/2019 1800 diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java deleted file mode 100644 index 60ba11167a..0000000000 --- a/src/main/java/Parser.java +++ /dev/null @@ -1,65 +0,0 @@ -import exceptions.EmptyTaskDescriptionException; -import exceptions.InvalidCommandException; -import exceptions.InvalidDateFormatException; -import exceptions.InvalidTaskNumberException; -import exceptions.TaskAlreadyDoneException; -import exceptions.TaskNotDoneException; - -public class Parser { - - private TaskList taskList; - - public Parser(TaskList taskList) { - this.taskList = taskList; - } - - /** - * Parses the user input and triggers the appropriate action on TaskList. - * @param input The entire string of the user's input. - * @throws InvalidTaskNumberException - * @throws TaskAlreadyDoneException - * @throws TaskNotDoneException - * @throws InvalidDateFormatException - * @throws DukeException When the command is invalid. - */ - public void parse(String input) throws InvalidCommandException, EmptyTaskDescriptionException, InvalidTaskNumberException, TaskAlreadyDoneException, TaskNotDoneException, InvalidDateFormatException { - if (input == null || input.trim().isEmpty()) { - throw new InvalidCommandException("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); - } - - String[] parts = input.split(" ", 2); - String command = parts[0].toLowerCase(); - - switch (command) { - case "bye": - taskList.bye(); - break; - case "list": - taskList.printTasks(); - break; - case "delete": - taskList.deleteTask(input); - break; - case "mark": - taskList.markTask(input); - break; - case "unmark": - taskList.unmarkTask(input); - break; - case "todo": - taskList.addTodo(input); - break; - case "deadline": - taskList.addDeadline(input); - break; - case "event": - taskList.addEvent(input); - break; - case "date": - taskList.eventsOndate(input); - break; - default: - throw new InvalidCommandException("Unknown command: " + input); - } - } -} diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java deleted file mode 100644 index 8358294ebd..0000000000 --- a/src/main/java/TaskList.java +++ /dev/null @@ -1,204 +0,0 @@ -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.util.ArrayList; -import exceptions.EmptyTaskDescriptionException; -import exceptions.InvalidDateFormatException; -import exceptions.InvalidTaskNumberException; -import exceptions.TaskAlreadyDoneException; -import exceptions.TaskNotDoneException; - -public class TaskList { - private ArrayList tasks = new ArrayList<>(); - private Ui ui; - - public TaskList() { - this.tasks = new ArrayList<>(); - this.ui = new Ui(); - } - - public TaskList(ArrayList tasks) { - this.tasks = tasks; - this.ui = new Ui(); - } - - public ArrayList getTasks() { - return this.tasks; - } - - /** - * Exits the chatbot. - */ - public void bye() { - ui.printMessage("Bye. Hope to see you again soon!"); - } - - /** - * Prints all tasks currently in the list. - */ - public void printTasks() { - System.out.println(" ____________________________________________________________"); - for (int i = 0; i < tasks.size(); i++) { - System.out.println(" " + (i + 1) + ". " + tasks.get(i)); - } - System.out.println(" ____________________________________________________________"); - } - - /** - * Deletes the task at the specified index provided by the command. - * @param command The user input containing the task index to delete. - */ - public void deleteTask(String command) throws InvalidTaskNumberException { - try { - int taskNo = Integer.parseInt(command.split(" ")[1]); - - if (taskNo <= 0 || taskNo > tasks.size()) { - throw new InvalidTaskNumberException("Task number out of range."); - } - - Task removedTask = tasks.remove(taskNo - 1); - ui.printMessage("Noted. I've removed this task:\n " + removedTask + "\n Now you have " + tasks.size() + " tasks in the list."); - } catch (Exception e) { - throw new InvalidTaskNumberException("Invalid task number."); - } - } - - /** - * Marks the task at the specified index as done. - * @param command The user input containing the task index to mark. - */ - public void markTask(String command) throws InvalidTaskNumberException, TaskAlreadyDoneException { - try { - int taskNo = Integer.parseInt(command.split(" ")[1]); - if (taskNo <= 0 || taskNo > tasks.size()) { - throw new InvalidTaskNumberException("Invalid task number."); - } - - Task task = tasks.get(taskNo - 1); - if (task.isDone()) { - throw new TaskAlreadyDoneException("Task \"" + task.getDescription() + "\" is already done."); - } - - task.markAsDone(); - ui.printMessage("Nice! I've marked this task as done:\n " + task); - } catch (Exception e) { - ui.printMessage(e.getMessage()); - } - } - - /** - * Unmarks the task at the specified index, marking it as not done. - * @param command The user input containing the task index to unmark. - */ - public void unmarkTask(String command) throws InvalidTaskNumberException, TaskNotDoneException{ - try { - int taskNo = Integer.parseInt(command.split(" ")[1]); - if (taskNo <= 0 || taskNo > tasks.size()) { - throw new InvalidTaskNumberException("Invalid task number."); - } - - Task task = tasks.get(taskNo - 1); - if (!task.isDone()) { - throw new TaskNotDoneException("Task \"" + task.getDescription() + "\" is not done yet."); - } - - task.unmarkAsDone(); - ui.printMessage("OK, I've marked this task as not done yet:\n " + task); - } catch (Exception e) { - ui.printMessage(e.getMessage()); - } - } - - /** - * Adds a new ToDo task to the task list. - * @param command The user input containing the task description. - */ - public void addTodo(String command) throws EmptyTaskDescriptionException { - if (command.length() <= 4) { - throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a todo cannot be empty."); - } - - String description = command.substring(5); - if (description.isEmpty()) { - throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a todo cannot be empty."); - } - - tasks.add(new Todo(description)); - ui.printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); - } - - /** - * Adds a new Deadline task to the task list. - * @param command The user input containing the task description and its deadline. - */ - public void addDeadline(String command) throws EmptyTaskDescriptionException { - if (command.length() <= 8) { - throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a deadline cannot be empty."); - } - - String[] parts = command.substring(9).split(" /by "); - if (parts.length < 2 || parts[0].trim().isEmpty() || parts[1].trim().isEmpty()) { - throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a deadline or its date cannot be empty."); - } - - tasks.add(new Deadline(parts[0], parts[1])); - ui.printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); - } - - /** - * Adds a new Event task to the task list. - * @param command The user input containing the event details. - */ - public void addEvent(String command) throws EmptyTaskDescriptionException { - if (command.length() <= 5) { - throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of an event cannot be empty."); - } - - String[] parts = command.substring(6).split(" /from "); - String[] timeParts = parts[1].split(" /to "); - if (parts.length < 2 || parts[0].trim().isEmpty() || timeParts.length < 2 || timeParts[0].trim().isEmpty() || timeParts[1].trim().isEmpty()) { - throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of an event or its time cannot be empty."); - } - - tasks.add(new Event(parts[0], timeParts[0], timeParts[1])); - ui.printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); - } - - /** - * Prints the tasks that are scheduled on a specific date. - * - * @param dateStr The specific date in which to filter tasks. - */ - public void eventsOndate(String dateStr) throws InvalidDateFormatException { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yyyy"); - LocalDate date; - - try { - date = LocalDate.parse(dateStr, formatter); - } catch (DateTimeParseException e) { - throw new InvalidDateFormatException(" Invalid date format. Please use the format 'd/M/yyyy'."); - } - - System.out.println(" ____________________________________________________________"); - System.out.println(" Tasks on " + date.format(DateTimeFormatter.ofPattern("M d yyyy")) + ":"); - int count = 0; - boolean hasTasks = false; - - for (Task task : tasks) { - if (task instanceof Deadline) { - Deadline d = (Deadline) task; - if (d.getBy().toLocalDate().equals(date)) { - System.out.println(" " + (count + 1) + ". " + task); - hasTasks = true; - } - } - count++; - } - - if (!hasTasks) { - System.out.println(" No tasks on this date."); - } - - System.out.println(" ____________________________________________________________"); - } -} diff --git a/src/main/java/exceptions/EmptyTaskDescriptionException.java b/src/main/java/exceptions/EmptyTaskDescriptionException.java deleted file mode 100644 index 611a17555e..0000000000 --- a/src/main/java/exceptions/EmptyTaskDescriptionException.java +++ /dev/null @@ -1,7 +0,0 @@ -package exceptions; - -public class EmptyTaskDescriptionException extends Exception { - public EmptyTaskDescriptionException(String message) { - super(message); - } -} \ No newline at end of file diff --git a/src/main/java/exceptions/InvalidCommandException.java b/src/main/java/exceptions/InvalidCommandException.java deleted file mode 100644 index 465809084f..0000000000 --- a/src/main/java/exceptions/InvalidCommandException.java +++ /dev/null @@ -1,7 +0,0 @@ -package exceptions; - -public class InvalidCommandException extends Exception { - public InvalidCommandException(String message) { - super(message); - } -} \ No newline at end of file diff --git a/src/main/java/exceptions/InvalidDateFormatException.java b/src/main/java/exceptions/InvalidDateFormatException.java deleted file mode 100644 index 39cd32e4dd..0000000000 --- a/src/main/java/exceptions/InvalidDateFormatException.java +++ /dev/null @@ -1,7 +0,0 @@ -package exceptions; - -public class InvalidDateFormatException extends Exception { - public InvalidDateFormatException(String message) { - super(message); - } -} \ No newline at end of file diff --git a/src/main/java/exceptions/InvalidTaskNumberException.java b/src/main/java/exceptions/InvalidTaskNumberException.java deleted file mode 100644 index 1babc3b009..0000000000 --- a/src/main/java/exceptions/InvalidTaskNumberException.java +++ /dev/null @@ -1,7 +0,0 @@ -package exceptions; - -public class InvalidTaskNumberException extends Exception { - public InvalidTaskNumberException(String message) { - super(message); - } -} \ No newline at end of file diff --git a/src/main/java/exceptions/TaskAlreadyDoneException.java b/src/main/java/exceptions/TaskAlreadyDoneException.java deleted file mode 100644 index 47e0c26b3c..0000000000 --- a/src/main/java/exceptions/TaskAlreadyDoneException.java +++ /dev/null @@ -1,7 +0,0 @@ -package exceptions; - -public class TaskAlreadyDoneException extends Exception { - public TaskAlreadyDoneException(String message) { - super(message); - } -} \ No newline at end of file diff --git a/src/main/java/exceptions/TaskNotDoneException.java b/src/main/java/exceptions/TaskNotDoneException.java deleted file mode 100644 index 8bd20ada9d..0000000000 --- a/src/main/java/exceptions/TaskNotDoneException.java +++ /dev/null @@ -1,7 +0,0 @@ -package exceptions; - -public class TaskNotDoneException extends Exception { - public TaskNotDoneException(String message) { - super(message); - } -} \ No newline at end of file diff --git a/src/main/java/Martin.java b/src/main/java/martin/Martin.java similarity index 84% rename from src/main/java/Martin.java rename to src/main/java/martin/Martin.java index cfc3319aec..df311d8791 100644 --- a/src/main/java/Martin.java +++ b/src/main/java/martin/Martin.java @@ -1,3 +1,9 @@ +package martin; + +import martin.commands.Command; +import martin.parser.Parser; +import martin.task.TaskList; + public class Martin { private Storage storage; private TaskList tasks; @@ -22,7 +28,8 @@ public void run() { while (!isExit) { try { String command = ui.readCommand(); - parser.parse(command); + Command cmd = parser.parse(command); + cmd.execute(); if ("bye".equalsIgnoreCase(command.trim())) { isExit = true; diff --git a/src/main/java/Storage.java b/src/main/java/martin/Storage.java similarity index 97% rename from src/main/java/Storage.java rename to src/main/java/martin/Storage.java index 28439462c0..ece45d00bd 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/martin/Storage.java @@ -1,3 +1,4 @@ +package martin; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -6,6 +7,8 @@ import java.util.ArrayList; import java.util.List; +import martin.task.Task; + public class Storage { private String filePath; diff --git a/src/main/java/Ui.java b/src/main/java/martin/Ui.java similarity index 88% rename from src/main/java/Ui.java rename to src/main/java/martin/Ui.java index 2157644811..888e94d014 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/martin/Ui.java @@ -1,3 +1,4 @@ +package martin; import java.util.Scanner; public class Ui { @@ -17,7 +18,7 @@ public String readCommand() { } public void showLine() { - System.out.println("_______________________________"); + System.out.println(" ____________________________________________________________"); } public void showError(String message) { diff --git a/src/main/java/martin/commands/ByeCommand.java b/src/main/java/martin/commands/ByeCommand.java new file mode 100644 index 0000000000..fcfdbbb13e --- /dev/null +++ b/src/main/java/martin/commands/ByeCommand.java @@ -0,0 +1,20 @@ +package martin.commands; + +import martin.Ui; + +public class ByeCommand implements Command { + + private Ui ui; + + public ByeCommand() { + this.ui = new Ui(); + } + + /** + * Exits the chatbot. + */ + @Override + public void execute() { + ui.printMessage("Bye. Hope to see you again soon!"); + } +} diff --git a/src/main/java/martin/commands/Command.java b/src/main/java/martin/commands/Command.java new file mode 100644 index 0000000000..f052f22cc4 --- /dev/null +++ b/src/main/java/martin/commands/Command.java @@ -0,0 +1,10 @@ +package martin.commands; + +import martin.exceptions.MartinException; + +public interface Command { + /** + * Executes the specific command. + */ + void execute() throws MartinException; +} \ No newline at end of file diff --git a/src/main/java/martin/commands/DateCommand.java b/src/main/java/martin/commands/DateCommand.java new file mode 100644 index 0000000000..acef9f995a --- /dev/null +++ b/src/main/java/martin/commands/DateCommand.java @@ -0,0 +1,61 @@ +package martin.commands; + +import martin.Ui; +import martin.exceptions.InvalidDateFormatException; +import martin.exceptions.MartinException; +import martin.task.Deadline; +import martin.task.Task; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; + +public class DateCommand implements Command { + + private Ui ui; + private String dateStr; + private ArrayList tasks; + + public DateCommand(String dateStr, ArrayList tasks) { + this.ui = new Ui(); + this.dateStr = dateStr; + this.tasks = tasks; + } + /** + * Prints the tasks that are scheduled on a specific date. + */ + @Override + public void execute() throws MartinException { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yyyy"); + LocalDate date; + + try { + date = LocalDate.parse(dateStr, formatter); + } catch (DateTimeParseException e) { + throw new InvalidDateFormatException(" Invalid date format. Please use the format 'd/M/yyyy'."); + } + + ui.showLine(); + System.out.println(" Tasks on " + date.format(DateTimeFormatter.ofPattern("M d yyyy")) + ":"); + int count = 0; + boolean hasTasks = false; + + for (Task task : tasks) { + if (task instanceof Deadline) { + Deadline d = (Deadline) task; + if (d.getBy().toLocalDate().equals(date)) { + System.out.println(" " + (count + 1) + ". " + task); + hasTasks = true; + } + } + count++; + } + + if (!hasTasks) { + System.out.println(" No tasks on this date."); + } + + ui.showLine(); + } +} diff --git a/src/main/java/martin/commands/DeadlineCommand.java b/src/main/java/martin/commands/DeadlineCommand.java new file mode 100644 index 0000000000..98ab413ed1 --- /dev/null +++ b/src/main/java/martin/commands/DeadlineCommand.java @@ -0,0 +1,40 @@ +package martin.commands; + +import martin.Ui; +import martin.exceptions.EmptyTaskDescriptionException; +import martin.exceptions.MartinException; +import martin.task.Deadline; +import martin.task.Task; + +import java.util.ArrayList; + +public class DeadlineCommand implements Command { + + private Ui ui; + private String command; + private ArrayList tasks; + + public DeadlineCommand(String command, ArrayList tasks) { + this.ui = new Ui(); + this.command = command; + this.tasks = tasks; + } + + /** + * Adds a new Deadline task to the task list. + **/ + @Override + public void execute() throws MartinException { + if (command.length() <= 8) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a deadline cannot be empty."); + } + + String[] parts = command.substring(9).split(" /by "); + if (parts.length < 2 || parts[0].trim().isEmpty() || parts[1].trim().isEmpty()) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a deadline or its date cannot be empty."); + } + + tasks.add(new Deadline(parts[0], parts[1])); + ui.printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); + } +} \ No newline at end of file diff --git a/src/main/java/martin/commands/DeleteCommand.java b/src/main/java/martin/commands/DeleteCommand.java new file mode 100644 index 0000000000..dfe8130f26 --- /dev/null +++ b/src/main/java/martin/commands/DeleteCommand.java @@ -0,0 +1,40 @@ +package martin.commands; + +import martin.Ui; +import martin.exceptions.InvalidTaskNumberException; +import martin.exceptions.MartinException; +import martin.task.Task; + +import java.util.ArrayList; + +public class DeleteCommand implements Command { + + private Ui ui; + private String command; + private ArrayList tasks; + + public DeleteCommand(String command, ArrayList tasks) { + this.ui = new Ui(); + this.command = command; + this.tasks = tasks; + } + + /** + * Deletes the task at the specified index provided by the command. + */ + @Override + public void execute() throws MartinException { + try { + int taskNo = Integer.parseInt(command.split(" ")[1]); + + if (taskNo <= 0 || taskNo > tasks.size()) { + throw new InvalidTaskNumberException("Task number out of range."); + } + + Task removedTask = tasks.remove(taskNo - 1); + ui.printMessage("Noted. I've removed this task:\n " + removedTask + "\n Now you have " + tasks.size() + " tasks in the list."); + } catch (Exception e) { + throw new InvalidTaskNumberException("Invalid task number."); + } + } +} diff --git a/src/main/java/martin/commands/EventCommand.java b/src/main/java/martin/commands/EventCommand.java new file mode 100644 index 0000000000..df3ae890e9 --- /dev/null +++ b/src/main/java/martin/commands/EventCommand.java @@ -0,0 +1,41 @@ +package martin.commands; + +import martin.Ui; +import martin.exceptions.EmptyTaskDescriptionException; +import martin.exceptions.MartinException; +import martin.task.Event; +import martin.task.Task; + +import java.util.ArrayList; + +public class EventCommand implements Command { + + private Ui ui; + private String command; + private ArrayList tasks; + + public EventCommand(String command, ArrayList tasks) { + this.ui = new Ui(); + this.command = command; + this.tasks = tasks; + } + + /** + * Adds a new Event task to the task list. + */ + @Override + public void execute() throws MartinException { + if (command.length() <= 5) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of an event cannot be empty."); + } + + String[] parts = command.substring(6).split(" /from "); + String[] timeParts = parts[1].split(" /to "); + if (parts.length < 2 || parts[0].trim().isEmpty() || timeParts.length < 2 || timeParts[0].trim().isEmpty() || timeParts[1].trim().isEmpty()) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of an event or its time cannot be empty."); + } + + tasks.add(new Event(parts[0], timeParts[0], timeParts[1])); + ui.printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); + } +} diff --git a/src/main/java/martin/commands/ListCommand.java b/src/main/java/martin/commands/ListCommand.java new file mode 100644 index 0000000000..8673a40757 --- /dev/null +++ b/src/main/java/martin/commands/ListCommand.java @@ -0,0 +1,29 @@ +package martin.commands; + +import martin.Ui; +import martin.task.Task; + +import java.util.ArrayList; + +public class ListCommand implements Command { + + private Ui ui; + private ArrayList tasks; + + public ListCommand(ArrayList tasks) { + this.ui = new Ui(); + this.tasks = tasks; + } + + /** + * Prints all tasks currently in the list. + */ + @Override + public void execute() { + ui.showLine(); + for (int i = 0; i < tasks.size(); i++) { + System.out.println(" " + (i + 1) + ". " + tasks.get(i)); + } + ui.showLine(); + } +} diff --git a/src/main/java/martin/commands/MarkCommand.java b/src/main/java/martin/commands/MarkCommand.java new file mode 100644 index 0000000000..91e9cdbb0a --- /dev/null +++ b/src/main/java/martin/commands/MarkCommand.java @@ -0,0 +1,45 @@ +package martin.commands; + +import martin.Ui; +import martin.exceptions.InvalidTaskNumberException; +import martin.exceptions.MartinException; +import martin.exceptions.TaskAlreadyDoneException; +import martin.task.Task; + +import java.util.ArrayList; + +public class MarkCommand implements Command { + + private Ui ui; + private String command; + private ArrayList tasks; + + public MarkCommand(String command, ArrayList tasks) { + this.ui = new Ui(); + this.command = command; + this.tasks = tasks; + } + + /** + * Marks the task at the specified index as done. + */ + @Override + public void execute() throws MartinException { + try { + int taskNo = Integer.parseInt(command.split(" ")[1]); + if (taskNo <= 0 || taskNo > tasks.size()) { + throw new InvalidTaskNumberException("Invalid task number."); + } + + Task task = tasks.get(taskNo - 1); + if (task.isDone()) { + throw new TaskAlreadyDoneException("Task \"" + task.getDescription() + "\" is already done."); + } + + task.markAsDone(); + ui.printMessage("Nice! I've marked this task as done:\n " + task); + } catch (Exception e) { + ui.printMessage(e.getMessage()); + } + } +} diff --git a/src/main/java/martin/commands/TodoCommand.java b/src/main/java/martin/commands/TodoCommand.java new file mode 100644 index 0000000000..252441382d --- /dev/null +++ b/src/main/java/martin/commands/TodoCommand.java @@ -0,0 +1,40 @@ +package martin.commands; + +import martin.Ui; +import martin.exceptions.EmptyTaskDescriptionException; +import martin.exceptions.MartinException; +import martin.task.Task; +import martin.task.Todo; + +import java.util.ArrayList; + +public class TodoCommand implements Command { + + private Ui ui; + private String command; + private ArrayList tasks; + + public TodoCommand(String command, ArrayList tasks) { + this.ui = new Ui(); + this.command = command; + this.tasks = tasks; + } + + /** + * Adds a new ToDo task to the task list. + */ + @Override + public void execute() throws MartinException { + if (command.length() <= 4) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a todo cannot be empty."); + } + + String description = command.substring(5); + if (description.isEmpty()) { + throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a todo cannot be empty."); + } + + tasks.add(new Todo(description)); + ui.printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); + } +} diff --git a/src/main/java/martin/commands/UnmarkCommand.java b/src/main/java/martin/commands/UnmarkCommand.java new file mode 100644 index 0000000000..6145e2a7ec --- /dev/null +++ b/src/main/java/martin/commands/UnmarkCommand.java @@ -0,0 +1,47 @@ +package martin.commands; + +import martin.Ui; +import martin.exceptions.InvalidTaskNumberException; +import martin.exceptions.MartinException; +import martin.exceptions.TaskNotDoneException; +import martin.task.Task; + +import java.util.ArrayList; + +public class UnmarkCommand implements Command { + + private Ui ui; + private String command; + private ArrayList tasks; + + public UnmarkCommand(String command, ArrayList tasks) { + this.ui = new Ui(); + this.command = command; + this.tasks = tasks; + } + + /** + * Unmarks the task at the specified index, marking it as not done. + * @param command The user input containing the task index to unmark. + * @param tasks The Array List of tasks in the task list. + */ + @Override + public void execute() throws MartinException { + try { + int taskNo = Integer.parseInt(command.split(" ")[1]); + if (taskNo <= 0 || taskNo > tasks.size()) { + throw new InvalidTaskNumberException("Invalid task number."); + } + + Task task = tasks.get(taskNo - 1); + if (!task.isDone()) { + throw new TaskNotDoneException("Task \"" + task.getDescription() + "\" is not done yet."); + } + + task.unmarkAsDone(); + ui.printMessage("OK, I've marked this task as not done yet:\n " + task); + } catch (Exception e) { + ui.printMessage(e.getMessage()); + } + } +} diff --git a/src/main/java/martin/exceptions/EmptyTaskDescriptionException.java b/src/main/java/martin/exceptions/EmptyTaskDescriptionException.java new file mode 100644 index 0000000000..dba77c4084 --- /dev/null +++ b/src/main/java/martin/exceptions/EmptyTaskDescriptionException.java @@ -0,0 +1,7 @@ +package martin.exceptions; + +public class EmptyTaskDescriptionException extends MartinException { + public EmptyTaskDescriptionException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/martin/exceptions/InvalidCommandException.java b/src/main/java/martin/exceptions/InvalidCommandException.java new file mode 100644 index 0000000000..a7972eae7c --- /dev/null +++ b/src/main/java/martin/exceptions/InvalidCommandException.java @@ -0,0 +1,7 @@ +package martin.exceptions; + +public class InvalidCommandException extends MartinException { + public InvalidCommandException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/martin/exceptions/InvalidDateFormatException.java b/src/main/java/martin/exceptions/InvalidDateFormatException.java new file mode 100644 index 0000000000..29046fc924 --- /dev/null +++ b/src/main/java/martin/exceptions/InvalidDateFormatException.java @@ -0,0 +1,7 @@ +package martin.exceptions; + +public class InvalidDateFormatException extends MartinException { + public InvalidDateFormatException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/martin/exceptions/InvalidTaskNumberException.java b/src/main/java/martin/exceptions/InvalidTaskNumberException.java new file mode 100644 index 0000000000..ac60b0f94c --- /dev/null +++ b/src/main/java/martin/exceptions/InvalidTaskNumberException.java @@ -0,0 +1,7 @@ +package martin.exceptions; + +public class InvalidTaskNumberException extends MartinException { + public InvalidTaskNumberException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/martin/exceptions/MartinException.java b/src/main/java/martin/exceptions/MartinException.java new file mode 100644 index 0000000000..80487f820c --- /dev/null +++ b/src/main/java/martin/exceptions/MartinException.java @@ -0,0 +1,7 @@ +package martin.exceptions; + +public class MartinException extends Exception { + public MartinException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/martin/exceptions/TaskAlreadyDoneException.java b/src/main/java/martin/exceptions/TaskAlreadyDoneException.java new file mode 100644 index 0000000000..cea9ece7ba --- /dev/null +++ b/src/main/java/martin/exceptions/TaskAlreadyDoneException.java @@ -0,0 +1,7 @@ +package martin.exceptions; + +public class TaskAlreadyDoneException extends MartinException { + public TaskAlreadyDoneException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/martin/exceptions/TaskNotDoneException.java b/src/main/java/martin/exceptions/TaskNotDoneException.java new file mode 100644 index 0000000000..228f427b4d --- /dev/null +++ b/src/main/java/martin/exceptions/TaskNotDoneException.java @@ -0,0 +1,7 @@ +package martin.exceptions; + +public class TaskNotDoneException extends MartinException { + public TaskNotDoneException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/martin/parser/ListCommand.java b/src/main/java/martin/parser/ListCommand.java new file mode 100644 index 0000000000..945ac94228 --- /dev/null +++ b/src/main/java/martin/parser/ListCommand.java @@ -0,0 +1,5 @@ +package martin.parser; + +public class ListCommand { + +} diff --git a/src/main/java/martin/parser/Parser.java b/src/main/java/martin/parser/Parser.java new file mode 100644 index 0000000000..9a2a1cae1c --- /dev/null +++ b/src/main/java/martin/parser/Parser.java @@ -0,0 +1,68 @@ +package martin.parser; + +import martin.exceptions.InvalidCommandException; +import martin.exceptions.MartinException; +import martin.task.Task; +import martin.task.TaskList; + +import java.util.ArrayList; + +import martin.commands.ByeCommand; +import martin.commands.ListCommand; +import martin.commands.MarkCommand; +import martin.commands.TodoCommand; +import martin.commands.UnmarkCommand; +import martin.commands.Command; +import martin.commands.DateCommand; +import martin.commands.DeadlineCommand; +import martin.commands.DeleteCommand; +import martin.commands.EventCommand; + + +public class Parser { + + private TaskList taskList; + + public Parser(TaskList taskList) { + this.taskList = taskList; + } + + /** + * Parses the user input and returns the appropriate Command object. + * @param input The entire string of the user's input. + * @throws MartinException When the command is invalid or there's an error. + * @return Command The appropriate command to be executed. + */ + public Command parse(String input) throws MartinException { + if (input == null || input.trim().isEmpty()) { + throw new InvalidCommandException("☹ OOPS!!! I'm sorry, but I don't know what that means :-("); + } + + String[] parts = input.split(" ", 2); + String command = parts[0].toLowerCase(); + ArrayList tasks = taskList.getTasks(); + + switch (command) { + case "bye": + return new ByeCommand(); + case "list": + return new ListCommand(tasks); + case "delete": + return new DeleteCommand(input, tasks); + case "mark": + return new MarkCommand(input, tasks); + case "unmark": + return new UnmarkCommand(input, tasks); + case "todo": + return new TodoCommand(input, tasks); + case "deadline": + return new DeadlineCommand(input, tasks); + case "event": + return new EventCommand(input, tasks); + case "date": + return new DateCommand(input, tasks); + default: + throw new InvalidCommandException("Unknown command: " + input); + } + } +} diff --git a/src/main/java/Deadline.java b/src/main/java/martin/task/Deadline.java similarity index 97% rename from src/main/java/Deadline.java rename to src/main/java/martin/task/Deadline.java index 3ad7c6b798..1cc34fb45c 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/martin/task/Deadline.java @@ -1,3 +1,5 @@ +package martin.task; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/Event.java b/src/main/java/martin/task/Event.java similarity index 94% rename from src/main/java/Event.java rename to src/main/java/martin/task/Event.java index e4cc99e527..3a7b0dc5ba 100644 --- a/src/main/java/Event.java +++ b/src/main/java/martin/task/Event.java @@ -1,3 +1,5 @@ +package martin.task; + public class Event extends Task { protected String from; protected String to; diff --git a/src/main/java/Task.java b/src/main/java/martin/task/Task.java similarity index 99% rename from src/main/java/Task.java rename to src/main/java/martin/task/Task.java index 41ced023f5..a228f085f0 100644 --- a/src/main/java/Task.java +++ b/src/main/java/martin/task/Task.java @@ -1,3 +1,5 @@ +package martin.task; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/martin/task/TaskList.java b/src/main/java/martin/task/TaskList.java new file mode 100644 index 0000000000..45687247e2 --- /dev/null +++ b/src/main/java/martin/task/TaskList.java @@ -0,0 +1,19 @@ +package martin.task; + +import java.util.ArrayList; + +public class TaskList { + private ArrayList tasks = new ArrayList<>(); + + public TaskList() { + this.tasks = new ArrayList<>(); + } + + public TaskList(ArrayList tasks) { + this.tasks = tasks; + } + + public ArrayList getTasks() { + return this.tasks; + } +} diff --git a/src/main/java/Todo.java b/src/main/java/martin/task/Todo.java similarity index 91% rename from src/main/java/Todo.java rename to src/main/java/martin/task/Todo.java index 355252cf6c..ef6b47a403 100644 --- a/src/main/java/Todo.java +++ b/src/main/java/martin/task/Todo.java @@ -1,3 +1,5 @@ +package martin.task; + public class Todo extends Task { public Todo(String description) { super(description); From d6550fc4a5cd09909ebc1cfee3a0719269beedfe Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 3 Sep 2023 03:02:28 +0800 Subject: [PATCH 13/35] Added Gradle Support --- build.gradle | 42 ++++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 63375 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 248 +++++++++++++++++++++++ gradlew.bat | 92 +++++++++ text-ui-test/runtest.sh | 0 6 files changed, 389 insertions(+) create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat mode change 100644 => 100755 text-ui-test/runtest.sh diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..a388517ae1 --- /dev/null +++ b/build.gradle @@ -0,0 +1,42 @@ +plugins { + id 'java' + id 'application' + id 'com.github.johnrengelman.shadow' version '7.1.2' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.10.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.10.0' +} + +test { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed" + + showExceptions true + exceptionFormat "full" + showCauses true + showStackTraces true + showStandardStreams = false + } +} + +application { + mainClass.set("seedu.duke.Duke") +} + +shadowJar { + archiveBaseName = "duke" + archiveClassifier = null + dependsOn("distZip", "distTar") +} + +run{ + standardInput = System.in +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..033e24c4cdf41af1ab109bc7f253b2b887023340 GIT binary patch literal 63375 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≫L+crb^Bz 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..6689b85bee --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh old mode 100644 new mode 100755 From 2a1c9a5f77663bd61d89d30aaec3c82fcbce27b2 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 3 Sep 2023 03:41:51 +0800 Subject: [PATCH 14/35] Added DeleteCommandTest and TodoCommandTest files --- build.gradle | 4 +-- .../martin/commands/DeleteCommandTest.java | 35 +++++++++++++++++++ .../java/martin/commands/TodoCommandTest.java | 26 ++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 src/test/java/martin/commands/DeleteCommandTest.java create mode 100644 src/test/java/martin/commands/TodoCommandTest.java diff --git a/build.gradle b/build.gradle index a388517ae1..828f9a059c 100644 --- a/build.gradle +++ b/build.gradle @@ -28,11 +28,11 @@ test { } application { - mainClass.set("seedu.duke.Duke") + mainClass.set("martin.Martin") } shadowJar { - archiveBaseName = "duke" + archiveBaseName = "martin" archiveClassifier = null dependsOn("distZip", "distTar") } diff --git a/src/test/java/martin/commands/DeleteCommandTest.java b/src/test/java/martin/commands/DeleteCommandTest.java new file mode 100644 index 0000000000..e9c88d0151 --- /dev/null +++ b/src/test/java/martin/commands/DeleteCommandTest.java @@ -0,0 +1,35 @@ +package martin.commands; + +import martin.exceptions.InvalidTaskNumberException; +import martin.exceptions.MartinException; +import martin.task.Task; +import martin.task.TaskList; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class DeleteCommandTest { + private TaskList list; + private DeleteCommand command; + + @BeforeEach + public void setUp() { + list = new TaskList(); + list.getTasks().add(new Task("Sample Task 1")); + } + + @Test + public void execute_deleteTask_taskRemoved() throws MartinException { + command = new DeleteCommand("delete 1", list.getTasks()); + command.execute(); + assertEquals(0, list.getTasks().size()); + } + + @Test + public void execute_invalidIndex_exceptionThrown() { + command = new DeleteCommand("delete 3", list.getTasks()); + assertThrows(InvalidTaskNumberException.class, () -> { + command.execute(); + }); + } +} diff --git a/src/test/java/martin/commands/TodoCommandTest.java b/src/test/java/martin/commands/TodoCommandTest.java new file mode 100644 index 0000000000..1a3fd2052a --- /dev/null +++ b/src/test/java/martin/commands/TodoCommandTest.java @@ -0,0 +1,26 @@ +package martin.commands; + +import martin.exceptions.MartinException; +import martin.task.Task; +import martin.task.TaskList; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class TodoCommandTest { + private TaskList list; + private TodoCommand command; + + @BeforeEach + public void setUp() { + list = new TaskList(); + } + + @Test + public void execute_addTask_taskAdded() throws MartinException { + command = new TodoCommand("todo Sample task", list.getTasks()); + command.execute(); + assertEquals(1, list.getTasks().size()); + assertTrue(list.getTasks().get(0) instanceof Task); + } +} From 67dc2219ca70340f3590414ff13ae396a6dc75b1 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 3 Sep 2023 03:48:20 +0800 Subject: [PATCH 15/35] Implemented the Fat Jar --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 828f9a059c..df83dc3a5a 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,7 @@ application { } shadowJar { + archiveFileName = 'martin.jar' archiveBaseName = "martin" archiveClassifier = null dependsOn("distZip", "distTar") From 2b2ea68dacaf0c77fc521fce45b4033796cc7404 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 3 Sep 2023 03:54:08 +0800 Subject: [PATCH 16/35] Add JavaDoc comments to the code --- .vscode/settings.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..c5f3f6b9c7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file From e4b277c97bdbe49da25cd4c5565af0762abe683f Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 3 Sep 2023 04:08:48 +0800 Subject: [PATCH 17/35] Added Find Command --- .vscode/settings.json | 3 -- data/martin.txt | 1 + src/main/java/martin/Ui.java | 3 ++ .../java/martin/commands/FindCommand.java | 38 +++++++++++++++++++ src/main/java/martin/parser/ListCommand.java | 5 --- src/main/java/martin/parser/Parser.java | 3 ++ src/main/java/martin/task/Task.java | 4 ++ 7 files changed, 49 insertions(+), 8 deletions(-) delete mode 100644 .vscode/settings.json create mode 100644 src/main/java/martin/commands/FindCommand.java delete mode 100644 src/main/java/martin/parser/ListCommand.java diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index c5f3f6b9c7..0000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "java.configuration.updateBuildConfiguration": "interactive" -} \ No newline at end of file diff --git a/data/martin.txt b/data/martin.txt index 2c4c4de310..c4dd5c9df0 100644 --- a/data/martin.txt +++ b/data/martin.txt @@ -1,2 +1,3 @@ 1 | test D | 0 | return book | 02/12/2019 1800 +0 | lol diff --git a/src/main/java/martin/Ui.java b/src/main/java/martin/Ui.java index 888e94d014..9a0e88dcf7 100644 --- a/src/main/java/martin/Ui.java +++ b/src/main/java/martin/Ui.java @@ -1,6 +1,9 @@ package martin; +import java.util.ArrayList; import java.util.Scanner; +import martin.task.Task; + public class Ui { private Scanner scanner; diff --git a/src/main/java/martin/commands/FindCommand.java b/src/main/java/martin/commands/FindCommand.java new file mode 100644 index 0000000000..b1ac85f193 --- /dev/null +++ b/src/main/java/martin/commands/FindCommand.java @@ -0,0 +1,38 @@ +package martin.commands; + +import martin.Ui; +import martin.task.Task; + +import java.util.ArrayList; + +public class FindCommand implements Command { + + private Ui ui; + private String command; + private ArrayList tasks; + + public FindCommand(String command, ArrayList tasks) { + this.ui = new Ui(); + this.command = command; + this.tasks = tasks; + } + + @Override + public void execute() { + ArrayList matchingTasks = new ArrayList<>(); + String keyword = command.substring(5); + + for (Task task : tasks) { + if (task.contains(keyword)) { + matchingTasks.add(task); + } + } + + ui.showLine(); + System.out.println(" Here are the matching tasks in your list:"); + for (int i = 0; i < matchingTasks.size(); i++) { + System.out.println(" " + (i + 1) + "." + matchingTasks.get(i).toString()); + } + ui.showLine(); + } +} diff --git a/src/main/java/martin/parser/ListCommand.java b/src/main/java/martin/parser/ListCommand.java deleted file mode 100644 index 945ac94228..0000000000 --- a/src/main/java/martin/parser/ListCommand.java +++ /dev/null @@ -1,5 +0,0 @@ -package martin.parser; - -public class ListCommand { - -} diff --git a/src/main/java/martin/parser/Parser.java b/src/main/java/martin/parser/Parser.java index 9a2a1cae1c..5ac8314736 100644 --- a/src/main/java/martin/parser/Parser.java +++ b/src/main/java/martin/parser/Parser.java @@ -17,6 +17,7 @@ import martin.commands.DeadlineCommand; import martin.commands.DeleteCommand; import martin.commands.EventCommand; +import martin.commands.FindCommand; public class Parser { @@ -61,6 +62,8 @@ public Command parse(String input) throws MartinException { return new EventCommand(input, tasks); case "date": return new DateCommand(input, tasks); + case "find": + return new FindCommand(input, tasks); default: throw new InvalidCommandException("Unknown command: " + input); } diff --git a/src/main/java/martin/task/Task.java b/src/main/java/martin/task/Task.java index a228f085f0..dbf0c29435 100644 --- a/src/main/java/martin/task/Task.java +++ b/src/main/java/martin/task/Task.java @@ -28,6 +28,10 @@ public void unmarkAsDone() { isDone = false; } + public boolean contains(String keyword) { + return description.contains(keyword); + } + /** * Convert the task to its file format representation. * @return String representation of task for file storage. From 6b3999427f729142fdd53068dd9b354442e72269 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Mon, 4 Sep 2023 18:09:54 +0800 Subject: [PATCH 18/35] Added Header comments for Exception classes --- src/main/java/martin/Storage.java | 1 + src/main/java/martin/Ui.java | 3 --- .../martin/exceptions/EmptyTaskDescriptionException.java | 5 +++++ .../java/martin/exceptions/InvalidCommandException.java | 5 +++++ .../java/martin/exceptions/InvalidDateFormatException.java | 5 +++++ .../java/martin/exceptions/InvalidTaskNumberException.java | 5 +++++ src/main/java/martin/exceptions/MartinException.java | 5 +++++ .../java/martin/exceptions/TaskAlreadyDoneException.java | 5 +++++ src/main/java/martin/exceptions/TaskNotDoneException.java | 6 ++++++ src/main/java/martin/parser/Parser.java | 1 + src/main/java/martin/task/Task.java | 2 ++ 11 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/main/java/martin/Storage.java b/src/main/java/martin/Storage.java index ece45d00bd..54b267ddb1 100644 --- a/src/main/java/martin/Storage.java +++ b/src/main/java/martin/Storage.java @@ -1,4 +1,5 @@ package martin; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; diff --git a/src/main/java/martin/Ui.java b/src/main/java/martin/Ui.java index 9a0e88dcf7..888e94d014 100644 --- a/src/main/java/martin/Ui.java +++ b/src/main/java/martin/Ui.java @@ -1,9 +1,6 @@ package martin; -import java.util.ArrayList; import java.util.Scanner; -import martin.task.Task; - public class Ui { private Scanner scanner; diff --git a/src/main/java/martin/exceptions/EmptyTaskDescriptionException.java b/src/main/java/martin/exceptions/EmptyTaskDescriptionException.java index dba77c4084..4af20e876c 100644 --- a/src/main/java/martin/exceptions/EmptyTaskDescriptionException.java +++ b/src/main/java/martin/exceptions/EmptyTaskDescriptionException.java @@ -1,5 +1,10 @@ package martin.exceptions; +/** + * Represents an exception when a task lacks a description. + * + * @param message Error message to be printed out. + */ public class EmptyTaskDescriptionException extends MartinException { public EmptyTaskDescriptionException(String message) { super(message); diff --git a/src/main/java/martin/exceptions/InvalidCommandException.java b/src/main/java/martin/exceptions/InvalidCommandException.java index a7972eae7c..13d47fd8e6 100644 --- a/src/main/java/martin/exceptions/InvalidCommandException.java +++ b/src/main/java/martin/exceptions/InvalidCommandException.java @@ -1,5 +1,10 @@ package martin.exceptions; +/** + * Represents an exception when command is invalid. + * + * @param message Error message to be printed out. + */ public class InvalidCommandException extends MartinException { public InvalidCommandException(String message) { super(message); diff --git a/src/main/java/martin/exceptions/InvalidDateFormatException.java b/src/main/java/martin/exceptions/InvalidDateFormatException.java index 29046fc924..af6977bd34 100644 --- a/src/main/java/martin/exceptions/InvalidDateFormatException.java +++ b/src/main/java/martin/exceptions/InvalidDateFormatException.java @@ -1,5 +1,10 @@ package martin.exceptions; +/** + * Represents an exception when a date is formatted wrongly. + * + * @param message Error message to be printed out. + */ public class InvalidDateFormatException extends MartinException { public InvalidDateFormatException(String message) { super(message); diff --git a/src/main/java/martin/exceptions/InvalidTaskNumberException.java b/src/main/java/martin/exceptions/InvalidTaskNumberException.java index ac60b0f94c..d31dc3d3ec 100644 --- a/src/main/java/martin/exceptions/InvalidTaskNumberException.java +++ b/src/main/java/martin/exceptions/InvalidTaskNumberException.java @@ -1,5 +1,10 @@ package martin.exceptions; +/** + * Represents an exception when the given task number is invalid. + * + * @param message Error message to be printed out. + */ public class InvalidTaskNumberException extends MartinException { public InvalidTaskNumberException(String message) { super(message); diff --git a/src/main/java/martin/exceptions/MartinException.java b/src/main/java/martin/exceptions/MartinException.java index 80487f820c..63eee07181 100644 --- a/src/main/java/martin/exceptions/MartinException.java +++ b/src/main/java/martin/exceptions/MartinException.java @@ -1,5 +1,10 @@ package martin.exceptions; +/** + * Represents an exception that is raised when running Martin chatbot. + * + * @param message Error message to be printed out. + */ public class MartinException extends Exception { public MartinException(String message) { super(message); diff --git a/src/main/java/martin/exceptions/TaskAlreadyDoneException.java b/src/main/java/martin/exceptions/TaskAlreadyDoneException.java index cea9ece7ba..3be94a51d0 100644 --- a/src/main/java/martin/exceptions/TaskAlreadyDoneException.java +++ b/src/main/java/martin/exceptions/TaskAlreadyDoneException.java @@ -1,5 +1,10 @@ package martin.exceptions; +/** + * Represents an exception when a marked task is marked again. + * + * @param message Error message to be printed out. + */ public class TaskAlreadyDoneException extends MartinException { public TaskAlreadyDoneException(String message) { super(message); diff --git a/src/main/java/martin/exceptions/TaskNotDoneException.java b/src/main/java/martin/exceptions/TaskNotDoneException.java index 228f427b4d..7941e4663c 100644 --- a/src/main/java/martin/exceptions/TaskNotDoneException.java +++ b/src/main/java/martin/exceptions/TaskNotDoneException.java @@ -1,5 +1,11 @@ package martin.exceptions; + +/** + * Represents an exception when an unmarked task is unmarked again. + * + * @param message Error message to be printed out. + */ public class TaskNotDoneException extends MartinException { public TaskNotDoneException(String message) { super(message); diff --git a/src/main/java/martin/parser/Parser.java b/src/main/java/martin/parser/Parser.java index 5ac8314736..f3de6b1c4e 100644 --- a/src/main/java/martin/parser/Parser.java +++ b/src/main/java/martin/parser/Parser.java @@ -30,6 +30,7 @@ public Parser(TaskList taskList) { /** * Parses the user input and returns the appropriate Command object. + * * @param input The entire string of the user's input. * @throws MartinException When the command is invalid or there's an error. * @return Command The appropriate command to be executed. diff --git a/src/main/java/martin/task/Task.java b/src/main/java/martin/task/Task.java index dbf0c29435..bf2fc0e756 100644 --- a/src/main/java/martin/task/Task.java +++ b/src/main/java/martin/task/Task.java @@ -34,6 +34,7 @@ public boolean contains(String keyword) { /** * Convert the task to its file format representation. + * * @return String representation of task for file storage. */ public String toFileFormat() { @@ -42,6 +43,7 @@ public String toFileFormat() { /** * Convert a file format string back to a Task. + * * @param fileFormatString String representation from file. * @return A new Task instance. */ From 097541b557e56bc77c78ec5d97b422f3acc7ca50 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 10 Sep 2023 01:25:43 +0800 Subject: [PATCH 19/35] Changed Martin Bot from CLI to a GUI interface --- .vscode/settings.json | 3 + build.gradle | 33 +++- src/main/java/martin/Martin.java | 41 ++--- src/main/java/martin/Storage.java | 41 ++--- src/main/java/martin/Ui.java | 37 ----- src/main/java/martin/commands/ByeCommand.java | 20 --- src/main/java/martin/commands/Command.java | 2 +- .../java/martin/commands/DateCommand.java | 21 ++- .../java/martin/commands/DeadlineCommand.java | 12 +- .../java/martin/commands/DeleteCommand.java | 8 +- .../java/martin/commands/EventCommand.java | 8 +- .../java/martin/commands/FindCommand.java | 18 ++- .../java/martin/commands/ListCommand.java | 16 +- .../java/martin/commands/MarkCommand.java | 10 +- .../java/martin/commands/TodoCommand.java | 8 +- .../java/martin/commands/UnmarkCommand.java | 12 +- src/main/java/martin/gui/DialogBox.java | 63 ++++++++ src/main/java/martin/gui/Launcher.java | 12 ++ src/main/java/martin/gui/MainWindow.java | 149 ++++++++++++++++++ src/main/java/martin/parser/Parser.java | 3 - src/main/resources/images/hikaru.png | Bin 0 -> 5623 bytes src/main/resources/images/martin.png | Bin 0 -> 5680 bytes 22 files changed, 342 insertions(+), 175 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 src/main/java/martin/Ui.java delete mode 100644 src/main/java/martin/commands/ByeCommand.java create mode 100644 src/main/java/martin/gui/DialogBox.java create mode 100644 src/main/java/martin/gui/Launcher.java create mode 100644 src/main/java/martin/gui/MainWindow.java create mode 100644 src/main/resources/images/hikaru.png create mode 100644 src/main/resources/images/martin.png diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..c5f3f6b9c7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index df83dc3a5a..c52443df87 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'java' id 'application' id 'com.github.johnrengelman.shadow' version '7.1.2' + id 'org.openjfx.javafxplugin' version '0.0.13' } repositories { @@ -9,6 +10,20 @@ repositories { } dependencies { + String javaFxVersion = '17.0.7' + + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.10.0' testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.10.0' } @@ -38,6 +53,22 @@ shadowJar { dependsOn("distZip", "distTar") } -run{ +javafx { + version = "17.0.7" + modules = [ 'javafx.controls' ] +} + +sourceSets { + main { + java { + srcDirs = ['src/main/java'] + } + resources { + srcDirs = ['src/main/resources'] + } + } +} + +run { standardInput = System.in } diff --git a/src/main/java/martin/Martin.java b/src/main/java/martin/Martin.java index df311d8791..f725811d46 100644 --- a/src/main/java/martin/Martin.java +++ b/src/main/java/martin/Martin.java @@ -1,5 +1,7 @@ package martin; +import java.io.IOException; + import martin.commands.Command; import martin.parser.Parser; import martin.task.TaskList; @@ -7,42 +9,33 @@ public class Martin { private Storage storage; private TaskList tasks; - private Ui ui; private Parser parser; public Martin(String filePath) { - ui = new Ui(); storage = new Storage(filePath); try { tasks = new TaskList(storage.loadFromFile()); parser = new Parser(tasks); } catch (Exception e) { - ui.showLoadingError(); tasks = new TaskList(); } } - public void run() { - ui.showWelcome(); - boolean isExit = false; - while (!isExit) { - try { - String command = ui.readCommand(); - Command cmd = parser.parse(command); - cmd.execute(); - - if ("bye".equalsIgnoreCase(command.trim())) { - isExit = true; - } - } catch (Exception e) { - ui.showError(e.getMessage()); - } finally { - storage.saveToFile(tasks.getTasks()); - } + /** + * Processes the given input and returns a response. + * + * @param input User's input string. + * @return Response message. + * @throws IOException + */ + public String getResponse(String input) throws IOException { + try { + Command cmd = parser.parse(input); + return cmd.execute(); // Assuming execute() returns a String + } catch (Exception e) { + return e.getMessage(); + } finally { + storage.saveToFile(tasks.getTasks()); } } - - public static void main(String[] args) { - new Martin("data/martin.txt").run(); - } } diff --git a/src/main/java/martin/Storage.java b/src/main/java/martin/Storage.java index 54b267ddb1..cc4720bc75 100644 --- a/src/main/java/martin/Storage.java +++ b/src/main/java/martin/Storage.java @@ -13,59 +13,48 @@ public class Storage { private String filePath; - private Ui ui; public Storage(String filePath) { this.filePath = filePath; - this.ui = new Ui(); } /** * Saves the given list of tasks to the file. * * @param tasks the list of tasks to save. + * @throws IOException if there's any issue with writing to the file. */ - public void saveToFile(List tasks) { + public void saveToFile(List tasks) throws IOException { List lines = new ArrayList<>(); for (Task task : tasks) { - lines.add(task.toFileFormat()); + lines.add(task.toFileFormat()); } Path path = Paths.get(filePath); - try { - if (!Files.exists(path)) { - Files.createDirectories(path.getParent()); - Files.createFile(path); - } - Files.write(path, lines, StandardOpenOption.TRUNCATE_EXISTING); - } catch (IOException e) { - ui.printMessage("Error saving tasks to file."); + if (!Files.exists(path)) { + Files.createDirectories(path.getParent()); + Files.createFile(path); } + Files.write(path, lines, StandardOpenOption.TRUNCATE_EXISTING); } /** * Loads tasks from the file. * * @return a list of tasks loaded from the file. + * @throws IOException if there's any issue with reading from the file. + * @throws IllegalArgumentException if there's a format issue with the data. */ - public ArrayList loadFromFile() { + public ArrayList loadFromFile() throws IOException, IllegalArgumentException { Path path = Paths.get(filePath); ArrayList tasks = new ArrayList<>(); if (Files.exists(path)) { - try { - List lines = Files.readAllLines(path); - for (String line : lines) { - if (line.trim().isEmpty()) - continue; - try { - tasks.add(Task.fromFileFormat(line)); - } catch (IllegalArgumentException e) { - ui.printMessage("Data file might be corrupted. (i.e., content not in expected format.)"); - } - } - } catch (IOException e) { - ui.printMessage("Error reading tasks from file."); + List lines = Files.readAllLines(path); + for (String line : lines) { + if (line.trim().isEmpty()) + continue; + tasks.add(Task.fromFileFormat(line)); } } diff --git a/src/main/java/martin/Ui.java b/src/main/java/martin/Ui.java deleted file mode 100644 index 888e94d014..0000000000 --- a/src/main/java/martin/Ui.java +++ /dev/null @@ -1,37 +0,0 @@ -package martin; -import java.util.Scanner; - -public class Ui { - - private Scanner scanner; - - public Ui() { - this.scanner = new Scanner(System.in); - } - - public void showWelcome() { - printMessage("Hello! I'm Martin\n What can I do for you?"); - } - - public String readCommand() { - return scanner.nextLine().trim(); - } - - public void showLine() { - System.out.println(" ____________________________________________________________"); - } - - public void showError(String message) { - printMessage("Error: " + message); - } - - public void showLoadingError() { - printMessage("Problem encountered while loading data!"); - } - - public void printMessage(String message) { - System.out.println(" ____________________________________________________________"); - System.out.println(" " + message); - System.out.println(" ____________________________________________________________"); - } -} diff --git a/src/main/java/martin/commands/ByeCommand.java b/src/main/java/martin/commands/ByeCommand.java deleted file mode 100644 index fcfdbbb13e..0000000000 --- a/src/main/java/martin/commands/ByeCommand.java +++ /dev/null @@ -1,20 +0,0 @@ -package martin.commands; - -import martin.Ui; - -public class ByeCommand implements Command { - - private Ui ui; - - public ByeCommand() { - this.ui = new Ui(); - } - - /** - * Exits the chatbot. - */ - @Override - public void execute() { - ui.printMessage("Bye. Hope to see you again soon!"); - } -} diff --git a/src/main/java/martin/commands/Command.java b/src/main/java/martin/commands/Command.java index f052f22cc4..cbcd85832a 100644 --- a/src/main/java/martin/commands/Command.java +++ b/src/main/java/martin/commands/Command.java @@ -6,5 +6,5 @@ public interface Command { /** * Executes the specific command. */ - void execute() throws MartinException; + String execute() throws MartinException; } \ No newline at end of file diff --git a/src/main/java/martin/commands/DateCommand.java b/src/main/java/martin/commands/DateCommand.java index acef9f995a..5feecda028 100644 --- a/src/main/java/martin/commands/DateCommand.java +++ b/src/main/java/martin/commands/DateCommand.java @@ -1,6 +1,5 @@ package martin.commands; -import martin.Ui; import martin.exceptions.InvalidDateFormatException; import martin.exceptions.MartinException; import martin.task.Deadline; @@ -13,31 +12,31 @@ public class DateCommand implements Command { - private Ui ui; private String dateStr; private ArrayList tasks; public DateCommand(String dateStr, ArrayList tasks) { - this.ui = new Ui(); this.dateStr = dateStr; this.tasks = tasks; } + /** - * Prints the tasks that are scheduled on a specific date. + * Returns a String of the tasks that are scheduled on a specific date. + * @return String A formatted string containing the tasks on the specified date. */ @Override - public void execute() throws MartinException { + public String execute() throws MartinException { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yyyy"); LocalDate date; + StringBuilder response = new StringBuilder(); try { date = LocalDate.parse(dateStr, formatter); } catch (DateTimeParseException e) { - throw new InvalidDateFormatException(" Invalid date format. Please use the format 'd/M/yyyy'."); + throw new InvalidDateFormatException("Invalid date format. Please use the format 'd/M/yyyy'."); } - ui.showLine(); - System.out.println(" Tasks on " + date.format(DateTimeFormatter.ofPattern("M d yyyy")) + ":"); + response.append("Tasks on ").append(date.format(DateTimeFormatter.ofPattern("M d yyyy"))).append(":\n"); int count = 0; boolean hasTasks = false; @@ -45,7 +44,7 @@ public void execute() throws MartinException { if (task instanceof Deadline) { Deadline d = (Deadline) task; if (d.getBy().toLocalDate().equals(date)) { - System.out.println(" " + (count + 1) + ". " + task); + response.append((count + 1)).append(". ").append(task).append("\n"); hasTasks = true; } } @@ -53,9 +52,9 @@ public void execute() throws MartinException { } if (!hasTasks) { - System.out.println(" No tasks on this date."); + response.append("No tasks on this date.\n"); } - ui.showLine(); + return response.toString(); } } diff --git a/src/main/java/martin/commands/DeadlineCommand.java b/src/main/java/martin/commands/DeadlineCommand.java index 98ab413ed1..ec50a096b4 100644 --- a/src/main/java/martin/commands/DeadlineCommand.java +++ b/src/main/java/martin/commands/DeadlineCommand.java @@ -1,6 +1,5 @@ package martin.commands; -import martin.Ui; import martin.exceptions.EmptyTaskDescriptionException; import martin.exceptions.MartinException; import martin.task.Deadline; @@ -10,21 +9,20 @@ public class DeadlineCommand implements Command { - private Ui ui; private String command; private ArrayList tasks; public DeadlineCommand(String command, ArrayList tasks) { - this.ui = new Ui(); this.command = command; this.tasks = tasks; } /** - * Adds a new Deadline task to the task list. + * Adds a new Deadline task to the task list and returns a confirmation message. + * @return String A confirmation message of the added Deadline task. **/ @Override - public void execute() throws MartinException { + public String execute() throws MartinException { if (command.length() <= 8) { throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a deadline cannot be empty."); } @@ -35,6 +33,6 @@ public void execute() throws MartinException { } tasks.add(new Deadline(parts[0], parts[1])); - ui.printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); + return "Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."; } -} \ No newline at end of file +} diff --git a/src/main/java/martin/commands/DeleteCommand.java b/src/main/java/martin/commands/DeleteCommand.java index dfe8130f26..8c2d7ac083 100644 --- a/src/main/java/martin/commands/DeleteCommand.java +++ b/src/main/java/martin/commands/DeleteCommand.java @@ -1,6 +1,5 @@ package martin.commands; -import martin.Ui; import martin.exceptions.InvalidTaskNumberException; import martin.exceptions.MartinException; import martin.task.Task; @@ -9,21 +8,20 @@ public class DeleteCommand implements Command { - private Ui ui; private String command; private ArrayList tasks; public DeleteCommand(String command, ArrayList tasks) { - this.ui = new Ui(); this.command = command; this.tasks = tasks; } /** * Deletes the task at the specified index provided by the command. + * @return String A confirmation message of the deleted task. */ @Override - public void execute() throws MartinException { + public String execute() throws MartinException { try { int taskNo = Integer.parseInt(command.split(" ")[1]); @@ -32,7 +30,7 @@ public void execute() throws MartinException { } Task removedTask = tasks.remove(taskNo - 1); - ui.printMessage("Noted. I've removed this task:\n " + removedTask + "\n Now you have " + tasks.size() + " tasks in the list."); + return "Noted. I've removed this task:\n " + removedTask + "\n Now you have " + tasks.size() + " tasks in the list."; } catch (Exception e) { throw new InvalidTaskNumberException("Invalid task number."); } diff --git a/src/main/java/martin/commands/EventCommand.java b/src/main/java/martin/commands/EventCommand.java index df3ae890e9..433899df6c 100644 --- a/src/main/java/martin/commands/EventCommand.java +++ b/src/main/java/martin/commands/EventCommand.java @@ -1,6 +1,5 @@ package martin.commands; -import martin.Ui; import martin.exceptions.EmptyTaskDescriptionException; import martin.exceptions.MartinException; import martin.task.Event; @@ -10,21 +9,20 @@ public class EventCommand implements Command { - private Ui ui; private String command; private ArrayList tasks; public EventCommand(String command, ArrayList tasks) { - this.ui = new Ui(); this.command = command; this.tasks = tasks; } /** * Adds a new Event task to the task list. + * @return String A confirmation message of the added event. */ @Override - public void execute() throws MartinException { + public String execute() throws MartinException { if (command.length() <= 5) { throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of an event cannot be empty."); } @@ -36,6 +34,6 @@ public void execute() throws MartinException { } tasks.add(new Event(parts[0], timeParts[0], timeParts[1])); - ui.printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); + return "Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."; } } diff --git a/src/main/java/martin/commands/FindCommand.java b/src/main/java/martin/commands/FindCommand.java index b1ac85f193..b0ec7039a9 100644 --- a/src/main/java/martin/commands/FindCommand.java +++ b/src/main/java/martin/commands/FindCommand.java @@ -1,24 +1,25 @@ package martin.commands; -import martin.Ui; import martin.task.Task; import java.util.ArrayList; public class FindCommand implements Command { - private Ui ui; private String command; private ArrayList tasks; public FindCommand(String command, ArrayList tasks) { - this.ui = new Ui(); this.command = command; this.tasks = tasks; } + /** + * Finds tasks that match the given keyword. + * @return String The list of matching tasks. + */ @Override - public void execute() { + public String execute() { ArrayList matchingTasks = new ArrayList<>(); String keyword = command.substring(5); @@ -28,11 +29,12 @@ public void execute() { } } - ui.showLine(); - System.out.println(" Here are the matching tasks in your list:"); + StringBuilder result = new StringBuilder(); + result.append("Here are the matching tasks in your list:\n"); for (int i = 0; i < matchingTasks.size(); i++) { - System.out.println(" " + (i + 1) + "." + matchingTasks.get(i).toString()); + result.append((i + 1) + "." + matchingTasks.get(i).toString()).append("\n"); } - ui.showLine(); + return result.toString().trim(); } } + diff --git a/src/main/java/martin/commands/ListCommand.java b/src/main/java/martin/commands/ListCommand.java index 8673a40757..a0585095eb 100644 --- a/src/main/java/martin/commands/ListCommand.java +++ b/src/main/java/martin/commands/ListCommand.java @@ -1,29 +1,29 @@ package martin.commands; -import martin.Ui; import martin.task.Task; import java.util.ArrayList; public class ListCommand implements Command { - private Ui ui; private ArrayList tasks; public ListCommand(ArrayList tasks) { - this.ui = new Ui(); this.tasks = tasks; } /** - * Prints all tasks currently in the list. + * Lists all tasks currently in the list. + * @return String The list of all tasks. */ @Override - public void execute() { - ui.showLine(); + public String execute() { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < tasks.size(); i++) { - System.out.println(" " + (i + 1) + ". " + tasks.get(i)); + result.append((i + 1) + ". " + tasks.get(i)).append("\n"); } - ui.showLine(); + + return result.toString().trim(); } } diff --git a/src/main/java/martin/commands/MarkCommand.java b/src/main/java/martin/commands/MarkCommand.java index 91e9cdbb0a..37570940fa 100644 --- a/src/main/java/martin/commands/MarkCommand.java +++ b/src/main/java/martin/commands/MarkCommand.java @@ -1,6 +1,5 @@ package martin.commands; -import martin.Ui; import martin.exceptions.InvalidTaskNumberException; import martin.exceptions.MartinException; import martin.exceptions.TaskAlreadyDoneException; @@ -10,21 +9,20 @@ public class MarkCommand implements Command { - private Ui ui; private String command; private ArrayList tasks; public MarkCommand(String command, ArrayList tasks) { - this.ui = new Ui(); this.command = command; this.tasks = tasks; } /** * Marks the task at the specified index as done. + * @return String A message indicating the task has been marked as done or an error message. */ @Override - public void execute() throws MartinException { + public String execute() throws MartinException { try { int taskNo = Integer.parseInt(command.split(" ")[1]); if (taskNo <= 0 || taskNo > tasks.size()) { @@ -37,9 +35,9 @@ public void execute() throws MartinException { } task.markAsDone(); - ui.printMessage("Nice! I've marked this task as done:\n " + task); + return "Nice! I've marked this task as done:\n " + task; } catch (Exception e) { - ui.printMessage(e.getMessage()); + return e.getMessage(); } } } diff --git a/src/main/java/martin/commands/TodoCommand.java b/src/main/java/martin/commands/TodoCommand.java index 252441382d..7c75c38d6d 100644 --- a/src/main/java/martin/commands/TodoCommand.java +++ b/src/main/java/martin/commands/TodoCommand.java @@ -1,6 +1,5 @@ package martin.commands; -import martin.Ui; import martin.exceptions.EmptyTaskDescriptionException; import martin.exceptions.MartinException; import martin.task.Task; @@ -10,21 +9,20 @@ public class TodoCommand implements Command { - private Ui ui; private String command; private ArrayList tasks; public TodoCommand(String command, ArrayList tasks) { - this.ui = new Ui(); this.command = command; this.tasks = tasks; } /** * Adds a new ToDo task to the task list. + * @return String A message indicating the task has been added or an error message. */ @Override - public void execute() throws MartinException { + public String execute() throws MartinException { if (command.length() <= 4) { throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a todo cannot be empty."); } @@ -35,6 +33,6 @@ public void execute() throws MartinException { } tasks.add(new Todo(description)); - ui.printMessage("Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."); + return "Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."; } } diff --git a/src/main/java/martin/commands/UnmarkCommand.java b/src/main/java/martin/commands/UnmarkCommand.java index 6145e2a7ec..5844d25bce 100644 --- a/src/main/java/martin/commands/UnmarkCommand.java +++ b/src/main/java/martin/commands/UnmarkCommand.java @@ -1,6 +1,5 @@ package martin.commands; -import martin.Ui; import martin.exceptions.InvalidTaskNumberException; import martin.exceptions.MartinException; import martin.exceptions.TaskNotDoneException; @@ -10,23 +9,20 @@ public class UnmarkCommand implements Command { - private Ui ui; private String command; private ArrayList tasks; public UnmarkCommand(String command, ArrayList tasks) { - this.ui = new Ui(); this.command = command; this.tasks = tasks; } /** * Unmarks the task at the specified index, marking it as not done. - * @param command The user input containing the task index to unmark. - * @param tasks The Array List of tasks in the task list. + * @return String A message indicating the task has been unmarked or an error message. */ @Override - public void execute() throws MartinException { + public String execute() throws MartinException { try { int taskNo = Integer.parseInt(command.split(" ")[1]); if (taskNo <= 0 || taskNo > tasks.size()) { @@ -39,9 +35,9 @@ public void execute() throws MartinException { } task.unmarkAsDone(); - ui.printMessage("OK, I've marked this task as not done yet:\n " + task); + return "OK, I've marked this task as not done yet:\n " + task; } catch (Exception e) { - ui.printMessage(e.getMessage()); + return e.getMessage(); } } } diff --git a/src/main/java/martin/gui/DialogBox.java b/src/main/java/martin/gui/DialogBox.java new file mode 100644 index 0000000000..364d6f1229 --- /dev/null +++ b/src/main/java/martin/gui/DialogBox.java @@ -0,0 +1,63 @@ +package martin.gui; + +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; + +/** + * Represents a dialog box in the GUI for Martin. It can represent either user input or Martin's response. + */ +public class DialogBox extends HBox { + + private Label text; + private ImageView displayPicture; + + /** + * Creates a dialog box with specified label and image view. + * + * @param l The label containing text for the dialog. + * @param iv The image view displaying the avatar for the dialog. + */ + private DialogBox(Label l, ImageView iv) { + text = l; + displayPicture = iv; + + text.setWrapText(true); + displayPicture.setFitWidth(50.0); + displayPicture.setFitHeight(50.0); + text.setMinHeight(Region.USE_PREF_SIZE); + + this.getChildren().addAll(text, displayPicture); + } + + /** + * Returns a dialog box for the user with the given text and image. + * + * @param text The text to be displayed in the user dialog box. + * @param img The image to be displayed as the user's avatar. + * @return A dialog box representing the user's input. + */ + public static DialogBox getUserDialog(String text, Image img) { + var db = new DialogBox(new Label(text), new ImageView(img)); + db.setAlignment(Pos.TOP_LEFT); + db.getChildren().setAll(db.displayPicture, db.text); + return db; + } + + /** + * Returns a dialog box for Martin with the given text and image. + * + * @param text The text to be displayed in Martin's dialog box. + * @param img The image to be displayed as Martin's avatar. + * @return A dialog box representing Martin's response. + */ + public static DialogBox getMartinDialog(String text, Image img) { + var db = new DialogBox(new Label(text), new ImageView(img)); + db.setAlignment(Pos.TOP_LEFT); + return db; + } +} + diff --git a/src/main/java/martin/gui/Launcher.java b/src/main/java/martin/gui/Launcher.java new file mode 100644 index 0000000000..94c47ed82a --- /dev/null +++ b/src/main/java/martin/gui/Launcher.java @@ -0,0 +1,12 @@ +package martin.gui; + +import javafx.application.Application; + +/** + * A launcher class to workaround classpath issues. + */ +public class Launcher { + public static void main(String[] args) { + Application.launch(MainWindow.class, args); + } +} diff --git a/src/main/java/martin/gui/MainWindow.java b/src/main/java/martin/gui/MainWindow.java new file mode 100644 index 0000000000..ddda91f869 --- /dev/null +++ b/src/main/java/martin/gui/MainWindow.java @@ -0,0 +1,149 @@ +package martin.gui; + +import java.io.IOException; + +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; +import martin.Martin; + +public class MainWindow extends Application { + + private ScrollPane scrollPane; + private VBox dialogContainer; + private TextField userInput; + private Button sendButton; + private Scene scene; + private Martin martin = new Martin("data/martin.txt"); + + Image martinImage = new Image(getClass().getResourceAsStream("/images/martin.png"), 50, 50, true, true); + Image hikaruImage = new Image(getClass().getResourceAsStream("/images/hikaru.png"), 50, 50, true, true); + + /* + * Launch the GUI. + */ + public static void main(String[] args) { + launch(args); + } + + @Override + public void start(Stage stage) { + //Step 1. Setting up required components + + //The container for the content of the chat to scroll. + scrollPane = new ScrollPane(); + dialogContainer = new VBox(); + scrollPane.setContent(dialogContainer); + + userInput = new TextField(); + sendButton = new Button("Send"); + + AnchorPane mainLayout = new AnchorPane(); + mainLayout.getChildren().addAll(scrollPane, userInput, sendButton); + + scene = new Scene(mainLayout); + + stage.setScene(scene); + stage.show(); + + //Step 2. Formatting the window to look as expected + stage.setTitle("Duke"); + stage.setResizable(false); + stage.setMinHeight(600.0); + stage.setMinWidth(400.0); + + mainLayout.setPrefSize(400.0, 600.0); + + scrollPane.setPrefSize(385, 535); + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); + + scrollPane.setVvalue(1.0); + scrollPane.setFitToWidth(true); + + // You will need to import `javafx.scene.layout.Region` for this. + dialogContainer.setPrefHeight(Region.USE_COMPUTED_SIZE); + + userInput.setPrefWidth(325.0); + + sendButton.setPrefWidth(55.0); + + AnchorPane.setTopAnchor(scrollPane, 1.0); + + AnchorPane.setBottomAnchor(sendButton, 1.0); + AnchorPane.setRightAnchor(sendButton, 1.0); + + AnchorPane.setLeftAnchor(userInput , 1.0); + AnchorPane.setBottomAnchor(userInput, 1.0); + + //Step 3. Add functionality to handle user input. + sendButton.setOnMouseClicked((event) -> { + try { + handleUserInput(); + } catch (IOException e) { + showErrorDialog("An error occurred while processing your input. Please try again."); + } + }); + + userInput.setOnAction((event) -> { + try { + handleUserInput(); + } catch (IOException e) { + showErrorDialog("An error occurred while processing your input. Please try again."); + } + }); + + // Display a welcome message when the app starts + dialogContainer.getChildren().addAll( + DialogBox.getMartinDialog("Hello! I'm Martin. How can I assist you today?", martinImage) + ); + } + + /** + * Input processing of user, and displaying of the user's text and Martin's reply. + * @throws IOException + */ + private void handleUserInput() throws IOException { + String input = userInput.getText(); + String response = getResponse(input); + + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, hikaruImage), + DialogBox.getMartinDialog(response, martinImage) + ); + + scrollPane.setVvalue(1.0); + userInput.clear(); + } + + /** + * Execute getResponse in Martin based on the given input. + * + * @throws IOException + */ + private String getResponse(String input) throws IOException { + return martin.getResponse(input); + } + + /** + * Displays an error dialog with a specified error message. + * + * @param errorMessage The specific error message to be shown in the dialog. + */ + private void showErrorDialog(String errorMessage) { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle("Error"); + alert.setHeaderText("Something went wrong!"); + alert.setContentText(errorMessage); + + alert.showAndWait(); + } +} diff --git a/src/main/java/martin/parser/Parser.java b/src/main/java/martin/parser/Parser.java index f3de6b1c4e..ce86048085 100644 --- a/src/main/java/martin/parser/Parser.java +++ b/src/main/java/martin/parser/Parser.java @@ -7,7 +7,6 @@ import java.util.ArrayList; -import martin.commands.ByeCommand; import martin.commands.ListCommand; import martin.commands.MarkCommand; import martin.commands.TodoCommand; @@ -45,8 +44,6 @@ public Command parse(String input) throws MartinException { ArrayList tasks = taskList.getTasks(); switch (command) { - case "bye": - return new ByeCommand(); case "list": return new ListCommand(tasks); case "delete": diff --git a/src/main/resources/images/hikaru.png b/src/main/resources/images/hikaru.png new file mode 100644 index 0000000000000000000000000000000000000000..f2fe5674ad31e59d0c1a3edafddbc2ee3db08050 GIT binary patch literal 5623 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 R4#EHc002ovPDHLkV1hRl{9*tA literal 0 HcmV?d00001 diff --git a/src/main/resources/images/martin.png b/src/main/resources/images/martin.png new file mode 100644 index 0000000000000000000000000000000000000000..cb2057a9234b613c107639a4d31be09d6884eae5 GIT binary patch literal 5680 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 Date: Wed, 13 Sep 2023 17:28:44 +0800 Subject: [PATCH 20/35] Implement assertions for null values - Added assertions to handle null values for input in the MainWindow class. - Ensured null responses are appropriately handled in the Martin class. - Introduced error handling for scenarios where the input is empty. --- .vscode/settings.json | 3 --- gradle/wrapper/gradle-wrapper.jar | Bin 63375 -> 61624 bytes gradle/wrapper/gradle-wrapper.properties | 1 - gradlew | 17 +++++------- src/main/java/martin/Martin.java | 8 +++++- src/main/java/martin/gui/MainWindow.java | 33 ++++++++++++++++------- 6 files changed, 37 insertions(+), 25 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index c5f3f6b9c7..0000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "java.configuration.updateBuildConfiguration": "interactive" -} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 033e24c4cdf41af1ab109bc7f253b2b887023340..afba109285af78dbd2a1d187e33ac4f87c76e392 100644 GIT binary patch delta 24872 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 HzpnldrMqp; delta 26705 zcmY&<18`@-vuF(+IrhOkg;RYO1Sq>Zm0|<(MnS_T9iUDlc#!Zsa{wMs?AVYwFfH=BXGJ}KsKdW`j z|9%OC044k<Hje>M8N$gZSLYWCnGq6fnqd6AU8n#gJ0;W zp#MM))9^n~jU@r~&+Lxi{9j{>uo(Ow1d`tWvjhqd1Wg?nHUGm2PcI?}@Cqz0M^ zvsh5dIar$38w+nB?xe-{T!7s9f5#5p<9A) zC}gSbwkjVvJidLP;f}BS?)J{j&inPn*E6!EAmm)QGwx)Af+R*?T7$kMZHr6MZ`gg^s6@+g_yOtz5 zQOX_uM_L-`RnKgb^~&um^u_5~PP4uN62 z#>vpU`U??A@D8uSb)tOQJjvnS#a;>EjlYIz8D{V;Fz;VMlj)(%v&y0%3U z>f$S+RP&pdSYxsmx0UITY@)Q_VY*6v#se=qF0)hZHd}bo{r*vz3C=jsv8e;t)*a&5 z@(3#a%PwBaqdF3lxxSzN7dDiy79!UHsrj)a+~f3Vdn2AgrOHI}r`(fu>%cB3>mMXM z2#vo~i+G&=_4jjzYFj~rMWH!%nS@WmHqzYsL3$VEgI#uIg>hE6{u*k_uv28CFV_zC zJUrWW@nIHS0&hg=KT)*}giNym&CqV1A;)G)s=9rb1~*O=yg5s_rEJVcG_H)E%5B`x zr9}w7`|`WROZL(VkmAAW`8&{_i@!ER5l8bR6#?vU|S#o<3H zsNAR>@QMCXCDiP}7^vOy?Ib#>4`lO{?eQSdc)>f;dqspS?c~aKLZupxRu#vew+bo* zVsyu#!|j+FeZ@aNfE{Qh2LlOW)Lw)KBfxALJ5_H{MA2QO3s1@>8CdDI+jAhp(# z+~LB9@)P<-XO9l7orTSiYe(m;$GTiSkkPB1!rL4U0^h2IT^XY+BRQO1EoCkyj@;rrXIdCBmv~=q)!3qT zY#|)@@>?-sl>7EfEAHC(0~l--{qGM2IL+G~FPDP3<`7RqiuFl^<>a_^*2S;HM_Y6C-Ppb;vWL zYkGdJEqMJEDK%tLc~emjr@KyWsXCB6n_i;t$=oRAR-+H=S7>1xn1)%DCd{C@68BCE z5Ko=&=Act7rtrZk{VYet6VquiM%cGH#3`{_#37r96lV}RI(wmviy;9$z)XosSEfZW z?e)UsJb=Lh#H=dQ8;mgyH!rNlBDsO>L83*!z@+i4NF4>2Wq|+PNiU;17g?9Sx7W8pK!r9z=h@lv}#8M7T~A8EDA(h5DFb!bxy{b$)+hvZ$XHx-gM zv1$JnFjjHri8bm6DO5hT&Qo`XwJ9dGXv82JVd=boD?c|szs(b5yB_q~(yqA2Uw30| zg*TRbh~4+Fyrifc+vKNJ{(A1SMru+k6mX}fn`XIhPmVw`W1Pp6pm9LFMj{wODmW&1 zr{rVfAH_Y~=Oq6DZ5a7|@O=Py(e*nb#in!vAgMt;D-X-vK^nJbx}>rH&6d~B8zpRK zACCxwsqYL_K)db?bb;;JEj|XraCBCntoRdv3E@CZTiOyrU~groi^7NKY@>SmX51vVC^ZS13yWXV5A%hHAr5;^7?S`*r^S zSM#kmds$18W?Mft@Y~S&?j-Sp{J(;Cb2;1Re}Zb@AKd0%&Wrzu7k_YpW|(;Y6+3SQ zpqtNmX8)sBKKfs3VBQgGGdzFwe}Z+J!0SJY=0@S0|3IOb`hVa_g7-f`n?pFwTDfnb4Q|Tb@IhX*3Z)D>2nq?7oD2FYxx(c!<5Vbes>u6 zEbL*e--{Ox6W!QPO4>j(X^XyzP{mkQyf8=vRS0e0qXnN@hwNRR)@GcokDg=3svbum zPOLK`PB{wC5WA5?Rn8cva+tmY?`8(C8om!F3@{>z!mvEj9k=K;JIdh#MP;bgiw)5| z7)+^Sz2sGq)<^%~lU>a1&XhM1%_9e*leTPZnaQ9vf;5|4x-}`urbk_*lPi+Y^ssRe zQfrCP5(Ukrc^eiUU{xF%1BI|VT6Jt>x8VdFLd3V#ZgUp7c;~-Q*?8o~PiWyf^3HB~ z14OMo_<>ts_~OkEr*m;RtqhyuD+yos4z8B$_@}PPeOHI=?Cmm9{JLM1Uddo%SuLJY zf^&r4>KgKCg+UCbJvh?Uzf!p~9VsbFQ`P`qsh%nH@#ve<%4E26#OG&tC>dcOa`4=7 zQ`?QftjEQva{5J9%$JT=XrE|Cn#pI=1nB*>V7%!`8+n@;4L8q}?v3TCpxUO8z;g%c zPy1E~_+)Wwp63(c&i4?L+fHW*C=(;zDR(D9jVG6@-sU2~2DTs{Ak26WIrhuDpYA&jr-Uwo@l}SM zwsJ(KjEPDM*}^zZ){A2+4`U;$T4h}tK6b{IHel|-o{RRVwGU5`{9<$9$TrHZ7g|I? z;m~w4)!~1_W*PAD_J-I`muHqVQWGMI1v{k!V7oCIxQEs$xtmKa3P}!O8|rthEb$&l zWJt?GsQl|B#S*JtiuUU|K@C@T(P*^L=+xw{NlpE+2G1sY`;e=eBO)h@r!u|l&K*CFZ`XD z+uLo$=4rW*nJi|-xk1{6Jig@&=eXY5bv>9}ex++-+ZVGXlaM3Zx$9ErNGW z^FxM3A0IKta)NK8b%g@9ivhqqWq!asp?gP^&?c*z^+}zawO_x7ZdCIle?a~3S=iUG zP}vCz0)h(;0tUhi4BEj0k~q=8OoaN`9BL0d0)Mn$m~M^<5Gw?w zT5ec9|14Os$%pZZ@UDWW8{MejUc3`t_jE~QU5tDXx1e4)XUn4&;9dv=2)|6=m?N|6 zU_|d1^Rx|FS(#^5o&r;WQBCtP{GMT^P7J@!epu%=`6~HfkO0LMa{3+U1%broB@d^i zGwP05<_ieLz0Dg4zE{sFnKtwdPs}N@u>@Ll-{i0mpMEyz8f1{IGw3eLO>2LXWSY83 zF0?yT^4{8sO`y9-{c7LMqV{1Wkc$6{nkgaQ zHY(!5*e%LHoZc$^$1X{)`Yh;8D&6{k`OZFqaF?Xqbj9p%De+a=36}(XIYX8AZs8lv z$72Mx^7I`}jw(;$Rp(;5Mgvyv2APX}s#d2k1K|`c=Ue>$J*E>b)c&xLARq{EARvtY zGo%=R|8^g7ntEF3S{PqQG8rrg24rv|LD~frDAMyAng!}{G;;-MG+YZCNWV#A6)hGr zVMQIE9GGYQZ)=4HdLeW*uk^Y!0cu)W&zUk5uyO`o>ibq23_6W}D4cNp$$W5Z(e4o@x{aOaE2~?l z?Za2Br{{8Tp$s`%r}mVxcxRkgN^w7AD;p+iFV3qJla(i?s@|GxtPi@#9)Dcm0bV0Z z<>~Xs^=uSRqI}bI8C>X{2RH~f3lk(X*cak$c(<}lVAc*&jafRAJBuAEVD&lgqgqU@ zB?BgaRu%`zWe>F22{uQxgGp4Q2u;{7GYlwc;D57mH~6oDZBXB8ta&Nc+T&zIqHMySE4XWK6sTUTdg3-E@}4A61**<8`i+3U$mI{d(l+(Y=<6 zuLM8W9Sdp=mKX-@b@edV?9*x(uw_jubx6m;z#+kL)LG)Vqk?;WrpbmE12A9E*ASXz znoCZ|!q3Fr$cFY&Epw%qx{jjM8zr!2m?b!h>1M1k)bto(k(2gGeZ6w_=L{07t36?A zAruA(w=kvM!t2VHfM_+#G&KE*Mszd>HX+E5Lo`+30%cC;@z`6yj(tz$jK z90ntN*FhMeG%pzW^SqcRfS?L{n#$l=XTw2kxZYtcv|i0yM5t;fx(~Gr)K9X zsZ{nQW-X!96u#U81~*={{qSq$o6EEogW5ZT+MqZ1{&KMAaR+b*&_rlZ;rMD*(|GZu zo7ehNjnP55xAEMqO44V7DM<>^adnlkZbA5TJdI($;%!?8dj$x0%jKD@0Hvn%h`Ybx z=2I_UR%o%rm8nVg>%4*QIY-sU*JKvYdRu96K59M|@qx*ahc)%^E(cu=b-us0RZ5lH z{yJ_zShJfQfdionaBNwf>bUUHu&sB6CDvuJ8r#N|>t@p2-g&-_YaCJNO%-m>88LPe<8mM#zJ~2?Jh1AK$f%c(Z4; zW;j~dAmr8_J7-2h07FQ6F%!$|#-Tsj_kS*7T{ieSOYt!O zFvT>OnWQ|+6<;nn$#aJi@_6bWxihc-Xx=(+C~3y}h0S-6@YyFE{jMlrlCXsq-Qo)a zLKEcyM?{4d{&O6iNjk|Q^fKHDgxxDfwaCfAh2$NTD90848{*O$v=#DX`^}JKLO5mN zYJVZ*8}z@NC$RS%7wGTt`~TmBRog!^{!7l*{?%7S{>zOfdcXqxJXrygRH2E=G<|wj zVg(StiV|B}5yi#CDno;#Kt|UHLh77G4$)RDt#Az8%4p(r!kxl4yKUZb_9tYZEw>5BJfTVkMhMi+4zBv;Hhef;zd0^j~Q+>Q;V|NT6A>V^>-TJe$AMD!F4qpp9G-^ol_{yzH z9{sUOMn^Bebvy@*^*s4dFm0CTI!Asa5gmp&GHo_iYVApEj&Y*H5JV(1#<0ZL++S>L z1u<9QN5pB7rstxB(SWx$#do;rs7E4gA3N-z#}}rQmZq3Jxid~-Q)=}q`54g=AdVE(ML#t1(aUiXW-K#v+`GX9?>PjwMgmR zx~sSqI|}kAoV`ZIx2U|lshMAx6HIU_na*D^a!pimZ4B6zcp|JZ7&((1cato484v3P zIV%MZ_V(BmGwu+TCa6C)@_G2>zu+iQFAD5^dht>WxoJ*JeSD0pkQrIGcQRCg=dM4= zECxydZ{%+fCncT8xmIJ628En~%WkTr486jev2Q=fY`4#eiU7SSdukcs^NG%&#>w+? zGFM{E7h@Fo{TN-+XyJ+G*eqk=r76|oOtOF2)g>`?eCibr1XOVTWZ&PbVgTt$Ws`~q zmCGD#S#)Kvw^0!F}YAIN{EI2v^NDN}^K_5JU>3@ctEIPYIoC*Yq$;rPD}1Nbh#t-a z*sh{_KmeCmr9YX-qgiKb+mDi!@ad589BR~8!K2=T{O2^}5JD>B#~;ze+V|2Z#FXY; z{_l3qO(>Fom~P@`rd5`#p)pKt zX8JwvC9nq&%_f7U+I7(0vm zw!z+~+kBb)$fMaH8)5FLcG6ypaO30`)P->8Ar?Ng&SfC-N}!C06vT;H``Z~#Cj9dI z0y^BZi@L=g!Obk0G(~l3tTOxf&nl-5WpUVaG}$i&fD$LS#w6))EDB8OS2~_>v1V8H zwlrMDzN+0anyB-U4l+hgDGB<}gK0jowoaNZg{`g-NBf6ELhYhA_{EfE8y5~G#4Lfm z+%*rw2gV;#&tg3+aQlhbNlQD$&ytH)0`AX#p5>J$jEa>uDHy1$mrfQ}c^scu5pk3U z{hsg_fWhor{ze&OHM$ZkE8i3R+Q{IQ7Lx0K|q|2A@wW;5%?kgaC;XCxap+@qnJa1+rBL5V9E=u11OY zLyc3LB#s1MofKR9_zo-O8AZb(_L2iG{p$b~t{!<&>+Ku-e=p?)aFThxf5)&K48UCg zBVbvGI&;c5v)c|_912tl5Zecpf5o9dh2M*xAmL%WCvF1 z<6q zx)5SEeJ2==Zxo(WP|U(&GBdz>CgqqRZuUtTlR0_7#5xTo#MP}XO<&HvZoYj*48CJ# z=LqYJww<2iOdaLfiCzm-*B_bm4Skanm{0yJ-dZB$&%(i1p%+?DS zk%#Fp3)g1p-OQ0Ly?9Z0!n3z8c%5z$GovTa61|F<^ifGdx3BdO7?HPc&*%7jTk4m2 z+5XdlY1J!Yrha}sg^>{N2BQ|b6o8vNnrxw+g=#HX3TxvRHq#|OnmA)hv@vF!OU{>Y z@u8a;7Y%Jfgm=Xa)x)M+K-UcS`3F+2yy70#v1x~pxt}yD%#VkVz`>r{DlypDjvMdDDh4Ze5+Wb7 z@~hr(b9cF|xV*>lqHHJb2jGpbYm@u$^VR9~<@q^6OPl?<4z|(3jA)T7U52dg`)a-8 z%{J=k)=GS(jS1c6R@d3V&Xcf_lpgdS8jNSw4zZ*pRMeNpI%&5OX9^pnu$oZ;6<5El zXfBURiO3S;Ez@`%d7{Tw?Rjk$G>a;n{4y{9URQ$|MV=0_R^(6C8bBZm5ynN@rlibp zNa3i1D}ne((4O>KEOimeUhvlP@**RTe;K4@N?7enp0t~mq0>|}#euTFI}1T<8_O@v z*@E{mYKu* zuIvhJQ7#t_>`k~cIAD>%U(r2{l^uRsuH3r-h9&-b$g=B`mpa`fDGMsslKZ5$< z5d?HlipC|j*^%G8;MniP^4(`~hQ4ON>){di&fS{C0qJ^B&^>GPX5nQUuP>tVeF;O* zTv=bBIOHo=ng=spt(&a(a}S^#sMeAk={*ftP^5#o{`_{N0Z^X!L0mXFK~y@}K&s%j zHRwi8m}+`e46#M{)UHz1;DA*wfN~}OGzB=8=O#O0v`)>|xD6j3e)i~BFdK)$Awh($ z%SySjEUa}@`rj0AUe{n!6^WS5Y!#~@njAMQ6C*0K!i=_Dg@1~9XUz6qN_ z_>VWz*_>iT0T#(>ki!`$`z(+UqmEQ&!VAn;F4qbe(lIzG+-)p*93+OyVA3LW=6{i= zsfj1Kcu<8$lnP_oIgZTl4{O&`nxqVqJF_pDhe;h2>rm^6RnqQ&_?Dml)T1C}$T&Wx z$upf}XO{~a-(#Y`R?i_As)AEb)ihHh6U>VQhfU)R2OI?(@r=>#PZf=}+4yCTw%L9N z#@$H&01wdR!30+EoWBp)sI7L8~ z3XILQn9>ah@byy0oS)#TEnpCE-oxkp8j1l(HckgGfCo9lSSmDrH1f4e618~ukr_KI*nsU zR3dKM+hQ6IeP6!IvSu25HGR5ZyqL3l5Z6x5Ax3Yeh1Q9{0J*DPBF&$39TVgnUp;dR zT7i@J;Y)l2XNoj|4lX_(y4BtRkv+h?_etL55+IQ~z3hf@(GeNaw{a3ZlA8`_#ZkIxB+E`s$ExYKYjQ3>e=N z`!Eg>8YDK)Nw`&z8-o-(E-ri}%V!ARU;}0l4(g2NI)m;P#IbHaMq<6J8L%2ml6IV~NI} zLsSK9OMjG7G5R2y`gB)_BN*f)W+POuXKMAO!R0PKoPXTCRVO)(nM8lVLjjWVSzhFX z;r(Os#J%P19W@{a@g`$8>Su8Ljg3V?l=P$_Ua!j5x<}SYKM-vfDikx z_<`LcWcF9p>-!s4)31W1L@N`12SiS*4}p-rSs|8}h!1kAg_kiXA;|X!B?vOhZc#=N zx03j9^UtCA@eE>XGW#wDbewgWvAz6gb^z7%ti*A7>2oZ{Y*CQzEGfSQ4R*u3&xqIu zmxJU!5pUX;4D)_!0g2u1!v}mB;8Sn)>B;Ts=jBa8R3xFlfA(k9#Ea`>tWdC2^e(~> zKdUVYHVL3C83w3s{bUOKHo-*vA|2M^XlEzoK>V5}om6Lm9Sa7@5K6MZu@as@^+>Y1 zl`XI6uqQ=$W9+fIWq#Fy`pyDav9Ci(?Luid z#0i1ZiNvn&i5H*vt!_hrYl9!IoIIJ6iq;w(!0cQ&>c&sYknh3El$!8uMNsyfB(;o* zt9@+?$(UihOP@0lX3GtA%FD-*#?cX0z~2My=C?MEYTH}GSI2QkZ#4Up&c{^u0eSBi z{t^oHj)j~9@GJI1#rFO$GAoZ>DJzFikJ50(a~b zwe7pwgAEnt|GVWOvE1BU;p1DK(M2Xj`Aa>zE2ttF?_{)BfPS44=VEHBIm6~V&I#gC zwJi?B#)@nOz!lwml%k`y;M z>e-2c&tkM(I#ud5T|si%jY>l`IAcea=zdYF>WtDS|7fPt=JQ&piA2QiO6Sw>! zt5A;QT~|#D)~v&$M>aEAJzSA@@z6_vh-`?+=!r)I zV_`)Mn6OGTkjX^pnwsp)dFDpSScbJBjje)Qt8)K7;Lplfs5JK}J2?F)^M)bleqI28 zPH~K{LWd)$qNuq1g=}OGW^4b)C#oBdaPjnof{`*^5_y9>4uMX2+aI`=(xq(0ytljC zU;(}tdOs@GYJ95eHDuq_Q@3++kExNTw*OMZGKqb$_vBG37o(83dKKIj%mD(o2+|Nl zM#G?*g@^RHJy$=Dda)>56G7OR*WV4G`d%)u2=3JY5>L)O*yxIG~mdH8`XnYqxU1v|tS+9!9;2z)x+@K^;qS8Q7y`8!7{J4KU_ ztF1+IazU3-&h{5W?Q+w<20?8Jb%m?tSsQ~?AMnLHl*m1}f6$>&NB04w@fq-V$k7Kv zQ>|VMY4eDanqLqG&dD!gNk9&etYxDVWu7hH@mHL2%oCtoU%=kp`Ux5vp zWT6fwsuPg3q&lm)4o>T&$o+(2v~hX;xicc8tEzS(Mv@nz!YSXuA=AJueoqMtFH;39 z>opgl^xtQy`XPb{PF;aI?*^!Q?xtWIGOoYROPC3!(BIRKS4`HFpPK+iSh)jjEzYUj z>?Evn0|7iqpx9uM7Bm5R-v23N7eK z+%UVy#y2z`$mE;xB#@AcIBEX*#j`^+u)3hOLUkYBJaK_TkC1O+0 zGnsP&ZG)dq*!mu^Tol%awdy{>nuuOqR2A=L8ozT6K98f5t5pB$=xXp~`;0~-?}$L9 z@xLSQNIop({3^n@JYF%WtNO!Z@1fSC#GIp+QzD0?uoGXZvjf6i`9GO(@AGGlggv3cKwCX9Is3 z?y)bIBMxpR{){YY@FieP1z9H2g>hskTLll*B7Lkcv*?7fen6DwSS3^4jRf;0H0mHO z1PG9LgI|?#hX4*3U*0`Z?(tQE9KcSINP@NyjEEq<_0Hb+9x>477o~Y-$x(X(tNUXf ziKJcVSLf`vq}v`|-Jt7Bet2_kPh*qf!Upfamq;JHam_6D^NQ>i-hi2^KukP4d9YHz z9zKSE{F3N8g?<#H3sNtBB!84G9K$9ZN(gGiu0$QUO97Nf8308oe2~cIn^AT886VN# zXfZ{hJf5(e**{|gkl<7A*f7N%F!_{Je^Y3u+Nf8~bH(MYoZ`miEuZp+0_n7zO|S=@ z(F$46G3l$CtEou4)6$XJ@?}mlnx$vZY+E2fKRzg`L?r~lPLQSNZk?SWD^@RLNzE>}JPnz{OruaxO zJ{IJIM|q8O)#8EI6_V^zef&$Ow_drcU}v+YPts@YbU$azBXX}NhvQ8?XU9~z;EjCk2 zls$7j8DxwdOKulA@=8{pmb{DE;yYM~XdXwwGwfWJ*;mkIHn!C%un8Ebpx|tKe3M(S zA-Vv>h5k|v5AID5e>;BoGtEfZYq8&Gctiz9@;SJr_rLbQyxZPdZ&pC1k-kVkKc0Yn zAXX4ngQUw97%&GLVLY`%?YlC9Nng6tGO%D{^cC+OhC=aWe+A~jv%8)pJW_i(VQ{sc z)JgpQX8(ljF`&rmE275fV70%%Pt`B=xhe?%8w zpTgXFTqSXYU*bPJCshT2@PDGv{*@9QRnu9smb+LyRzGy(5-)sd~1VyX;%RvH84;_8(WVwPUQAJ?w@UBl`E zgTvN?KnwHHaX_}fz#R-#jD#OLz`=d&jS2?!BP6Sl35weYWDLPBt=@QBeDOXl_Yy2o zC1wlV_!IOdn426b+IX;N%P-o6y}gV{DQ`PbW0n?Kx>6*VN*I5ZI8U-?a#>e&wvNf zoe}bk6_~mW>woP2_wgk2B`z>@2OXH`0RtoecKu_G#{Z9I{F0=ZBBk@+-I3jXh2h#w@8 zNDqS)O_FA1Ow9L^>*_t1VYg7>An`ThyaC~V|uJ9_+c1B zQEw`bYsoTTlso{537(-k9}eo$#Ah2Ym(LNq9~J)g^{+w__QiOK`BEq_^uf2B7@rAC!p^Pj)Td# zE_33w%rv2Er74l|bNJ;|1Z`s&U>9y`$jZ*EWhofcN;)=8!B&^oYGyLC+^lm(MK$!R zJ|*>!?T7WE?HSsdj*QjZlizkz#!ZHG_7^NDcPpN$w|r^?;F$r%kloTnVU)0WMEM3R>Z)PAm2CbQHxkHYPlE}+d#EmCCxAWmDC@T;~a z)*{@_e{rE8c_ZkW>_`yTa6x}Uc4A3qJ2%N;4dMdW>oA3Iu^xPJUU?Bd<+7CrCS~-H z%%0|8iDs#$Rk{z1?<~n|bQd~FmcHphAPwsrl~uOI;ayE_%|9ilUYlsVzg28dlA%7q zV|%owZB?`=F3E2~oV=3<{9tGJw5F~lsos}p$J>}M+(+fFDohnYu*W-Ct1m>)`BJ6F z^W$e~s6Dkw#@n39nab4EO|B!Tv(6ZD50WltFW!eb%=MBPnryG!$Aqe9yW;Gw+ehsF zNBi%t-6KT2IS@d+IRu9!wt0aewtWfh5E`+B6I{K8>{Y)_4X?ig#DzOLs~P#u$2{WjAZRrsxQyL^1ztBVb#b~3+Po54J) zcbBH#tWG#248Kg&&PoH+Pe2CilwpYHEsrMh5H7~igWWf+gW}hE#-3#{=#+wg^`a+3 zJdM!B#j;8K+2C6Q6r;-EdT!wdbz%SRJPQkOpb+v^%QHRlsaa=kzMlGv!Fu5j;%xdOhD3?n>|~nxj7s!W z`AdnQ-oamyRzJV#v`M|9#$d$r5(ZPOzYl4$xuTGDGIDZnfTq)F(kF{6=Gl8awSd;N zbW177;{eNq44zVuv(ZyFee4<<03asL#h=;Ob8DG#E(|e)BIza+$2b6+~uI@ zF3$q_5rCKm(JxPB?q49B!z)LHoHf*mW8siKXHj^+i@&hqFK0~`8QsuDSXlY3w8amd z-pROjMHw0O9KM5VTO1}2Ne>*e7@V}`nROGZ42GZ30F?6*m*b|EDoX6cO?48NWQ1MR zVq;lU;GdoimeGg|1+TLtfSfS&HEE|nmk$nqlDttAsH9=spZzSVI3iFdr^~Vno9y8? ztntLGLHR6EhEb-d>A{yHsFWRqJi)~xL^oLXI77%hlAIV6+ZsFqijUKgA2IN77?1)0 z(;*=o-BLtvum$p4Gk<3D&q|gH5&sUw&YEx|$9@2s|G-9q+Q2$U zm`|h!qWEUaZGA+AlJF<-|2_t4G%H{9`N!eK{>Rh*Z>AmK8XBlyjtTh33R1=y&2M(6 zrzhO|7-deOH6P5YjY(^=!9nygz4a3Pr4~eNyI-uxoFQdjvH}+_nt*# zf8`nBR9@dcZ@KtgbN~*crQD2TemMLtX)?kQ)XP*qnqh{#F2Ivt zciL_a?r2m}2Xm%J6vE(Y%26*bY6w~BHo{(r_3JUBYQvVUDHQW(2oN*cSXkGQs^Vmnw zA*l{H4R*rhFiP36mB=kR;@k?+^6}e=lmlz-9uUJ!mjKFVKLFh<5cgc6E(=j&p4zWz+^E^*x@u_cZlh_TwxG=SNB$;y#u}oVVuSWa8CSfYNLnlOj|bWyG*2 zi?^t}g%7bdz!}x^J#x_!efP)4UHr=r)$*tZS$_AH^=%W6ES{Z{cKLS7p|+dR3Oq8OOzYymd>%e}J5s&+-U#r;CG7QclvNL9kNdZpE-cKg!C z_Syec_m@rYcgM}NBvk0vdBWE7j{c0m(@(r*R6 zdVn)Z-+f8?Pd`nf&Q;k(hG{dFo$#rLO#b7-fXv> z%TkjavXq+E=*G*_?q ziJX96CQ8TFkd{~F%y}PMzQN)1$s+R$_`=Vl{p!+pJY++9SE1AWi~Cof9Y$hqY1iV2 zXRk9uKi~|vZ>7S^3pYU8$04b$qT7FMvhCj90PaCbykgTlA;ISbDOZRp*SF`<8PKc7 zcXNL`cJvC%ygU8u^t8TCzvaAQ7Y(=oB(aMd}4MGLKGl< z65$>&Je1($)fY>2y8!a9#?I{J%?4088T-R~St>#_|5}jl5%q@r`$2BOZuzw#y>BW( z?jbat>4X%S`0f^H5D?v$Jf`%Z1qjQp_kO|k7NpPSFMn9=Y0<-I$j+ZVvH1$;*j;+n zGw8&9I>3IB@b(q9=qq_>a}QXTBTj;ByYn<#rYGz7?DUCIyU-ryqwL&RqxnkB=$M_3m`AShz;cd zVIvykWT+rThK2CF%o=AK3De_b;!46{zD0c{7ur+BqFmH}JKgm|&go*A~mq|Louo3Ueh;QN6GwN%()Wu{CZyw4Lx zJQ>;}ZUO7gfB1SSZl<3;rsZeCr4xR06jI`FZ9+kJPOXiCK#lMxT?<>el3=Acb}eR} zA`jLqx?bCXf^PYCnOcw-b3q-#mSOTzu9*lJU+oy}8ZjioNDjI`FapFO$T^G9StUG= zc@{s=sUV|#8vGg7IB9mXO-Re`uwEN7E7q5GVrj!jQ5#uJ^Jav^Jtyb4<>hwNvR%h& z29Y{h=5b<6xs)a?DyG|@itiC~L6I%g(?t5hdMt{%gt9}X&DSxS!gy^iLphNkV&Stb zl)71uNV+irWjH$X!+@v4EVb!zh;x5^x6!(YY6byGX08o4t`F&IVzW9fDLfbO`A{%#6_Tt0mtjDBiB|XmF2MW`#q7I2zm>uUk5; z!OXaY)KW(#t>yrwX^15I4k5f`EzbU>iqWmE+lnOA6o5mm6%!+=oyOmKU7KqMsF1)XJ)QM7Zz&GuOOmXKK0|epG9=IQ%iY}|^V+3KPc*3T55&J3FZ~VbzMd7H=ct7>` z*N3lL5q`!2oiE7m@92`^ERsSc&f*b%M*c)K=9Ff?XMNHMNw8q$xpzJRavr3?c^Zcr zKD~Rc6hEIS6JMt#i7FxMH#=f zu4!g;xEy$IJj^50@6`jwfEzypVS1QpSSHk~Uz+hsAxH{uux{!Um-L8Yp5GPbT^>Kh zeLTOSD{ybFNzAXmIR1!#Dc3gdY?GM)X4Z$WYV#}PL9aKl6iLigN=31P!m>{|xJtu@ z&~WTJ_Mr1+B1fk<-H9aoVg|z4+=x_VZK~rC%#kI>EXbQu=em`EU$|K@%XWz~Uxo~g zIPjzDM3OaME`=14I#OFn(Md8+p`caEG4eD;>POZnb2N=C;YwcHYMO~SX67rW{bieg;PXbSSrwQjy^LpT)fB28GN$n|nr7eo%U_HYBdEgg%iIU9l>?&_i=9 z9&hYEmT)ItLUk+w=8wiU$|Y|;wG%p{KCW1QZS>nT@84`uYEmUd*_O99N%GOJAQr!BRGBUHy?&yM`+ zq+uN}x>T7%(6goOaeRrrL8NzcfK;4Zmp}}*`oaVou!*zT$H>6Z22H;(pWD`+t!tKn z4(*i5u{xjI{#XrRzp5S;T$BOi=vG-w%#9Z%w=J@sYJ zE*GsqKub?e(s5*Zbt>0z@9bdvHyoo?YA-%f@Q*nG@YrSt46P~!#Pb?lQ@D96HHB2p z7yAg3$3-9ZKL0dUu6*SFl zKXNmMqf>mj(0j%gM{P&c8?`}mOg5kZc8wBN0G`D7CP!NFJFBdGY@_a?#gZT5YOP}^ z9jhQ&fU+nrif_@ob}WhG?RiDu-|iVgNDM<+JD3&ce2nBf>bst5>hI!{>hDJTfvkPq zUcJ;^=N%pPCpUd`#bRf{8?U^D72Fq;x2dEa#U5M-{-3%c)B25zCMERkuht|<%DWlK zkb!74O3<9?FG>vkHT3rUr8(e+GYCEGvf5ufpP^RDwkT`Ty*1c>vd9*jxf*8>T*Fcc znq9GymN`(t9vDK>6!Fp#!8#kle-V$CXtCD7KZuE~-BJyXe^?hLJVi$86cDHq+div@ z&1!V$UfHvf;u!H)sQw-~y!4IQP+rf?$q%?)v`UL2^Qzx?c8n+vQBIi9nJ9uLY^Ors z%It=Ckg#D{1#ZvdaQlRVe*z5F)T9k?csCTBCMJ(w28?N=pWRoGsiaDTlaQcTit=fu z3c?93bAGQi$Q<;M4W;aBhZqyuRiV>3$NOUD30ExZ!SqF1mZ;?}!eWD`GN^>Poq1pt z(-%zS&?!fw!QBjVQbFa^-8Rb4pcjMO^W0`H>)e`(jX%--eUgnwR_RfRX__Mcp!*7U2MVW5Qg%tB|!RCYQoOY{AzvxCIRyBOd^u zjc{>TTJhiYO3yofV2zDoUx?(+KpE+17Q&w}6vT>C&QzwJla~_sen8fdmVVZsCUdv_ z2m-8I-9O{{3*7twFeGKMb`gy1rum4-VU+Lpe?tNfra#i1%KAc?oYNBUUuGMKNK9Py z?;J6?63n~Sg!tuC3c89nX zg{-DUD+TfSMGYk55n*zI~!o|&m#tT_zvFdoR-gqT}-?M4j68XbBv-l9%XQ%fNa`q^gG!pJ3 zPNjt>8u(9`W$GTgGP!l89hwHr7+)o~-6fr^$f|teP>r-^)zVkzmjI1b%GfBhGqHt@ z=xn;hPR(7RxU2LZ`2**4kLjmZ@6_`rZ7Z;p64N@^mlOkz z3N?;OQ96)(2=h4UB$ROnI~41#%FOTf))korGT9_7)A=@C1c5Ne8tBx z#6%<~lMopv^7apoy@FqBEOl$!CVt5(cJ#&}6`afZ+J`*hRJ0H6N^)j*N(s zcAnNM{d_ebAHLZs*Eb!1{^&|~XZCKvr;2wnQJ=OhCyna>8?#bCPI0hgjH$1`Nyu%; z_5kl&m0avqfE<1xpfKST3TR{&-pv@sw$NtRHYg9Dtrghylfi52i#z^YC%C5nXjK&3d^AF(%2;Xr^%<5JlUGJJY_JhRsc)RI zdg4YTE)=+KRe22#g=n6n;QW1*B*2s3w(zU2PMF_ITg9<#ZS#@y*glns&t`{s76G{d zrs0VM;!404VI6+`QxSnuW=$7HW)prRhZYp&;vWGH+^}%gMF^E~Bx*2iALVkYCo+1e zr)8(#5%`=#If;hVwWaY)=QM?ifKj51?+4w!Nrv9KX)5sZ?&9q;m`o)zT*=wbm!FC1 zCJi`{h8Y)=FV;V1QBVar8SzW>XH=(zmx0Txk1&DxgzJi97c;$iI@xGdGQ>QkrNwwf zJdTUJ4=2#gsserM!MDb6A0Ib9-^~(Pg5OwX6|ps9aRgNLQF+NBH>yMiK1BpRLe_ct zo&i3wT9cEGmqVc2xV8&>fgfAU)QhZ#9?OBXU-ilrg{mn}ZSh)4tL42jcfuN5EJs^k zGuZ+8RYoD1HFSlGFQXS;Qs@k4GBjB`C$2K#OsMDEk%M#LOGFTj$wOp?;9^9TmJ`@v zhcgaW=2MbdqXVpnYsb)S-4Z+lswFaq4_}6qItJRw>|72PH|7YOy@rlIC@Dw{o?CKF zUlK@+UT76~jfX##Cfqy$SQ6f;#05F(a&H6Arh!`b*>Qk=_lfT5b(r?k$Kf)Mk?2Ve zI7-O8T2z^Ihf7LZ1-LEO4#l>qVxajxD!6GHMJy}(PNRELug)}`$N7@K*nT3_-muI0 z8}(wDyJ&hAl#IQ0nQl2aHIt{1?Im3w8oZL&5sTCn7^V~q%?o}3pDYd^ohY0JivVo) z0!LcIUVOmc$_Dr0m=jM57Mq$>Dvdyd4alpg$LRnuteJptV&&9JnSb}R5KA@5saT}nx3Vm;@N~38*yBwJ_<*DUfH18qv@}11b_iWRZ zeyZ6{(R`}|1#UU?NR{<0YS=+oabck@N|#XC0il$HYK zUICC!RKM?*Gbnz5%kM#RF_G!>hmUw-dD=&e=o5uAHz_{7P*C5;j_$|R@O1=ECb#|+ z%ONUrC~e&=M6b$`KBqHL@vV&cu_AK`=h~$l;B~B3oMFxsK%h{!a%1K2YO^5!NY9ll z@Y*9^ABV)_JjMC4rGU(6M03L2>%G zNGlDb)HX%KyIF_a#(@t=-X`QF8^#Jd|J3_ne+p>BB;P;W3B0c%U16!%O=z&{?0D@4= zG6O)6ZQkq;zED9@X{_EnARP)O)C+=lF=|S4+)9`FX_MSaksq3d7_t!rcYQx0ooY4D znk~VfqU(K`?VHe_v?mGVqNa%bESW~44D2&6PaMjoTW&g zRbXod`_Q!UqcAzA(Fbw*6bM=}O*;il1j*y|AIu%#}Rhix5!g)lgHwCJcxtwm; z({{=9$L<||+2l6qFfP@E-aI^5wTvtsM-)asygS~c*Ep8bJ#4TU(IRRbSM8_qb{cNhxDHW5 zVHA&ZSVvx~xbl;A3_+VbW)%Gl*Km=M)~Xqh(Kao}L}^2)X$joBmLN34V(z`;Fiv!B z{GQGaJ2Hm6pI(`i2!rJ^lcH76KHa;}y*5dglViLkl)N&A(3m)>LI z<&s;v=R3-*6$&8tc+~~pE3rTt z|9Qb>igp}!NPg#S>@%{dgY3p)7K@bj;kd!@xIuYB>$JNFxyw(O%&}$886R7PEsel$ z=aAAEbzDwAx0rzzG3sQb3l6jS6}Oyk)=Jsiw?x1_u3308cwOdFdh53QXzN!p>UhI<8O2Y+ z*PDXdn`!(?{kZ2-JKp|6*H6UX>za*dJ(a%_NH)%XC7a-ah|ga&qCobYKvGL``ehLz zx7|?;_3QiIFA`V zvS0%i31&B6qc=a0^8gjchAAkCO`d@>fX3t?hSb_`6Y)Zl|BOa_)WyZG(YJl|=NW5j z+S9|2ZCWU$UK!LaHpYHKIAiI2(mgBPh+azOYs=JOrm;GSfE1;m&-NVl9Jp4x)_KWj zpMcG=!I}2DG{G%f^|$Jp%B$M%T2N+Dw_-2h$EN}A_x#%9Rb|vM8F(V+v3q3fay(8c zaZQmUFm7j@d`A$E|u%P&-8Jvo0a*A33mhk<45VSCsQrE78#>Uv}MMQtR7e0%M&D^;VF7<^04vv%XyWWWFV0d z!EO!Mp_EEN=kgMGv$BrveXI{S=%aXs-nz$# z6vNRT(8l@jejgO13g0bwao9^0y^n?H4?~zkycFOk@XE-;iAtg?2|9G|*;%=pm9h4*auuZ2}-w?Y4(I**t2M=Omw0zQmvMqw}&Nf(J8(EmI zxCNf@V5Zx8-p$y00rT1$U~t>_+Ita8m`kQNyy~7#P3YBWa_r9UkhuGeLz8pywDRzB zNVz8{=R7bY5l4{3UD+6561@`C=VC$wr!LAa(=ny?#I;@y!?H(w4i=Ib2Vo{yLUFLb z*=~!2QN*>_<%lsjVly)^6SSa!S87Puo8o&R6!8NTSY(!1+}|-1&Y&zMkg#{fUqjGA z3Ux9|H10vn1b3(_I2w2gUQsuSEQthq5&*_bv<_3AM?oGKa{&B!Tgvpy+-t_r_k}0aJ944RIF$ zKQT@?#`yA>BtpzP*vaZBbhoP6#RobPD^CjGmuG@VP2Cq4KTkw0aVdi`OKk0ZN=pt? z$Zq8f9H_pkSfbM|v|#)LiJv!DBadIWy*UB6osdDscYfVRPp zlIl_z$bs(Cmw+~=+G@sr0`k#__Zofn&WTt+yg(bfP17V-XfQd&?z>UsFhsc?$$B1ROq7LA;=kJ4Ag! zzBLKJvZi+EE6w;(7ozSLw&3@=kLo@<#EoyV-ZZ8~@Rh@SRs#U6Db^blBYPJe=BZ(ztwl*V)%rFZ2H z#3Tpj$d-0Y*U(kwdY}=@W)g{+yUv){b;k|t^cp&}&NtZ(#DaQA`k9|p+AH#!M&_2* zO6xl@rkUyaFT`$<6}L@IvYt%?O9)qmcofB`!o@@-B_;4$OIX%Q%+qr@yP9*wjrxMk zaG^$a_I5rvvqkD$MYQ3~SkNA)9qcn)=5&`KOI%KL^!f`;3qUdh=A4-P^h|J(^lpEL zxaV!9!INu*WB6P?Dl-+ENhKNWBz}v%d^0B}PZ<%rRf0cS%dw<_ zdAzxelAu!Da=l_Zl_*K?S(lh%K?#R@o}@hgaa4<){P+rusWv!mt-NxXN-WO`ucmR% z(?(92A8?UT2qdBo`2Hlp#I8l%Evc9lwr2kdj;W^lTA;8P*(mZ|=Ph?0wR{CS_Nc;qYg0t6I)|**Y(pv9n{-Cg>6nosJD!_cso@n$jnPFt=$q!B zlkAw3*2iMh1^Cx_j{fB3CLa@kX7j`jCTw%4bv8@eu-O0 zbxHD{-J29JisXz!CR_##zM3qkN<6G{v64Rs=O7Ut#R86?U7(+XPa+~}XKlIXlAY2? z^#zpe8i9IA1wKQB8mHJs**bKwsH~=ooN2g&XEkQf&E5|@?Mp9-tvmB#2)2kVmnskR zZk3ZQ=PVW8UL!ujSSM;Io-B`iR6V4H(wOV*P8 z%noWfrHNttQ^XiwOqS(S$oQU(JL6tiOm4%_q^|BJzKkqh$4)^8J3zFR-6`er#cNQ! zO*x%oq@BS%wIVQusxAmP{(ikKFIix!&50yJUE~IJceC}LlxmG>`|cjrD8)Udsm;9N z42U4(s5wM_NyxZHxvhH7c>^0iuk<)mVU6v=c(_ODw#1y3r&SWrIGZ=2k=M*JMUX9Xu*iVD$h(PtbfQAp5X zhDzvNAO}D*=lBdWq))ZIi3>w73qJ*cfGXvZOLZkflKL~V-!nMp`9MJ{f-h-V>(wR6 zTUmScR#|jf@ORUi0xN}ChqIlegxl* zB~@dewMOOBD0l8P@-CfNyeX`VtJ<1Rnrj0on3rT;#o^*Dh=1uuc%m^H&kW?A1d{ka z3wN;P02fog>>brT?t1HREWB`;1+NGhvE;XR@Vt`;JGE%ZCwCXg6=?s^ALXGfUJYTc z|416CoSaz1S?XEsU%Fh(B&@Sl8Tfp@r;Yw{rF@Owul zPMJ+JsK$&XKf>BJW&Da#3qa-YDnTQLi<{>fJB7fk`)>98eIXQfMbgebTECUN>TrOe zbk8&X)cG2GnFO&X_KjfyN?qK65lctZGbM`;{WNde0p@h4(KpwEWutEl1wcVV_w)l5 zg4GC$^%5?Ot5CaG_rV!mg&tks`f#p0>@QK2nb6v^m=7iDQv~SvDj#{#fa|zXB@`;U zZzoG@t98Rg9!75|zZpyoeoccX39=a>>H{II1XIt_oxM=#=G2|iA&diu356y#IxuD1 z7fv;(D^S&CvJt5VbF&zrsQ?LC>)b<$hz^7;i1E$};6=6!QC0Jk0|~G%3N-0n@McS} z83IvuZ+6CAgbdf;5=Hl8i5 zB0>9`?B*|;y1uusLW%pR_~PHtn($7wz$~a;FxF$**MAkTIhMDldTolsW6DWd$V}M# zI=v15ohk9zphtS|O~|Z*9CKUXsaG~St1hRAF8sWT zR>1~641JL>fjULsqy>=E<@@4Zc7$<(ml3x#0JSD-;Nd!<@1uw2*SZBFp1t#*EKAo& zqa)XgKWaUNTyA?vuzRqOGnH4Anbv+5&wibgUG8Wv1UGbsKDUAc=SH^e7<~sV^C|65 z6$E(lY@+B}G#KJ3@Z{iQzi`bbtUCx>dNBaOj? zrOnvw{t?)FV>H0LjGb@a++n^dbipt}-@K2npc4&)O&wG)ptWZYOw%QeOLTHg@Wj^0 zkDc2HE$ZZPVZoj>R%nEIas39-jn}p@K)$VekE^EiNH{k2@j7spWh05{tvr5PNTA6c z)cO{3*D8$DI3igt;>8&0h5_luW5m|#CJk=Ud`m!M?dUroy0mt8WSH9puM(4R+!otS zHX2T!Lp^ihfp{CXmD|gFgpo}JS5M^4`3;^Va*xxet18y9#%`u}4WAm9hZ)zt+jaFV z;jz3x4ppU~+bZixZ*rPE1M=dL)mGF$tf2SI?mzhL0Kb2xPE1P^G_o$BIaZ9_jtPyH7uWnTwQvQ;lMqDHL+u~oN`rC3{V}w}XH62Kq_yPdJ zSm*s+0|1$>3;*rYxK0bPme-9TrND*_q%7R{Wh>$VK*XEByejblAlFR+h;>N<01a&l zL97*00ElEu6=DUx1b{-fWFgl0mMx^@-FCzIZ_eUB=URY1Z2vlh1Am7KW|0H5Nk;`T z-ucymKnSqF4f@DO51QEd)q`UQu)+rc_#tCi=-=H{+oggz5d=*O3V<+nex0q+0s#(% zf0N+$AR67EdNBZqZ4U)zTkJPzwfE~u`H}!o76hD?{N3KNB-Y>btvxCjI+@=fn+)0C zAe%e@6>=2E@2e=tTpm(QiT0P)=6~WS+^2$pHw2L!P=b#3Q6VjCSO7qn2PiPumJpB& z0;nHQ!SGmvh^>YG3BDZyq1b}XZ3O>;{!$EwqyDQgOsG@wRlC2=C9!rjvvjaxg-8ZF zu>PmjU)~pXaDNB)%Nxxe1eucqahz5%Y~ln75g8r^dI6MQm20quiPL+)_>kz z_YoD0j5h?rgKVL}Wct4WkpElj?~muu`}|G%CH+VJKg2&Yz5XCJ2mC`s^CSEp;(rx{ zprBs+m`U@Z=QqamN+P}VoKQiC{aOW$7 zxU+&p&i?(`{E>tFhx?BTh&##u`D4&}0HCTf6d3%$-<=pg`}MPKoKeB_efkXqo}+y8X-J{{iL=@t6Pr diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 66c01cfeba..4e86b92707 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,6 +2,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-bin.zip networkTimeout=10000 -validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index fcb6fca147..7bba831f6e 100755 --- a/gradlew +++ b/gradlew @@ -85,6 +85,9 @@ done APP_BASE_NAME=${0##*/} APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -121,6 +124,7 @@ if [ -n "$JAVA_HOME" ] ; then JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java + echo "Using JAVACMD: $JAVACMD" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -130,13 +134,10 @@ location of your Java installation." fi else JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." - fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,10 +198,6 @@ if "$cygwin" || "$msys" ; then done fi - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in diff --git a/src/main/java/martin/Martin.java b/src/main/java/martin/Martin.java index f725811d46..06c9753d3b 100644 --- a/src/main/java/martin/Martin.java +++ b/src/main/java/martin/Martin.java @@ -19,6 +19,9 @@ public Martin(String filePath) { } catch (Exception e) { tasks = new TaskList(); } + + assert tasks != null : "Tasks should be initialized."; + assert parser != null : "Parser should be initialized."; } /** @@ -29,9 +32,12 @@ public Martin(String filePath) { * @throws IOException */ public String getResponse(String input) throws IOException { + assert input != null : "Input to getResponse should not be null."; + try { Command cmd = parser.parse(input); - return cmd.execute(); // Assuming execute() returns a String + assert cmd != null : "Parsed command should not be null."; + return cmd.execute(); } catch (Exception e) { return e.getMessage(); } finally { diff --git a/src/main/java/martin/gui/MainWindow.java b/src/main/java/martin/gui/MainWindow.java index ddda91f869..1bb9644e15 100644 --- a/src/main/java/martin/gui/MainWindow.java +++ b/src/main/java/martin/gui/MainWindow.java @@ -39,20 +39,25 @@ public void start(Stage stage) { //Step 1. Setting up required components //The container for the content of the chat to scroll. - scrollPane = new ScrollPane(); - dialogContainer = new VBox(); - scrollPane.setContent(dialogContainer); + scrollPane = new ScrollPane(); + dialogContainer = new VBox(); + scrollPane.setContent(dialogContainer); - userInput = new TextField(); - sendButton = new Button("Send"); + userInput = new TextField(); + sendButton = new Button("Send"); - AnchorPane mainLayout = new AnchorPane(); - mainLayout.getChildren().addAll(scrollPane, userInput, sendButton); + assert scrollPane != null : "ScrollPane should be initialized."; + assert dialogContainer != null : "DialogContainer should be initialized."; + assert userInput != null : "UserInput TextField should be initialized."; + assert sendButton != null : "SendButton should be initialized."; - scene = new Scene(mainLayout); + AnchorPane mainLayout = new AnchorPane(); + mainLayout.getChildren().addAll(scrollPane, userInput, sendButton); - stage.setScene(scene); - stage.show(); + scene = new Scene(mainLayout); + + stage.setScene(scene); + stage.show(); //Step 2. Formatting the window to look as expected stage.setTitle("Duke"); @@ -113,7 +118,15 @@ public void start(Stage stage) { */ private void handleUserInput() throws IOException { String input = userInput.getText(); + assert input != null : "User input should not be null."; + + if (input.trim().isEmpty()) { + showErrorDialog("Input cannot be empty."); + return; + } + String response = getResponse(input); + assert response != null : "Response from Martin should not be null."; dialogContainer.getChildren().addAll( DialogBox.getUserDialog(input, hikaruImage), From 244570075ab49e5d0e1cff0b4072f285d9de4df4 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Fri, 15 Sep 2023 14:42:27 +0800 Subject: [PATCH 21/35] Implement A-CodeQuality Refactored code in the DateCommand class to minimize arrowhead style code. Also added javadoc headers for all the command messages briefly explaining what theydo, and cleaned up the headers for their respective execute() commands. --- .../java/martin/commands/DateCommand.java | 77 +++++++++++++------ .../java/martin/commands/DeadlineCommand.java | 12 ++- .../java/martin/commands/DeleteCommand.java | 12 ++- .../java/martin/commands/EventCommand.java | 12 ++- .../java/martin/commands/FindCommand.java | 10 ++- .../java/martin/commands/ListCommand.java | 14 +++- .../java/martin/commands/MarkCommand.java | 13 +++- .../java/martin/commands/TodoCommand.java | 12 ++- .../java/martin/commands/UnmarkCommand.java | 13 +++- src/main/java/martin/gui/MainWindow.java | 1 + 10 files changed, 127 insertions(+), 49 deletions(-) diff --git a/src/main/java/martin/commands/DateCommand.java b/src/main/java/martin/commands/DateCommand.java index 5feecda028..a5712a1f62 100644 --- a/src/main/java/martin/commands/DateCommand.java +++ b/src/main/java/martin/commands/DateCommand.java @@ -9,50 +9,77 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.ArrayList; +import java.util.stream.Collectors; +/** + * Represents a command that shows Tasks on the specified date. + */ public class DateCommand implements Command { - private String dateStr; + private String command; private ArrayList tasks; - public DateCommand(String dateStr, ArrayList tasks) { - this.dateStr = dateStr; + public DateCommand(String command, ArrayList tasks) { + this.command = command; this.tasks = tasks; } - + /** - * Returns a String of the tasks that are scheduled on a specific date. - * @return String A formatted string containing the tasks on the specified date. - */ + * Executes the command, returning a formatted string of tasks for the given date. + * + * @return A string representation of tasks for the given date. + * @throws MartinException If there's an error executing the command. + */ @Override public String execute() throws MartinException { + LocalDate date = parseDate(command); + return formatTasksForDate(date); + } + + /** + * Parses the date from the given command. + * + * @param command The input command string. + * @return LocalDate The parsed date. + * @throws InvalidDateFormatException When the date format is incorrect. + */ + private LocalDate parseDate(String command) throws InvalidDateFormatException { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yyyy"); - LocalDate date; - StringBuilder response = new StringBuilder(); - + String[] parts = command.split(" ", 2); // Splits into two parts + + if (parts.length < 2) { + throw new InvalidDateFormatException("Invalid date format. The command is missing a date."); + } + + String dateStr = parts[1].trim(); + try { - date = LocalDate.parse(dateStr, formatter); + return LocalDate.parse(dateStr, formatter); } catch (DateTimeParseException e) { throw new InvalidDateFormatException("Invalid date format. Please use the format 'd/M/yyyy'."); } + } + /** + * Formats the tasks for the given date. + * + * @param date The date for which tasks should be formatted. + * @return A string representation of tasks for the given date. + */ + private String formatTasksForDate(LocalDate date) { + StringBuilder response = new StringBuilder(); response.append("Tasks on ").append(date.format(DateTimeFormatter.ofPattern("M d yyyy"))).append(":\n"); - int count = 0; - boolean hasTasks = false; - - for (Task task : tasks) { - if (task instanceof Deadline) { - Deadline d = (Deadline) task; - if (d.getBy().toLocalDate().equals(date)) { - response.append((count + 1)).append(". ").append(task).append("\n"); - hasTasks = true; - } - } - count++; - } - if (!hasTasks) { + // Get tasks for the date + String tasksOnDate = tasks.stream() + .filter(task -> task instanceof Deadline && ((Deadline) task).getBy().toLocalDate().equals(date)) + .map(task -> task.toString()) + .collect(Collectors.joining("\n")); + + if (tasksOnDate.isEmpty()) { response.append("No tasks on this date.\n"); + } else { + response.append(tasksOnDate); } return response.toString(); diff --git a/src/main/java/martin/commands/DeadlineCommand.java b/src/main/java/martin/commands/DeadlineCommand.java index ec50a096b4..2121957165 100644 --- a/src/main/java/martin/commands/DeadlineCommand.java +++ b/src/main/java/martin/commands/DeadlineCommand.java @@ -7,6 +7,9 @@ import java.util.ArrayList; +/** + * Represents a command that handles the addition of Deadline tasks. + */ public class DeadlineCommand implements Command { private String command; @@ -18,9 +21,12 @@ public DeadlineCommand(String command, ArrayList tasks) { } /** - * Adds a new Deadline task to the task list and returns a confirmation message. - * @return String A confirmation message of the added Deadline task. - **/ + * Adds a new Deadline task to the task list. + * + * @return A confirmation message indicating that the Deadline task has been added. + * @throws EmptyTaskDescriptionException If the description of the deadline or its date is missing. + * @throws MartinException If there's an error executing the command. + */ @Override public String execute() throws MartinException { if (command.length() <= 8) { diff --git a/src/main/java/martin/commands/DeleteCommand.java b/src/main/java/martin/commands/DeleteCommand.java index 8c2d7ac083..398bcdd99e 100644 --- a/src/main/java/martin/commands/DeleteCommand.java +++ b/src/main/java/martin/commands/DeleteCommand.java @@ -6,6 +6,9 @@ import java.util.ArrayList; +/** + * Represents a command that handles the deletion of tasks from the task list. + */ public class DeleteCommand implements Command { private String command; @@ -17,9 +20,12 @@ public DeleteCommand(String command, ArrayList tasks) { } /** - * Deletes the task at the specified index provided by the command. - * @return String A confirmation message of the deleted task. - */ + * Deletes the task at the specified index provided by the command. + * + * @return A confirmation message of the deleted task. + * @throws InvalidTaskNumberException If the specified task number is invalid or out of range. + * @throws MartinException If there's any other error executing the command. + */ @Override public String execute() throws MartinException { try { diff --git a/src/main/java/martin/commands/EventCommand.java b/src/main/java/martin/commands/EventCommand.java index 433899df6c..90a4ada09d 100644 --- a/src/main/java/martin/commands/EventCommand.java +++ b/src/main/java/martin/commands/EventCommand.java @@ -7,6 +7,9 @@ import java.util.ArrayList; +/** + * Represents a command that handles the addition of events to the task list. + */ public class EventCommand implements Command { private String command; @@ -18,9 +21,12 @@ public EventCommand(String command, ArrayList tasks) { } /** - * Adds a new Event task to the task list. - * @return String A confirmation message of the added event. - */ + * Adds a new Event task to the task list. + * + * @return A confirmation message indicating that the event has been added. + * @throws EmptyTaskDescriptionException If the event description or its time is empty. + * @throws MartinException If there's any other error executing the command. + */ @Override public String execute() throws MartinException { if (command.length() <= 5) { diff --git a/src/main/java/martin/commands/FindCommand.java b/src/main/java/martin/commands/FindCommand.java index b0ec7039a9..ae1dcee56b 100644 --- a/src/main/java/martin/commands/FindCommand.java +++ b/src/main/java/martin/commands/FindCommand.java @@ -4,6 +4,9 @@ import java.util.ArrayList; +/** + * Represents a command that searches and lists tasks that contain a given keyword. + */ public class FindCommand implements Command { private String command; @@ -15,9 +18,10 @@ public FindCommand(String command, ArrayList tasks) { } /** - * Finds tasks that match the given keyword. - * @return String The list of matching tasks. - */ + * Finds tasks that match the given keyword. + * + * @return A formatted string listing all the tasks that match the keyword. + */ @Override public String execute() { ArrayList matchingTasks = new ArrayList<>(); diff --git a/src/main/java/martin/commands/ListCommand.java b/src/main/java/martin/commands/ListCommand.java index a0585095eb..85fde95cba 100644 --- a/src/main/java/martin/commands/ListCommand.java +++ b/src/main/java/martin/commands/ListCommand.java @@ -4,6 +4,9 @@ import java.util.ArrayList; +/** + * Represents a command that lists all the tasks from the task list. + */ public class ListCommand implements Command { private ArrayList tasks; @@ -13,11 +16,16 @@ public ListCommand(ArrayList tasks) { } /** - * Lists all tasks currently in the list. - * @return String The list of all tasks. - */ + * Lists all tasks currently in the list. + * + * @return A string representation of the list of all tasks. + */ @Override public String execute() { + if (tasks.isEmpty()) { + return "The task list is currently empty!"; + } + StringBuilder result = new StringBuilder(); for (int i = 0; i < tasks.size(); i++) { diff --git a/src/main/java/martin/commands/MarkCommand.java b/src/main/java/martin/commands/MarkCommand.java index 37570940fa..b07ba51cae 100644 --- a/src/main/java/martin/commands/MarkCommand.java +++ b/src/main/java/martin/commands/MarkCommand.java @@ -7,6 +7,9 @@ import java.util.ArrayList; +/** + * Represents a command that to mark a task at a specified index. + */ public class MarkCommand implements Command { private String command; @@ -18,9 +21,13 @@ public MarkCommand(String command, ArrayList tasks) { } /** - * Marks the task at the specified index as done. - * @return String A message indicating the task has been marked as done or an error message. - */ + * Marks the task at the specified index as done. + * + * @return String A message indicating the task has been marked as done or an error message. + * @throws InvalidTaskNumberException If an invalid task number is given. + * @throws TaskAlreadyDoneException If the task to be marked is already marked as done. + * @throws MartinException If there's any other error executing the command. + */ @Override public String execute() throws MartinException { try { diff --git a/src/main/java/martin/commands/TodoCommand.java b/src/main/java/martin/commands/TodoCommand.java index 7c75c38d6d..a623399b5d 100644 --- a/src/main/java/martin/commands/TodoCommand.java +++ b/src/main/java/martin/commands/TodoCommand.java @@ -7,6 +7,9 @@ import java.util.ArrayList; +/** + * Adds a new Todo task to the task list. + */ public class TodoCommand implements Command { private String command; @@ -18,9 +21,12 @@ public TodoCommand(String command, ArrayList tasks) { } /** - * Adds a new ToDo task to the task list. - * @return String A message indicating the task has been added or an error message. - */ + * Adds a new ToDo task to the task list. + * + * @return A string indicating the task has been added or an error message. + * @throws EmptyTaskDescription If no task description is given in the input. + * @throws MartinException If there's any other error executing the command. + */ @Override public String execute() throws MartinException { if (command.length() <= 4) { diff --git a/src/main/java/martin/commands/UnmarkCommand.java b/src/main/java/martin/commands/UnmarkCommand.java index 5844d25bce..de9be9a384 100644 --- a/src/main/java/martin/commands/UnmarkCommand.java +++ b/src/main/java/martin/commands/UnmarkCommand.java @@ -7,6 +7,9 @@ import java.util.ArrayList; +/** + * Represents a command that to unmark a task at a specified index. + */ public class UnmarkCommand implements Command { private String command; @@ -18,9 +21,13 @@ public UnmarkCommand(String command, ArrayList tasks) { } /** - * Unmarks the task at the specified index, marking it as not done. - * @return String A message indicating the task has been unmarked or an error message. - */ + * Unmarks the task at the specified index, implying that it is not done. + * + * @return A string indicating the task has been marked as done or an error message. + * @throws InvalidTaskNumberException If an invalid task number is given. + * @throws TaskNotDoneException If the task to be unmarked is already marked as not done. + * @throws MartinException If there's any other error executing the command. + */ @Override public String execute() throws MartinException { try { diff --git a/src/main/java/martin/gui/MainWindow.java b/src/main/java/martin/gui/MainWindow.java index 1bb9644e15..7dee580ac9 100644 --- a/src/main/java/martin/gui/MainWindow.java +++ b/src/main/java/martin/gui/MainWindow.java @@ -114,6 +114,7 @@ public void start(Stage stage) { /** * Input processing of user, and displaying of the user's text and Martin's reply. + * * @throws IOException */ private void handleUserInput() throws IOException { From bc2532432be1a267c2bd650c697aef7a3f5d1152 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 17 Sep 2023 17:35:11 +0800 Subject: [PATCH 22/35] Add Java CI workflow using GitHub Actions --- .github/workflows/gradle.yml | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .github/workflows/gradle.yml diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000000..fd8c44d086 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,50 @@ +name: Java CI + +on: [push, pull_request] + +jobs: + build: + strategy: + matrix: + platform: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.platform }} + + steps: + - name: Set up repository + uses: actions/checkout@master + + - name: Set up repository + uses: actions/checkout@master + with: + ref: master + + - name: Merge to master + run: git checkout --progress --force ${{ github.sha }} + + - name: Validate Gradle Wrapper + uses: gradle/wrapper-validation-action@v1 + + - name: Setup JDK 11 + uses: actions/setup-java@v1 + with: + java-version: '11' + java-package: jdk+fx + + - name: Build and check with Gradle + run: ./gradlew check + + - name: Perform IO redirection test (*NIX) + if: runner.os == 'Linux' + working-directory: ${{ github.workspace }}/text-ui-test + run: ./runtest.sh + + - name: Perform IO redirection test (MacOS) + if: always() && runner.os == 'macOS' + working-directory: ${{ github.workspace }}/text-ui-test + run: ./runtest.sh + + - name: Perform IO redirection test (Windows) + if: always() && runner.os == 'Windows' + working-directory: ${{ github.workspace }}/text-ui-test + shell: cmd + run: runtest.bat \ No newline at end of file From 13ab009d009ef8db485c2f3ac18a4ca6540095c1 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 17 Sep 2023 17:43:14 +0800 Subject: [PATCH 23/35] Changed runtest files --- .github/workflows/gradle.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index fd8c44d086..3f9e591530 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -25,8 +25,9 @@ jobs: uses: gradle/wrapper-validation-action@v1 - name: Setup JDK 11 - uses: actions/setup-java@v1 + uses: actions/setup-java@v2 with: + distribution: 'temurin' java-version: '11' java-package: jdk+fx From 8a2a77c5edb6fe5cb4cd46f5b6c860a84156b5c6 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 17 Sep 2023 17:47:39 +0800 Subject: [PATCH 24/35] Change JDK version --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 3f9e591530..211382352b 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -28,7 +28,7 @@ jobs: uses: actions/setup-java@v2 with: distribution: 'temurin' - java-version: '11' + java-version: '17' java-package: jdk+fx - name: Build and check with Gradle From 6ae82a39aa8ddbcaacffb3c75091c7c50c537ff9 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 17 Sep 2023 18:23:01 +0800 Subject: [PATCH 25/35] Add LocalDateTime support to Events --- .github/workflows/gradle.yml | 2 +- .../java/martin/commands/EventCommand.java | 17 ++++++++++++-- src/main/java/martin/gui/MainWindow.java | 10 ++++---- src/main/java/martin/task/Deadline.java | 6 +++++ src/main/java/martin/task/Event.java | 15 +++++++++--- src/main/java/martin/task/Task.java | 23 ++++++++++++++++--- text-ui-test/runtest.bat | 2 +- text-ui-test/runtest.sh | 2 +- 8 files changed, 62 insertions(+), 15 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 211382352b..8d66dd7b51 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -24,7 +24,7 @@ jobs: - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@v1 - - name: Setup JDK 11 + - name: Setup JDK 17 uses: actions/setup-java@v2 with: distribution: 'temurin' diff --git a/src/main/java/martin/commands/EventCommand.java b/src/main/java/martin/commands/EventCommand.java index 90a4ada09d..c2f38b3caf 100644 --- a/src/main/java/martin/commands/EventCommand.java +++ b/src/main/java/martin/commands/EventCommand.java @@ -5,6 +5,9 @@ import martin.task.Event; import martin.task.Task; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.ArrayList; /** @@ -35,11 +38,21 @@ public String execute() throws MartinException { String[] parts = command.substring(6).split(" /from "); String[] timeParts = parts[1].split(" /to "); + if (parts.length < 2 || parts[0].trim().isEmpty() || timeParts.length < 2 || timeParts[0].trim().isEmpty() || timeParts[1].trim().isEmpty()) { throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of an event or its time cannot be empty."); } - tasks.add(new Event(parts[0], timeParts[0], timeParts[1])); - return "Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yyyy HHmm"); + try { + LocalDateTime from = LocalDateTime.parse(timeParts[0].trim(), formatter); + LocalDateTime to = LocalDateTime.parse(timeParts[1].trim(), formatter); + + tasks.add(new Event(parts[0].trim(), from, to)); + + return "Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."; + } catch (DateTimeParseException e) { + throw new MartinException("☹ OOPS!!! The time of the event is in an invalid format. Please use the format d/M/yyyy HHmm."); + } } } diff --git a/src/main/java/martin/gui/MainWindow.java b/src/main/java/martin/gui/MainWindow.java index 7dee580ac9..d1a76c1c99 100644 --- a/src/main/java/martin/gui/MainWindow.java +++ b/src/main/java/martin/gui/MainWindow.java @@ -113,9 +113,9 @@ public void start(Stage stage) { } /** - * Input processing of user, and displaying of the user's text and Martin's reply. + * Processes user input and displays both user's text and Martin's reply. * - * @throws IOException + * @throws IOException If there is an error during input/output operation. */ private void handleUserInput() throws IOException { String input = userInput.getText(); @@ -139,9 +139,11 @@ private void handleUserInput() throws IOException { } /** - * Execute getResponse in Martin based on the given input. + * Executes the getResponse method in Martin using the provided input. * - * @throws IOException + * @param input The input string to get a response for. + * @return The response from Martin for the given input. + * @throws IOException If there is an error during input/output operation. */ private String getResponse(String input) throws IOException { return martin.getResponse(input); diff --git a/src/main/java/martin/task/Deadline.java b/src/main/java/martin/task/Deadline.java index 1cc34fb45c..0368c54947 100644 --- a/src/main/java/martin/task/Deadline.java +++ b/src/main/java/martin/task/Deadline.java @@ -1,5 +1,6 @@ package martin.task; +import java.time.Duration; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -20,6 +21,11 @@ public LocalDateTime getBy() { return by; } + @Override + public void snooze(Duration duration) { + this.by = this.by.plus(duration); + } + @Override public String toString() { return "[D]" + super.toString() + " (by: " + by.format(DateTimeFormatter.ofPattern("MMM dd yyyy HH:mm")) + ")"; diff --git a/src/main/java/martin/task/Event.java b/src/main/java/martin/task/Event.java index 3a7b0dc5ba..3ab50c6ed1 100644 --- a/src/main/java/martin/task/Event.java +++ b/src/main/java/martin/task/Event.java @@ -1,15 +1,24 @@ package martin.task; +import java.time.Duration; +import java.time.LocalDateTime; + public class Event extends Task { - protected String from; - protected String to; + private LocalDateTime from; + private LocalDateTime to; - public Event(String description, String from, String to) { + public Event(String description, LocalDateTime from, LocalDateTime to) { super(description); this.from = from; this.to = to; } + @Override + public void snooze(Duration duration) { + this.from = this.from.plus(duration); + this.to = this.to.plus(duration); + } + @Override public String toString() { return "[E][" + (isDone ? "X" : " ") + "] " + description + " (from: " + from + " to: " + to + ")"; diff --git a/src/main/java/martin/task/Task.java b/src/main/java/martin/task/Task.java index bf2fc0e756..18cf27e4bc 100644 --- a/src/main/java/martin/task/Task.java +++ b/src/main/java/martin/task/Task.java @@ -1,5 +1,6 @@ package martin.task; +import java.time.Duration; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -28,21 +29,37 @@ public void unmarkAsDone() { isDone = false; } + /** + * Checks if the task's description contains the specified keyword. + * + * @param keyword The keyword to check against the task's description. + * @return true if the task's description contains the keyword, otherwise false. + */ public boolean contains(String keyword) { return description.contains(keyword); } /** - * Convert the task to its file format representation. + * Snoozes or postpones the task by the given duration. + * + * @param duration The amount of time to snooze or postpone the task. + * @throws UnsupportedOperationException if the task type does not support snoozing. + */ + public void snooze(Duration duration) { + throw new UnsupportedOperationException("This task type cannot be snoozed."); + } + + /** + * Converts the task to its corresponding file format representation. * - * @return String representation of task for file storage. + * @return A string representation of the task suitable for file storage. */ public String toFileFormat() { return (isDone ? "1" : "0") + " | " + description; } /** - * Convert a file format string back to a Task. + * Converts a file format string back to a Task. * * @param fileFormatString String representation from file. * @return A new Task instance. diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 0b58ac029d..306700ddc2 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -7,7 +7,7 @@ REM delete output from previous run if exist ACTUAL.TXT del ACTUAL.TXT REM compile the code into the bin folder -javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\*.java +javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\*.* IF ERRORLEVEL 1 ( echo ********** BUILD FAILURE ********** exit /b 1 diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index a9070b0b01..d9b29808c5 100755 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -13,7 +13,7 @@ then fi # compile the code into the bin folder, terminates if error occurred -if ! javac -cp ../src/main/java -Xlint:none -d ../bin ../src/main/java/*.java +if ! javac -cp ../src/main/java/martin -Xlint:none -d ../bin ../src/main/java/*.java then echo "********** BUILD FAILURE **********" exit 1 From f33d4f049e39d1e3201f577f9dd5f8a8dda28c73 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 17 Sep 2023 18:51:30 +0800 Subject: [PATCH 26/35] Implement Snooze and enhance Event format This commit introduces the snooze command for the task management app. Users can now snooze their tasks, delaying them by a specified amount of times in minutes. Additionally, events now support the toFileFormat method. Javadoc headers have also been added to all command class constructors. --- .../java/martin/commands/DateCommand.java | 30 ++++++--- .../java/martin/commands/DeadlineCommand.java | 16 +++-- .../java/martin/commands/DeleteCommand.java | 14 ++-- .../java/martin/commands/EventCommand.java | 27 ++++++-- .../java/martin/commands/FindCommand.java | 14 ++-- .../java/martin/commands/ListCommand.java | 5 ++ .../java/martin/commands/MarkCommand.java | 14 ++-- .../java/martin/commands/SnoozeCommand.java | 64 +++++++++++++++++++ .../java/martin/commands/TodoCommand.java | 16 +++-- .../java/martin/commands/UnmarkCommand.java | 14 ++-- src/main/java/martin/parser/Parser.java | 4 +- src/main/java/martin/task/Event.java | 16 ++++- 12 files changed, 191 insertions(+), 43 deletions(-) create mode 100644 src/main/java/martin/commands/SnoozeCommand.java diff --git a/src/main/java/martin/commands/DateCommand.java b/src/main/java/martin/commands/DateCommand.java index a5712a1f62..d6d04e9046 100644 --- a/src/main/java/martin/commands/DateCommand.java +++ b/src/main/java/martin/commands/DateCommand.java @@ -3,6 +3,7 @@ import martin.exceptions.InvalidDateFormatException; import martin.exceptions.MartinException; import martin.task.Deadline; +import martin.task.Event; import martin.task.Task; import java.time.LocalDate; @@ -16,11 +17,17 @@ */ public class DateCommand implements Command { - private String command; + private String input; private ArrayList tasks; - public DateCommand(String command, ArrayList tasks) { - this.command = command; + /** + * Creates a Date Command. + * + * @param input The user command input. + * @param tasks The list of tasks. + */ + public DateCommand(String input, ArrayList tasks) { + this.input = input; this.tasks = tasks; } @@ -32,20 +39,20 @@ public DateCommand(String command, ArrayList tasks) { */ @Override public String execute() throws MartinException { - LocalDate date = parseDate(command); + LocalDate date = parseDate(input); return formatTasksForDate(date); } /** * Parses the date from the given command. * - * @param command The input command string. + * @param input The user command input. * @return LocalDate The parsed date. * @throws InvalidDateFormatException When the date format is incorrect. */ - private LocalDate parseDate(String command) throws InvalidDateFormatException { + private LocalDate parseDate(String input) throws InvalidDateFormatException { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yyyy"); - String[] parts = command.split(" ", 2); // Splits into two parts + String[] parts = input.split(" ", 2); // Splits into two parts if (parts.length < 2) { throw new InvalidDateFormatException("Invalid date format. The command is missing a date."); @@ -72,7 +79,12 @@ private String formatTasksForDate(LocalDate date) { // Get tasks for the date String tasksOnDate = tasks.stream() - .filter(task -> task instanceof Deadline && ((Deadline) task).getBy().toLocalDate().equals(date)) + .filter(task -> + (task instanceof Deadline && ((Deadline) task).getBy().toLocalDate().equals(date)) || + (task instanceof Event && + !date.isBefore(((Event) task).getFrom().toLocalDate()) && + !date.isAfter(((Event) task).getTo().toLocalDate())) + ) .map(task -> task.toString()) .collect(Collectors.joining("\n")); @@ -84,4 +96,4 @@ private String formatTasksForDate(LocalDate date) { return response.toString(); } -} +} \ No newline at end of file diff --git a/src/main/java/martin/commands/DeadlineCommand.java b/src/main/java/martin/commands/DeadlineCommand.java index 2121957165..04c3b97ce2 100644 --- a/src/main/java/martin/commands/DeadlineCommand.java +++ b/src/main/java/martin/commands/DeadlineCommand.java @@ -12,11 +12,17 @@ */ public class DeadlineCommand implements Command { - private String command; + private String input; private ArrayList tasks; - public DeadlineCommand(String command, ArrayList tasks) { - this.command = command; + /** + * Creates a Deadline Command. + * + * @param input The user command input. + * @param tasks The list of tasks. + */ + public DeadlineCommand(String input, ArrayList tasks) { + this.input = input; this.tasks = tasks; } @@ -29,11 +35,11 @@ public DeadlineCommand(String command, ArrayList tasks) { */ @Override public String execute() throws MartinException { - if (command.length() <= 8) { + if (input.length() <= 8) { throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a deadline cannot be empty."); } - String[] parts = command.substring(9).split(" /by "); + String[] parts = input.substring(9).split(" /by "); if (parts.length < 2 || parts[0].trim().isEmpty() || parts[1].trim().isEmpty()) { throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a deadline or its date cannot be empty."); } diff --git a/src/main/java/martin/commands/DeleteCommand.java b/src/main/java/martin/commands/DeleteCommand.java index 398bcdd99e..206eb64eb5 100644 --- a/src/main/java/martin/commands/DeleteCommand.java +++ b/src/main/java/martin/commands/DeleteCommand.java @@ -11,11 +11,17 @@ */ public class DeleteCommand implements Command { - private String command; + private String input; private ArrayList tasks; - public DeleteCommand(String command, ArrayList tasks) { - this.command = command; + /** + * Creates a Delete Command. + * + * @param input The user command input. + * @param tasks The list of tasks. + */ + public DeleteCommand(String input, ArrayList tasks) { + this.input = input; this.tasks = tasks; } @@ -29,7 +35,7 @@ public DeleteCommand(String command, ArrayList tasks) { @Override public String execute() throws MartinException { try { - int taskNo = Integer.parseInt(command.split(" ")[1]); + int taskNo = Integer.parseInt(input.split(" ")[1]); if (taskNo <= 0 || taskNo > tasks.size()) { throw new InvalidTaskNumberException("Task number out of range."); diff --git a/src/main/java/martin/commands/EventCommand.java b/src/main/java/martin/commands/EventCommand.java index c2f38b3caf..9e24d4dee0 100644 --- a/src/main/java/martin/commands/EventCommand.java +++ b/src/main/java/martin/commands/EventCommand.java @@ -15,11 +15,17 @@ */ public class EventCommand implements Command { - private String command; + private String input; private ArrayList tasks; - public EventCommand(String command, ArrayList tasks) { - this.command = command; + /** + * Creates an Event Command. + * + * @param input The user command input. + * @param tasks The list of tasks. + */ + public EventCommand(String input, ArrayList tasks) { + this.input = input; this.tasks = tasks; } @@ -32,14 +38,23 @@ public EventCommand(String command, ArrayList tasks) { */ @Override public String execute() throws MartinException { - if (command.length() <= 5) { + if (input.length() <= 5) { throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of an event cannot be empty."); } - String[] parts = command.substring(6).split(" /from "); + String[] parts = input.substring(6).split(" /from "); + + if (parts.length < 2) { + throw new MartinException("☹ OOPS!!! The command is missing the time details. Please specify the event starting time using /from."); + } + String[] timeParts = parts[1].split(" /to "); - if (parts.length < 2 || parts[0].trim().isEmpty() || timeParts.length < 2 || timeParts[0].trim().isEmpty() || timeParts[1].trim().isEmpty()) { + if (timeParts.length < 2) { + throw new MartinException("☹ OOPS!!! The command is missing the ending time. Please specify the event ending time using /to."); + } + + if (parts[0].trim().isEmpty() || timeParts[0].trim().isEmpty() || timeParts[1].trim().isEmpty()) { throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of an event or its time cannot be empty."); } diff --git a/src/main/java/martin/commands/FindCommand.java b/src/main/java/martin/commands/FindCommand.java index ae1dcee56b..d3d2c2bef7 100644 --- a/src/main/java/martin/commands/FindCommand.java +++ b/src/main/java/martin/commands/FindCommand.java @@ -9,11 +9,17 @@ */ public class FindCommand implements Command { - private String command; + private String input; private ArrayList tasks; - public FindCommand(String command, ArrayList tasks) { - this.command = command; + /** + * Creates a Find Command. + * + * @param input The user command input. + * @param tasks The list of tasks. + */ + public FindCommand(String input, ArrayList tasks) { + this.input = input; this.tasks = tasks; } @@ -25,7 +31,7 @@ public FindCommand(String command, ArrayList tasks) { @Override public String execute() { ArrayList matchingTasks = new ArrayList<>(); - String keyword = command.substring(5); + String keyword = input.substring(5); for (Task task : tasks) { if (task.contains(keyword)) { diff --git a/src/main/java/martin/commands/ListCommand.java b/src/main/java/martin/commands/ListCommand.java index 85fde95cba..bb7a2a5095 100644 --- a/src/main/java/martin/commands/ListCommand.java +++ b/src/main/java/martin/commands/ListCommand.java @@ -11,6 +11,11 @@ public class ListCommand implements Command { private ArrayList tasks; + /** + * Creates a List Command. + * + * @param input The user command input. + */ public ListCommand(ArrayList tasks) { this.tasks = tasks; } diff --git a/src/main/java/martin/commands/MarkCommand.java b/src/main/java/martin/commands/MarkCommand.java index b07ba51cae..295d4f84d3 100644 --- a/src/main/java/martin/commands/MarkCommand.java +++ b/src/main/java/martin/commands/MarkCommand.java @@ -12,11 +12,17 @@ */ public class MarkCommand implements Command { - private String command; + private String input; private ArrayList tasks; - public MarkCommand(String command, ArrayList tasks) { - this.command = command; + /** + * Creates a Mark Command. + * + * @param input The user command input. + * @param tasks The list of tasks. + */ + public MarkCommand(String input, ArrayList tasks) { + this.input = input; this.tasks = tasks; } @@ -31,7 +37,7 @@ public MarkCommand(String command, ArrayList tasks) { @Override public String execute() throws MartinException { try { - int taskNo = Integer.parseInt(command.split(" ")[1]); + int taskNo = Integer.parseInt(input.split(" ")[1]); if (taskNo <= 0 || taskNo > tasks.size()) { throw new InvalidTaskNumberException("Invalid task number."); } diff --git a/src/main/java/martin/commands/SnoozeCommand.java b/src/main/java/martin/commands/SnoozeCommand.java new file mode 100644 index 0000000000..a1d002c38d --- /dev/null +++ b/src/main/java/martin/commands/SnoozeCommand.java @@ -0,0 +1,64 @@ +package martin.commands; + +import martin.exceptions.MartinException; +import martin.task.Task; +import java.time.Duration; +import java.util.ArrayList; + +/** + * Represents a command that handles the snoozing of tasks in the task list. + */ +public class SnoozeCommand implements Command { + + private String input; + private ArrayList tasks; + + /** + * Creates a SnoozeCommand. + * + * @param input The user command input. + * @param tasks The list of tasks. + */ + public SnoozeCommand(String input, ArrayList tasks) { + this.input = input; + this.tasks = tasks; + } + + /** + * Snoozes a task by the specified duration. + * + * @return A confirmation message indicating that the task has been snoozed. + * @throws MartinException If there's an error executing the command. + */ + @Override + public String execute() throws MartinException { + String[] parts = input.split(" "); + + if (parts.length != 3) { + throw new MartinException("☹ OOPS!!! Incorrect format for snooze command."); + } + + int taskIndex; + int durationMinutes; + + try { + taskIndex = Integer.parseInt(parts[1]); + durationMinutes = Integer.parseInt(parts[2]); + } catch (NumberFormatException e) { + throw new MartinException("☹ OOPS!!! Invalid task index or snooze duration."); + } + + if (taskIndex <= 0 || taskIndex > tasks.size()) { + throw new MartinException("☹ OOPS!!! The task index is out of range."); + } + + Task task = tasks.get(taskIndex - 1); // Adjust for 0-based indexing + + try { + task.snooze(Duration.ofMinutes(durationMinutes)); + return "Got it. I've snoozed the task:\n " + task; + } catch (UnsupportedOperationException e) { + throw new MartinException("☹ OOPS!!! This task cannot be snoozed."); + } + } +} diff --git a/src/main/java/martin/commands/TodoCommand.java b/src/main/java/martin/commands/TodoCommand.java index a623399b5d..ef51d56e2b 100644 --- a/src/main/java/martin/commands/TodoCommand.java +++ b/src/main/java/martin/commands/TodoCommand.java @@ -12,11 +12,17 @@ */ public class TodoCommand implements Command { - private String command; + private String input; private ArrayList tasks; - public TodoCommand(String command, ArrayList tasks) { - this.command = command; + /** + * Creates a Todo Command. + * + * @param input The user command input. + * @param tasks The list of tasks. + */ + public TodoCommand(String input, ArrayList tasks) { + this.input = input; this.tasks = tasks; } @@ -29,11 +35,11 @@ public TodoCommand(String command, ArrayList tasks) { */ @Override public String execute() throws MartinException { - if (command.length() <= 4) { + if (input.length() <= 4) { throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a todo cannot be empty."); } - String description = command.substring(5); + String description = input.substring(5); if (description.isEmpty()) { throw new EmptyTaskDescriptionException("☹ OOPS!!! The description of a todo cannot be empty."); } diff --git a/src/main/java/martin/commands/UnmarkCommand.java b/src/main/java/martin/commands/UnmarkCommand.java index de9be9a384..e336687307 100644 --- a/src/main/java/martin/commands/UnmarkCommand.java +++ b/src/main/java/martin/commands/UnmarkCommand.java @@ -12,11 +12,17 @@ */ public class UnmarkCommand implements Command { - private String command; + private String input; private ArrayList tasks; - public UnmarkCommand(String command, ArrayList tasks) { - this.command = command; + /** + * Creates an Unmark Command. + * + * @param input The user command input. + * @param tasks The list of tasks. + */ + public UnmarkCommand(String input, ArrayList tasks) { + this.input = input; this.tasks = tasks; } @@ -31,7 +37,7 @@ public UnmarkCommand(String command, ArrayList tasks) { @Override public String execute() throws MartinException { try { - int taskNo = Integer.parseInt(command.split(" ")[1]); + int taskNo = Integer.parseInt(input.split(" ")[1]); if (taskNo <= 0 || taskNo > tasks.size()) { throw new InvalidTaskNumberException("Invalid task number."); } diff --git a/src/main/java/martin/parser/Parser.java b/src/main/java/martin/parser/Parser.java index ce86048085..d45e4152a4 100644 --- a/src/main/java/martin/parser/Parser.java +++ b/src/main/java/martin/parser/Parser.java @@ -9,6 +9,7 @@ import martin.commands.ListCommand; import martin.commands.MarkCommand; +import martin.commands.SnoozeCommand; import martin.commands.TodoCommand; import martin.commands.UnmarkCommand; import martin.commands.Command; @@ -18,7 +19,6 @@ import martin.commands.EventCommand; import martin.commands.FindCommand; - public class Parser { private TaskList taskList; @@ -62,6 +62,8 @@ public Command parse(String input) throws MartinException { return new DateCommand(input, tasks); case "find": return new FindCommand(input, tasks); + case "snooze": + return new SnoozeCommand(input, tasks); default: throw new InvalidCommandException("Unknown command: " + input); } diff --git a/src/main/java/martin/task/Event.java b/src/main/java/martin/task/Event.java index 3ab50c6ed1..d81ddf1fc3 100644 --- a/src/main/java/martin/task/Event.java +++ b/src/main/java/martin/task/Event.java @@ -2,6 +2,7 @@ import java.time.Duration; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; public class Event extends Task { private LocalDateTime from; @@ -13,6 +14,14 @@ public Event(String description, LocalDateTime from, LocalDateTime to) { this.to = to; } + public LocalDateTime getFrom() { + return from; + } + + public LocalDateTime getTo() { + return to; + } + @Override public void snooze(Duration duration) { this.from = this.from.plus(duration); @@ -21,6 +30,11 @@ public void snooze(Duration duration) { @Override public String toString() { - return "[E][" + (isDone ? "X" : " ") + "] " + description + " (from: " + from + " to: " + to + ")"; + return "[E]" + super.toString() + " (from: " + from.format(DateTimeFormatter.ofPattern("MMM dd yyyy HH:mm")) + " to: " + to.format(DateTimeFormatter.ofPattern("MMM dd yyyy HH:mm")) + ")"; + } + + @Override + public String toFileFormat() { + return "E | " + (isDone() ? "1" : "0") + " | " + getDescription() + " | " + from.format(DateTimeFormatter.ofPattern("dd/MM/yyyy HHmm")) + " | " + to.format(DateTimeFormatter.ofPattern("dd/MM/yyyy HHmm")); } } \ No newline at end of file From 493ce02ba6843054ad9ab9e60786de20108e4e97 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Sun, 17 Sep 2023 19:50:59 +0800 Subject: [PATCH 27/35] Implement Sort Command and support for parsing Events Implement SortCommand to enable task sorting functionality. Extend the fromFileFormat method in Task to handle Event tasks. --- src/main/java/martin/Martin.java | 9 +-- .../java/martin/commands/EventCommand.java | 4 ++ .../java/martin/commands/SortCommand.java | 61 +++++++++++++++++++ src/main/java/martin/parser/Parser.java | 3 + src/main/java/martin/task/Deadline.java | 11 +++- src/main/java/martin/task/Task.java | 17 ++++++ 6 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 src/main/java/martin/commands/SortCommand.java diff --git a/src/main/java/martin/Martin.java b/src/main/java/martin/Martin.java index 06c9753d3b..2588349f2d 100644 --- a/src/main/java/martin/Martin.java +++ b/src/main/java/martin/Martin.java @@ -15,13 +15,14 @@ public Martin(String filePath) { storage = new Storage(filePath); try { tasks = new TaskList(storage.loadFromFile()); - parser = new Parser(tasks); } catch (Exception e) { + e.printStackTrace(); tasks = new TaskList(); + } finally { + parser = new Parser(tasks); + assert tasks != null : "Tasks should be initialized."; + assert parser != null : "Parser should be initialized."; } - - assert tasks != null : "Tasks should be initialized."; - assert parser != null : "Parser should be initialized."; } /** diff --git a/src/main/java/martin/commands/EventCommand.java b/src/main/java/martin/commands/EventCommand.java index 9e24d4dee0..3a452ecf84 100644 --- a/src/main/java/martin/commands/EventCommand.java +++ b/src/main/java/martin/commands/EventCommand.java @@ -63,6 +63,10 @@ public String execute() throws MartinException { LocalDateTime from = LocalDateTime.parse(timeParts[0].trim(), formatter); LocalDateTime to = LocalDateTime.parse(timeParts[1].trim(), formatter); + if (from.isAfter(to) || from.isEqual(to)) { + throw new MartinException("☹ OOPS!!! The start time of the event must be earlier than the end time."); + } + tasks.add(new Event(parts[0].trim(), from, to)); return "Got it. I've added this task:\n " + tasks.get(tasks.size() - 1) + "\n Now you have " + tasks.size() + " tasks in the list."; diff --git a/src/main/java/martin/commands/SortCommand.java b/src/main/java/martin/commands/SortCommand.java new file mode 100644 index 0000000000..c93c1cc5fd --- /dev/null +++ b/src/main/java/martin/commands/SortCommand.java @@ -0,0 +1,61 @@ +package martin.commands; + +import martin.exceptions.MartinException; +import martin.task.Deadline; +import martin.task.Event; +import martin.task.Task; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +/** + * Represents a command that sorts the tasks in the task list. + */ +public class SortCommand implements Command { + + private ArrayList tasks; + + /** + * Creates a SortCommand. + * + * @param tasks The list of tasks. + */ + public SortCommand(ArrayList tasks) { + this.tasks = tasks; + } + + /** + * Retrieves the LocalDateTime associated with the task. + * + * @param task The task to inspect. + * @return The LocalDateTime associated with the task. + */ + private LocalDateTime getDateFromTask(Task task) { + if (task instanceof Deadline) { + return ((Deadline) task).getBy(); + } else if (task instanceof Event) { + return ((Event) task).getFrom(); + } + return LocalDateTime.MAX; // Default, for tasks without a date + } + + /** + * Sorts the tasks by date and time. + * + * @return A confirmation message indicating that the tasks have been sorted. + * @throws MartinException If there's an error executing the command. + */ + @Override + public String execute() throws MartinException { + if (tasks.isEmpty()) { + return "☹ OOPS!!! The task list is empty."; + } + + Comparator taskComparator = Comparator.comparing(this::getDateFromTask); + Collections.sort(tasks, taskComparator); + + return "Tasks have been sorted!"; + } +} diff --git a/src/main/java/martin/parser/Parser.java b/src/main/java/martin/parser/Parser.java index d45e4152a4..8877358bf8 100644 --- a/src/main/java/martin/parser/Parser.java +++ b/src/main/java/martin/parser/Parser.java @@ -10,6 +10,7 @@ import martin.commands.ListCommand; import martin.commands.MarkCommand; import martin.commands.SnoozeCommand; +import martin.commands.SortCommand; import martin.commands.TodoCommand; import martin.commands.UnmarkCommand; import martin.commands.Command; @@ -64,6 +65,8 @@ public Command parse(String input) throws MartinException { return new FindCommand(input, tasks); case "snooze": return new SnoozeCommand(input, tasks); + case "sort": + return new SortCommand(tasks); default: throw new InvalidCommandException("Unknown command: " + input); } diff --git a/src/main/java/martin/task/Deadline.java b/src/main/java/martin/task/Deadline.java index 0368c54947..3c02e8a26a 100644 --- a/src/main/java/martin/task/Deadline.java +++ b/src/main/java/martin/task/Deadline.java @@ -3,6 +3,9 @@ import java.time.Duration; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +import martin.exceptions.MartinException; public class Deadline extends Task { private LocalDateTime by; @@ -12,9 +15,13 @@ public Deadline(String description, LocalDateTime by) { this.by = by; } - public Deadline(String description, String by) { + public Deadline(String description, String by) throws MartinException { super(description); - this.by = LocalDateTime.parse(by, DateTimeFormatter.ofPattern("d/M/yyyy HHmm")); + try { + this.by = LocalDateTime.parse(by, DateTimeFormatter.ofPattern("d/M/yyyy HHmm")); + } catch (DateTimeParseException e) { + throw new MartinException("☹ OOPS!!! The deadline time component is missing. Expected format: d/M/yyyy HHmm"); + } } public LocalDateTime getBy() { diff --git a/src/main/java/martin/task/Task.java b/src/main/java/martin/task/Task.java index 18cf27e4bc..44e98a22bc 100644 --- a/src/main/java/martin/task/Task.java +++ b/src/main/java/martin/task/Task.java @@ -87,6 +87,23 @@ public static Task fromFileFormat(String fileFormatString) { return deadline; + case "E": + if (parts.length != 5) { + throw new IllegalArgumentException("Invalid Event format in file."); + } + + boolean isDoneEvent = "1".equals(parts[1]); + String descriptionEvent = parts[2]; + LocalDateTime from = LocalDateTime.parse(parts[3], DateTimeFormatter.ofPattern("dd/MM/yyyy HHmm")); + LocalDateTime to = LocalDateTime.parse(parts[4], DateTimeFormatter.ofPattern("dd/MM/yyyy HHmm")); + + Event event = new Event(descriptionEvent, from, to); + if (isDoneEvent) { + event.markAsDone(); + } + + return event; + default: if (parts.length != 2) { throw new IllegalArgumentException("Invalid task format in file."); From d965a0e0e5aad45ae6ef1d9a9edc77d288311454 Mon Sep 17 00:00:00 2001 From: jonyeokj Date: Tue, 19 Sep 2023 19:17:53 +0800 Subject: [PATCH 28/35] Improve GUI and updated README --- docs/README.md | 247 +++++++++++++++++++++-- docs/Ui.png | Bin 0 -> 259541 bytes src/main/java/martin/gui/DialogBox.java | 16 +- src/main/java/martin/gui/MainWindow.java | 46 ++++- 4 files changed, 290 insertions(+), 19 deletions(-) create mode 100644 docs/Ui.png diff --git a/docs/README.md b/docs/README.md index 8077118ebe..1f55942f01 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,29 +1,250 @@ -# User Guide +# Martin Chatbot User Guide ## Features -### Feature-ABC +- **Date Command**: Find tasks by a specific date. +- **Deadline Command**: Create a deadline task. +- **Event Command**: Create an event with a start and end time. +- **Todo Command**: Add a simple task. +- **Delete Command**: Remove a task based on its index. +- **Find Command**: Search for tasks with a keyword. +- **List Command**: Show all tasks currently stored. +- **Mark Command**: Mark a specific task as done. +- **Unmark Command**: Revert a task to its undone status. +- **Snooze Command**: Delay a task by a number of minutes. +- **Sort Command**: Organize tasks chronologically. -Description of the feature. +## Usage -### Feature-XYZ +### `date` - Find tasks by date -Description of the feature. +Given a specific date, it displays all tasks (deadlines or events) associated with that date. -## Usage +**Example of usage:** + +`date d/M/yyyy` + +**Expected outcome:** + +`Tasks on [date]` +A list of all deadlines or events for the given date is shown. If there are no tasks on the provided date, the output will display "No tasks on this date." + +**Notes:** +- Use the format 'd/M/yyyy' for the date. +- The list will display tasks on the date or events spanning the date. + +**Example output:** +``` +Tasks on 1 1 2023: + +[D][] Homework (by: Jan 01 2023 2359) +[E][] Test (from: Jan 01 2023 1500 to: Jan 01 2023 1700) +``` + +### `deadline` - Create a deadline task + +Create a new deadline task and add it to the list. Deadlines have a description and a specific due date-time. + +**Example of usage:** -### `Keyword` - Describe action +\`deadline [description] /by d/M/yyyy HHmm\` -Describe the action and its outcome. +**Expected outcome:** -Example of usage: +A confirmation message indicating that the deadline has been added. -`keyword (optional arguments)` +**Example output:** +``` +Got it. I've added this task: +[D][] Homework (by: Jan 01 2023 2359) +Now you have [total tasks] tasks in the list. +``` + +### `event` - Create an event task + +Create a new event task and add it to the list. Events have a description and a start and end date-time. + +**Example of usage:** + +\`event [description] /from d/M/yyyy HHmm /to d/M/yyyy HHmm\` + +**Expected outcome:** -Expected outcome: +A confirmation message indicating that the event has been added. -Description of the outcome. +**Example output:** ``` -expected output +Got it. I've added this task: +[E][] Meeting (from: Jan 01 2023 1500 to: Jan 01 2023 1700) +Now you have [total tasks] tasks in the list. +``` + +### `todo` - Create a todo task + +Create a new todo task and add it to the list. Todos only have a description. + +**Example of usage:** + +\`todo [description]\` + +**Expected outcome:** + +A confirmation message indicating that the todo task has been added. + +**Example output:** + +``` +Got it. I've added this task: +[T][] Read a book +Now you have [total tasks] tasks in the list. +``` + +### `delete` - Remove a task from the list + +Delete a task based on its index in the list. + +**Example of usage:** + +\`delete [index]\` + +**Expected outcome:** + +A confirmation message indicating that the task has been removed. + +**Notes:** +- Provide an integer index for the task's position in the list. +- Index starts from 1 (e.g., `delete 1` for the first task). +- Invalid or out-of-range indices will result in an error message. + +**Example output:** +``` +Noted. I've removed this task: +[T][] Read a book +Now you have [remaining tasks] tasks in the list. +``` + +### `find` - Search for tasks by keyword + +Search for tasks that contain a specified keyword in their descriptions. + +**Example of usage:** + +\`find [keyword]\` + +**Expected outcome:** + +A list of tasks whose descriptions contain the specified keyword is displayed. + +**Notes:** +- Matches tasks with the keyword as a substring in their description. + +**Example output:** +``` +Here are the matching tasks in your list: + +[T][] CS lecture +[D][] Submit CS assignment (by: Mar 01 2023 2359) +``` + +### `list` - Display all tasks + +Shows a list of all tasks currently in storage. + +**Example of usage:** + +\`list\` + +**Expected outcome:** + +A comprehensive list of all your tasks is displayed. + +**Example output:** + +``` +Here are the tasks in your list: + +[T][] Read a book +[D][] CS2103T ip submission (by: Sep 17 2023 2359) +[E][] Team meeting (from: Sep 20 2023 1500 to: Sep 20 2023 1700) +``` + +### `mark` - Mark a task as done + +Marks a task at the specified index as completed. + +**Example of usage:** + +\`mark [index]\` + +**Expected outcome:** + +The specified task is marked as done and a confirmation message is displayed. + +**Example output:** + +``` +Nice! I've marked this task as done: +[T][X] Read a book +``` + +### `unmark` - Unmark a task as done + +Unmarks a task at the specified index, setting it back to its incomplete state. + +**Example of usage:** + +\`unmark [index]\` + +**Expected outcome:** + +The specified task is unmarked as done and a confirmation message is displayed. + +**Example output:** +``` +OK, I've marked this task as not done yet: +[T][] Read a book +``` + +### `snooze` - Delay a task + +Snoozes a task at the specified index by a given number of minutes. + +**Example of usage:** + +\`snooze [index] [minutes]\` + +**Expected outcome:** + +The specified task's deadline or event time is delayed by the given number of minutes, and a confirmation message is shown. + +**Notes:** +- Todos do not support snoozing, so ensure you pick tasks that can be snoozed. + +**Example output:** +``` +Got it. I've snoozed the task: +[D][] Homework (by: Jan 02 2023 0019) +``` + +### `sort` - Sort tasks chronologically + +Sorts the tasks in the list chronologically, where deadlines are sorted by their due dates, events are sorted by their start dates, and tasks without dates (like ToDos) are placed at the end. + +**Example of usage:** + +\`sort\` + +**Expected outcome:** + +The tasks in the list will be rearranged in a chronological order based on their associated dates and times. A confirmation message will be displayed. + +**Notes:** +- Tasks without associated dates (e.g., Todo tasks) will be placed at the end of the list. +- If the task list is empty, a notification will be given. + +**Example output:** + ``` +Tasks have been sorted! +``` \ No newline at end of file diff --git a/docs/Ui.png b/docs/Ui.png new file mode 100644 index 0000000000000000000000000000000000000000..e22cd3a46fbe745eca795553f0039a2b725ba744 GIT binary patch literal 259541 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%0DrVU?I=7*u6G(&jp&Cx%=4rJ%G> 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