From 219f7a4e3cae07c7cdc3bfc77712b9c41d8044b8 Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 31 Jan 2025 17:16:15 +0800 Subject: [PATCH 01/19] Add Flaaaash remove Duke. --- src/main/java/Duke.java | 10 ---------- src/main/java/Flaaaash.java | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 10 deletions(-) delete mode 100644 src/main/java/Duke.java create mode 100644 src/main/java/Flaaaash.java diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334c..000000000 --- a/src/main/java/Duke.java +++ /dev/null @@ -1,10 +0,0 @@ -public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - } -} diff --git a/src/main/java/Flaaaash.java b/src/main/java/Flaaaash.java new file mode 100644 index 000000000..47ff80df6 --- /dev/null +++ b/src/main/java/Flaaaash.java @@ -0,0 +1,20 @@ +public class Flaaaash { + private static void greet() { + String greetingLine = "---------------------------------\n" + + "Hello! I'm Flaaaash\n" + + "What can I do for you?\n" + + "---------------------------------"; + System.out.println(greetingLine); + } + + private static void exit() { + String exitLine = "Bye. Hope to see you again soon!\n" + + "---------------------------------\n"; + System.out.println(exitLine); + } + + public static void main(String[] args) { + greet(); + exit(); + } +} From c85bf1cc415509b1da99abca071de381f12ae51a Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Thu, 6 Feb 2025 22:44:03 +0800 Subject: [PATCH 02/19] Add increment 1-3. Add Task class --- src/main/java/Flaaaash.java | 93 +++++++++++++++++++++++++++++++++---- src/main/java/Task.java | 26 +++++++++++ 2 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 src/main/java/Task.java diff --git a/src/main/java/Flaaaash.java b/src/main/java/Flaaaash.java index 47ff80df6..95409af61 100644 --- a/src/main/java/Flaaaash.java +++ b/src/main/java/Flaaaash.java @@ -1,20 +1,97 @@ +import java.util.Scanner; + public class Flaaaash { + private static final int maxCount = 100; + private static final Task[] tasks = new Task[maxCount]; + private static int taskCount = 0; + + private static void markTask(int index) { + System.out.println("____________________________________________________________"); + if (index < 0 || index > taskCount) { + System.out.println(" Invalid index"); + } else { + System.out.println(" Nice! I've marked this task as done:"); + tasks[index - 1].markAsDone(); + System.out.println(tasks[index - 1]); + } + System.out.println("____________________________________________________________"); + } + + private static void unmarkTask(int index) { + System.out.println("____________________________________________________________"); + if (index < 0 || index > taskCount) { + System.out.println(" Invalid index"); + } else { + System.out.println(" OK, I've marked this task as not done yet:"); + tasks[index - 1].unmarkAsDone(); + System.out.println(tasks[index - 1]); + } + System.out.println("____________________________________________________________"); + } + + private static void addTask(String taskName) { + tasks[taskCount] = new Task(taskName); + taskCount += 1; + + System.out.println("____________________________________________________________"); + System.out.println(" added: " + taskName); + System.out.println("____________________________________________________________"); + } + + private static void list() { + System.out.println("____________________________________________________________"); + if (taskCount == 0) { + System.out.println(" List is empty!"); + } else { + System.out.println("Here are the tasks in your list:"); + for(int i = 0; i < taskCount; i += 1) { + System.out.println(" " + (i + 1) + ". " + tasks[i]); + } + } + System.out.println("____________________________________________________________"); + } + + private static void repeat(String input) { + System.out.println("____________________________________________________________"); + System.out.println(" " + input); + System.out.println("____________________________________________________________"); + } + private static void greet() { - String greetingLine = "---------------------------------\n" - + "Hello! I'm Flaaaash\n" - + "What can I do for you?\n" - + "---------------------------------"; - System.out.println(greetingLine); + System.out.println("____________________________________________________________"); + System.out.println(" Hello! I'm Flaaaash"); + System.out.println(" What can I do for you?"); + System.out.println("____________________________________________________________"); } private static void exit() { - String exitLine = "Bye. Hope to see you again soon!\n" - + "---------------------------------\n"; - System.out.println(exitLine); + System.out.println("____________________________________________________________"); + System.out.println(" Bye. Hope to see you again soon!"); + System.out.println("____________________________________________________________"); } public static void main(String[] args) { greet(); + + Scanner scanner = new Scanner(System.in); + while (true) { + String input = scanner.nextLine(); + if (input.equals("bye")) { + break; + } else if (input.equals("list")) { + list(); + } else if (input.startsWith("mark")) { + int taskIndex = Integer.parseInt(input.split(" ")[1]); + markTask(taskIndex); + } else if (input.startsWith("unmark")) { + int taskIndex = Integer.parseInt(input.split(" ")[1]); + unmarkTask(taskIndex); + } else { + addTask(input); + } + } + exit(); + scanner.close(); } } diff --git a/src/main/java/Task.java b/src/main/java/Task.java new file mode 100644 index 000000000..3f3203a48 --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,26 @@ +public class Task { + protected String description; + protected boolean isDone; + + public Task(String description) { + this.description = description; + this.isDone = false; + } + + public String getStatusIcon() { + return (isDone ? "X" : " "); // mark done task with X + } + + public void markAsDone() { + this.isDone = true; + } + + public void unmarkAsDone() { + this.isDone = false; + } + + @Override + public String toString() { + return "[" + getStatusIcon() + "] " + description; + } +} From f915452e63b46e136633a8c83fe5146fd3d39b84 Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 7 Feb 2025 00:06:10 +0800 Subject: [PATCH 03/19] Add sub-classes Todo, Deadline and Event of Task class. --- src/main/java/Deadline.java | 13 +++++++ src/main/java/Event.java | 15 ++++++++ src/main/java/Flaaaash.java | 73 ++++++++++++++++++++++++++----------- src/main/java/Task.java | 8 ++-- src/main/java/Todo.java | 10 +++++ 5 files changed, 93 insertions(+), 26 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 000000000..2e4e51a78 --- /dev/null +++ b/src/main/java/Deadline.java @@ -0,0 +1,13 @@ +public class Deadline extends Task { + protected String deadLine; + + public Deadline(String taskName, String deadLine) { + super(taskName); + this.deadLine = deadLine; + } + + @Override + public String toString() { + return "[D]" + super.toString() + " (by: " + deadLine + ")"; + } +} diff --git a/src/main/java/Event.java b/src/main/java/Event.java new file mode 100644 index 000000000..3080cc867 --- /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 taskName, String from, String to) { + super(taskName); + this.from = from; + this.to = to; + } + + @Override + public String toString() { + return "[E]" + super.toString() + " (from: " + from + " to: " + to + ")"; + } +} diff --git a/src/main/java/Flaaaash.java b/src/main/java/Flaaaash.java index 95409af61..9b8ea8b74 100644 --- a/src/main/java/Flaaaash.java +++ b/src/main/java/Flaaaash.java @@ -12,7 +12,7 @@ private static void markTask(int index) { } else { System.out.println(" Nice! I've marked this task as done:"); tasks[index - 1].markAsDone(); - System.out.println(tasks[index - 1]); + System.out.println(" " + tasks[index - 1]); } System.out.println("____________________________________________________________"); } @@ -24,17 +24,19 @@ private static void unmarkTask(int index) { } else { System.out.println(" OK, I've marked this task as not done yet:"); tasks[index - 1].unmarkAsDone(); - System.out.println(tasks[index - 1]); + System.out.println(" " + tasks[index - 1]); } System.out.println("____________________________________________________________"); } - private static void addTask(String taskName) { - tasks[taskCount] = new Task(taskName); + private static void addTask(Task task) { + tasks[taskCount] = task; taskCount += 1; System.out.println("____________________________________________________________"); - System.out.println(" added: " + taskName); + System.out.println(" Got it. I've added this task:"); + System.out.println(" " + tasks[taskCount - 1]); + System.out.println(" Now you have " + taskCount + " tasks in the list."); System.out.println("____________________________________________________________"); } @@ -43,7 +45,7 @@ private static void list() { if (taskCount == 0) { System.out.println(" List is empty!"); } else { - System.out.println("Here are the tasks in your list:"); + System.out.println(" Here are the tasks in your list:"); for(int i = 0; i < taskCount; i += 1) { System.out.println(" " + (i + 1) + ". " + tasks[i]); } @@ -72,26 +74,53 @@ private static void exit() { public static void main(String[] args) { greet(); - Scanner scanner = new Scanner(System.in); + while (true) { - String input = scanner.nextLine(); - if (input.equals("bye")) { - break; - } else if (input.equals("list")) { + String input = scanner.nextLine().trim(); + String[] inputParts = input.split(" ", 2); + String command = inputParts[0].toLowerCase(); + + switch (command) { + case "bye": + exit(); + scanner.close(); + return; + + case "list": list(); - } else if (input.startsWith("mark")) { - int taskIndex = Integer.parseInt(input.split(" ")[1]); - markTask(taskIndex); - } else if (input.startsWith("unmark")) { - int taskIndex = Integer.parseInt(input.split(" ")[1]); - unmarkTask(taskIndex); - } else { - addTask(input); + break; + + case "mark": + markTask(Integer.parseInt(inputParts[1])); + break; + + case "unmark": + unmarkTask(Integer.parseInt(inputParts[1])); + break; + + case "todo": + addTask(new Todo(inputParts[1])); + break; + + case "deadline": + String[] deadlineInputDetails = inputParts[1].split(" /by ", 2); + String deadlineName = deadlineInputDetails[0]; + String deadLine = deadlineInputDetails[1]; + addTask(new Deadline(deadlineName, deadLine)); + break; + + case "event": + String[] eventInputDetails = inputParts[1].split(" /from | /to ", 3); + String eventName = eventInputDetails[0]; + String from = eventInputDetails[1]; + String to = eventInputDetails[2]; + addTask(new Event(eventName, from, to)); + break; + + default: + repeat(command); } } - - exit(); - scanner.close(); } } diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 3f3203a48..4eb668738 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -1,9 +1,9 @@ public class Task { - protected String description; + protected String taskName; protected boolean isDone; - public Task(String description) { - this.description = description; + public Task(String taskName) { + this.taskName = taskName; this.isDone = false; } @@ -21,6 +21,6 @@ public void unmarkAsDone() { @Override public String toString() { - return "[" + getStatusIcon() + "] " + description; + return "[" + getStatusIcon() + "] " + taskName; } } diff --git a/src/main/java/Todo.java b/src/main/java/Todo.java new file mode 100644 index 000000000..baff9a0be --- /dev/null +++ b/src/main/java/Todo.java @@ -0,0 +1,10 @@ +public class Todo extends Task { + public Todo(String taskName) { + super(taskName); + } + + @Override + public String toString() { + return "[T]" + super.toString(); + } +} From ef0e0e5f11b4e66a9d8288c9067b93b90d0039f7 Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 14 Feb 2025 12:05:33 +0800 Subject: [PATCH 04/19] Level-5. Exception handling --- src/main/java/Flaaaash.java | 29 +++++++++++++++++++++++++++-- text-ui-test/EXPECTED.TXT | 6 ------ text-ui-test/input.txt | 6 ++++++ text-ui-test/runtest.bat | 2 +- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/main/java/Flaaaash.java b/src/main/java/Flaaaash.java index 9b8ea8b74..05b23b7e2 100644 --- a/src/main/java/Flaaaash.java +++ b/src/main/java/Flaaaash.java @@ -92,31 +92,56 @@ public static void main(String[] args) { break; case "mark": - markTask(Integer.parseInt(inputParts[1])); + try { + markTask(Integer.parseInt(inputParts[1])); + break; + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println("Invalid format! Use: mark "); + } break; case "unmark": + try { unmarkTask(Integer.parseInt(inputParts[1])); break; + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println("Invalid format! Use: unmark "); + } + break; case "todo": - addTask(new Todo(inputParts[1])); + if (inputParts.length < 2) { + System.out.println("Task description cannot be empty."); + } else { + addTask(new Todo(inputParts[1])); + } break; case "deadline": + try { String[] deadlineInputDetails = inputParts[1].split(" /by ", 2); String deadlineName = deadlineInputDetails[0]; String deadLine = deadlineInputDetails[1]; addTask(new Deadline(deadlineName, deadLine)); break; + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println("Invalid format! Use: deadline /by "); + } + break; + case "event": + try { String[] eventInputDetails = inputParts[1].split(" /from | /to ", 3); String eventName = eventInputDetails[0]; String from = eventInputDetails[1]; String to = eventInputDetails[2]; addTask(new Event(eventName, from, to)); break; + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println("Invalid format! Use: event /from /to "); + } + break; default: repeat(command); diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e..8b1378917 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb..7dc7f0987 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,6 @@ +todo Liu Xuyan: Answer CS2113 tutorial questions +deadline weekly CS2113 quiz Monday /by 9pm +list +event CS2113 lecture /from Friday 4 /to 6pm +mark 1 +list \ No newline at end of file diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 087374464..eef4da0eb 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 Flaaaash < input.txt > ACTUAL.TXT REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT From 97802b21f8312109d976c9b437caa2618ada96b2 Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Thu, 20 Feb 2025 23:11:35 +0800 Subject: [PATCH 05/19] Add deleteTask(). Change tasks array to a ArrayList type for dynamic sizes. --- src/main/java/Flaaaash.java | 70 +++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 23 deletions(-) diff --git a/src/main/java/Flaaaash.java b/src/main/java/Flaaaash.java index 05b23b7e2..b905d433e 100644 --- a/src/main/java/Flaaaash.java +++ b/src/main/java/Flaaaash.java @@ -1,53 +1,62 @@ import java.util.Scanner; +import java.util.ArrayList; public class Flaaaash { - private static final int maxCount = 100; - private static final Task[] tasks = new Task[maxCount]; - private static int taskCount = 0; +// private static final int maxCount = 100; + private static final ArrayList tasks = new ArrayList<>(); +// private static int taskCount = 0; private static void markTask(int index) { System.out.println("____________________________________________________________"); - if (index < 0 || index > taskCount) { + if (index < 0 || index > tasks.size()) { System.out.println(" Invalid index"); } else { System.out.println(" Nice! I've marked this task as done:"); - tasks[index - 1].markAsDone(); - System.out.println(" " + tasks[index - 1]); + tasks.get(index - 1).markAsDone(); + System.out.println(" " + tasks.get(index - 1)); } System.out.println("____________________________________________________________"); } private static void unmarkTask(int index) { System.out.println("____________________________________________________________"); - if (index < 0 || index > taskCount) { + if (index < 0 || index > tasks.size()) { System.out.println(" Invalid index"); } else { System.out.println(" OK, I've marked this task as not done yet:"); - tasks[index - 1].unmarkAsDone(); - System.out.println(" " + tasks[index - 1]); + tasks.get(index - 1).unmarkAsDone(); + System.out.println(" " + tasks.get(index - 1)); } System.out.println("____________________________________________________________"); } private static void addTask(Task task) { - tasks[taskCount] = task; - taskCount += 1; + tasks.add(task); System.out.println("____________________________________________________________"); System.out.println(" Got it. I've added this task:"); - System.out.println(" " + tasks[taskCount - 1]); - System.out.println(" Now you have " + taskCount + " tasks in the list."); + System.out.println(" " + task); + System.out.println(" Now you have " + tasks.size() + " tasks in the list."); + System.out.println("____________________________________________________________"); + } + + private static void deleteTask(int index) { + System.out.println("____________________________________________________________"); + System.out.println("Noted. I've removed this task:"); + System.out.println(" " + tasks.get(index - 1)); + tasks.remove(tasks.get(index - 1)); + System.out.println(" Now you have " + tasks.size() + " tasks in the list."); System.out.println("____________________________________________________________"); } private static void list() { System.out.println("____________________________________________________________"); - if (taskCount == 0) { + if (tasks.isEmpty()) { System.out.println(" List is empty!"); } else { System.out.println(" Here are the tasks in your list:"); - for(int i = 0; i < taskCount; i += 1) { - System.out.println(" " + (i + 1) + ". " + tasks[i]); + for(int i = 0; i < tasks.size(); i += 1) { + System.out.println(" " + (i + 1) + ". " + tasks.get(i)); } } System.out.println("____________________________________________________________"); @@ -59,6 +68,12 @@ private static void repeat(String input) { System.out.println("____________________________________________________________"); } + private static void reportUnknownCommand(String input) { + System.out.println("____________________________________________________________"); + System.out.println(" <" + input + "> is an unknown command. Please Refer to the user guide." ); + System.out.println("____________________________________________________________"); + } + private static void greet() { System.out.println("____________________________________________________________"); System.out.println(" Hello! I'm Flaaaash"); @@ -101,13 +116,22 @@ public static void main(String[] args) { break; case "unmark": - try { - unmarkTask(Integer.parseInt(inputParts[1])); + try { + unmarkTask(Integer.parseInt(inputParts[1])); + break; + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println("Invalid format! Use: unmark "); + } + break; + + case "delete": + try { + deleteTask(Integer.parseInt(inputParts[1])); + break; + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println("Invalid format! Use: delete "); + } break; - } catch (ArrayIndexOutOfBoundsException e) { - System.out.println("Invalid format! Use: unmark "); - } - break; case "todo": if (inputParts.length < 2) { @@ -144,7 +168,7 @@ public static void main(String[] args) { break; default: - repeat(command); + reportUnknownCommand(command); } } } From fc1b49accf378af34bd58c290955f2d0a168666a Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 21 Feb 2025 15:56:24 +0800 Subject: [PATCH 06/19] Add save and load methods. --- src/main/java/Deadline.java | 13 +++-- src/main/java/Event.java | 5 ++ src/main/java/Flaaaash.java | 76 +++++++++++++++++++++++++++++- src/main/java/META-INF/MANIFEST.MF | 3 ++ src/main/java/Task.java | 4 +- src/main/java/Todo.java | 5 ++ 6 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 src/main/java/META-INF/MANIFEST.MF diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 2e4e51a78..3189930dc 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -1,13 +1,18 @@ public class Deadline extends Task { - protected String deadLine; + protected String deadline; - public Deadline(String taskName, String deadLine) { + public Deadline(String taskName, String deadline) { super(taskName); - this.deadLine = deadLine; + this.deadline = deadline; } @Override public String toString() { - return "[D]" + super.toString() + " (by: " + deadLine + ")"; + return "[D]" + super.toString() + " (by: " + deadline + ")"; + } + + @Override + public String toFileString() { + return "D / " + (isDone ? "1" : "0") + " / " + taskName + " / " + deadline; } } diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 3080cc867..e3dc94b5e 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -12,4 +12,9 @@ public Event(String taskName, String from, String to) { public String toString() { return "[E]" + super.toString() + " (from: " + from + " to: " + to + ")"; } + + @Override + public String toFileString() { + return "E / " + (isDone ? "1" : "0") + " / " + taskName + " / " + from + " / " + to; + } } diff --git a/src/main/java/Flaaaash.java b/src/main/java/Flaaaash.java index 05b23b7e2..140a6b997 100644 --- a/src/main/java/Flaaaash.java +++ b/src/main/java/Flaaaash.java @@ -1,9 +1,81 @@ +import java.util.Arrays; import java.util.Scanner; +import java.io.*; public class Flaaaash { private static final int maxCount = 100; private static final Task[] tasks = new Task[maxCount]; private static int taskCount = 0; + private static final String FILE_PATH = System.getProperty("user.home") + File.separator + "Desktop" + File.separator + "Flaaaash.txt"; + + private static void saveToFile() { + try { + File file = new File(FILE_PATH); + BufferedWriter writer = new BufferedWriter(new FileWriter(file)); + + for(int i = 0; i < taskCount; i += 1) { + writer.write(tasks[i].toFileString()); + writer.newLine(); + } + + writer.close(); + } catch (IOException e) { + System.out.println("Error saving tasks: " + e.getMessage()); + } + } + + private static void loadFromFile() { + File file = new File(FILE_PATH); + if (!file.exists()) { + return; + } + + try { + Scanner fileScanner = new Scanner(file); + while (fileScanner.hasNextLine()) { + String line = fileScanner.nextLine(); + String[] parts = line.split(" / "); + + try { + String type = parts[0]; + boolean isDone = parts[1].equals("1"); + String description = parts[2]; + + switch (type) { + case "T": + Todo todo = new Todo(description); + addTask(todo); + if (isDone) { + todo.markAsDone(); + } + break; + case "D": + Deadline deadline = new Deadline(description, parts[3]); + addTask(deadline); + if (isDone) { + deadline.markAsDone(); + } + break; + case "E": + Event event = new Event(description, parts[3], parts[4]); + addTask(event); + if (isDone) { + event.markAsDone(); + } + break; + default: + throw new IllegalArgumentException("Invalid task type"); + } + + } catch (Exception e) { + System.out.println("Skipping corrupted entry: " + line); + } + } + fileScanner.close(); + } catch (FileNotFoundException e) { + System.out.println("File not found, starting fresh."); + } + } private static void markTask(int index) { System.out.println("____________________________________________________________"); @@ -46,7 +118,7 @@ private static void list() { System.out.println(" List is empty!"); } else { System.out.println(" Here are the tasks in your list:"); - for(int i = 0; i < taskCount; i += 1) { + for (int i = 0; i < taskCount; i += 1) { System.out.println(" " + (i + 1) + ". " + tasks[i]); } } @@ -67,12 +139,14 @@ private static void greet() { } private static void exit() { + saveToFile(); System.out.println("____________________________________________________________"); System.out.println(" Bye. Hope to see you again soon!"); System.out.println("____________________________________________________________"); } public static void main(String[] args) { + loadFromFile(); greet(); Scanner scanner = new Scanner(System.in); diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..9a6599a45 --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Flaaaash + diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 4eb668738..146affba7 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -1,4 +1,4 @@ -public class Task { +public abstract class Task { protected String taskName; protected boolean isDone; @@ -23,4 +23,6 @@ public void unmarkAsDone() { public String toString() { return "[" + getStatusIcon() + "] " + taskName; } + + public abstract String toFileString(); } diff --git a/src/main/java/Todo.java b/src/main/java/Todo.java index baff9a0be..a9fe9edfc 100644 --- a/src/main/java/Todo.java +++ b/src/main/java/Todo.java @@ -7,4 +7,9 @@ public Todo(String taskName) { public String toString() { return "[T]" + super.toString(); } + + @Override + public String toFileString() { + return "T / " + (isDone ? "1" : "0") + " / " + taskName; + } } From 49f4871488529c17cd5fd4dec48a18c474c18b2d Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 21 Feb 2025 16:01:23 +0800 Subject: [PATCH 07/19] Level-7 --- src/main/java/Flaaaash.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/Flaaaash.java b/src/main/java/Flaaaash.java index 140a6b997..2bd05bf5d 100644 --- a/src/main/java/Flaaaash.java +++ b/src/main/java/Flaaaash.java @@ -44,21 +44,21 @@ private static void loadFromFile() { switch (type) { case "T": Todo todo = new Todo(description); - addTask(todo); + addTaskNoText(todo); if (isDone) { todo.markAsDone(); } break; case "D": Deadline deadline = new Deadline(description, parts[3]); - addTask(deadline); + addTaskNoText(deadline); if (isDone) { deadline.markAsDone(); } break; case "E": Event event = new Event(description, parts[3], parts[4]); - addTask(event); + addTaskNoText(event); if (isDone) { event.markAsDone(); } @@ -112,6 +112,11 @@ private static void addTask(Task task) { System.out.println("____________________________________________________________"); } + private static void addTaskNoText(Task task) { + tasks[taskCount] = task; + taskCount += 1; + } + private static void list() { System.out.println("____________________________________________________________"); if (taskCount == 0) { From a66933156bb798e6eaec2bcd5dc2caf34781a82f Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 21 Feb 2025 16:17:46 +0800 Subject: [PATCH 08/19] Fixed a naming conflict. --- src/main/java/Flaaaash.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/Flaaaash.java b/src/main/java/Flaaaash.java index 0aef775c0..236fb4853 100644 --- a/src/main/java/Flaaaash.java +++ b/src/main/java/Flaaaash.java @@ -119,8 +119,7 @@ private static void deleteTask(int index) { } private static void addTaskNoText(Task task) { - tasks[taskCount] = task; - taskCount += 1; + tasks.add(task); } private static void list() { From 790d185174b6539584c7d371c7ae82bc51ac7576 Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 21 Feb 2025 16:30:44 +0800 Subject: [PATCH 09/19] A-Packages --- src/main/java/Flaaaash.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/Flaaaash.java b/src/main/java/Flaaaash.java index 236fb4853..bc5aada78 100644 --- a/src/main/java/Flaaaash.java +++ b/src/main/java/Flaaaash.java @@ -15,8 +15,8 @@ private static void saveToFile() { writer.write(task.toFileString()); writer.newLine(); } - writer.close(); + } catch (IOException e) { System.out.println("Error saving tasks: " + e.getMessage()); } @@ -69,6 +69,7 @@ private static void loadFromFile() { System.out.println("Skipping corrupted entry: " + line); } } + fileScanner.close(); } catch (FileNotFoundException e) { System.out.println("File not found, starting fresh."); @@ -126,6 +127,7 @@ private static void list() { System.out.println("____________________________________________________________"); if (tasks.isEmpty()) { System.out.println(" List is empty!"); + System.out.println("____________________________________________________________"); } else { System.out.println(" Here are the tasks in your list:"); for (int i = 0; i < tasks.size(); i += 1) { From 237eb841c2f9fe4c314e34b956b6a390ec37ac2b Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 14 Mar 2025 06:51:56 +0800 Subject: [PATCH 10/19] Apply a more OOP design Create package for command, ui, parser, data, common, storage and exception. --- src/main/java/Flaaaash.java | 279 +++--------------- src/main/java/commands/Command.java | 19 ++ src/main/java/commands/DeadlineCommand.java | 29 ++ src/main/java/commands/DeleteCommand.java | 24 ++ src/main/java/commands/EventCommand.java | 28 ++ src/main/java/commands/ExitCommand.java | 19 ++ src/main/java/commands/HelpCommand.java | 29 ++ src/main/java/commands/InvalidCommand.java | 16 + src/main/java/commands/ListCommand.java | 26 ++ src/main/java/commands/MarkCommand.java | 21 ++ src/main/java/commands/TodoCommand.java | 27 ++ src/main/java/commands/UnmarkCommand.java | 21 ++ .../java/commands/WrongFormatCommand.java | 15 + src/main/java/common/Messages.java | 9 + src/main/java/data/TaskList.java | 44 +++ src/main/java/{ => data/task}/Deadline.java | 2 + src/main/java/{ => data/task}/Event.java | 2 + src/main/java/{ => data/task}/Task.java | 2 + src/main/java/{ => data/task}/Todo.java | 2 + .../exceptions/EmptyArgumentException.java | 7 + .../exceptions/IllegalValueException.java | 13 + src/main/java/parser/Parser.java | 93 ++++++ src/main/java/storage/StorageFile.java | 60 ++++ src/main/java/storage/TaskDecoder.java | 68 +++++ src/main/java/storage/TaskEncoder.java | 14 + src/main/java/ui/TextUi.java | 58 ++++ 26 files changed, 695 insertions(+), 232 deletions(-) create mode 100644 src/main/java/commands/Command.java create mode 100644 src/main/java/commands/DeadlineCommand.java create mode 100644 src/main/java/commands/DeleteCommand.java create mode 100644 src/main/java/commands/EventCommand.java create mode 100644 src/main/java/commands/ExitCommand.java create mode 100644 src/main/java/commands/HelpCommand.java create mode 100644 src/main/java/commands/InvalidCommand.java create mode 100644 src/main/java/commands/ListCommand.java create mode 100644 src/main/java/commands/MarkCommand.java create mode 100644 src/main/java/commands/TodoCommand.java create mode 100644 src/main/java/commands/UnmarkCommand.java create mode 100644 src/main/java/commands/WrongFormatCommand.java create mode 100644 src/main/java/common/Messages.java create mode 100644 src/main/java/data/TaskList.java rename src/main/java/{ => data/task}/Deadline.java (95%) rename src/main/java/{ => data/task}/Event.java (96%) rename src/main/java/{ => data/task}/Task.java (96%) rename src/main/java/{ => data/task}/Todo.java (94%) create mode 100644 src/main/java/exceptions/EmptyArgumentException.java create mode 100644 src/main/java/exceptions/IllegalValueException.java create mode 100644 src/main/java/parser/Parser.java create mode 100644 src/main/java/storage/StorageFile.java create mode 100644 src/main/java/storage/TaskDecoder.java create mode 100644 src/main/java/storage/TaskEncoder.java create mode 100644 src/main/java/ui/TextUi.java diff --git a/src/main/java/Flaaaash.java b/src/main/java/Flaaaash.java index bc5aada78..7fe5eaacb 100644 --- a/src/main/java/Flaaaash.java +++ b/src/main/java/Flaaaash.java @@ -1,252 +1,67 @@ -import java.io.*; -import java.util.ArrayList; -import java.util.Scanner; +import commands.Command; +import commands.ExitCommand; +import data.TaskList; +import parser.Parser; +import storage.StorageFile; +import storage.StorageFile.StorageOperationException; +import ui.TextUi; -public class Flaaaash { - private static final ArrayList tasks = new ArrayList<>(); - private static final String FILE_PATH = System.getProperty("user.home") + File.separator + "Desktop" + File.separator + "Flaaaash.txt"; - - private static void saveToFile() { - try { - File file = new File(FILE_PATH); - BufferedWriter writer = new BufferedWriter(new FileWriter(file)); - - for (Task task : tasks) { - writer.write(task.toFileString()); - writer.newLine(); - } - writer.close(); - - } catch (IOException e) { - System.out.println("Error saving tasks: " + e.getMessage()); - } - } - - private static void loadFromFile() { - File file = new File(FILE_PATH); - if (!file.exists()) { - return; - } - - try { - Scanner fileScanner = new Scanner(file); - while (fileScanner.hasNextLine()) { - String line = fileScanner.nextLine(); - String[] parts = line.split(" / "); - try { - String type = parts[0]; - boolean isDone = parts[1].equals("1"); - String description = parts[2]; +import java.io.File; - switch (type) { - case "T": - Todo todo = new Todo(description); - addTaskNoText(todo); - if (isDone) { - todo.markAsDone(); - } - break; - case "D": - Deadline deadline = new Deadline(description, parts[3]); - addTaskNoText(deadline); - if (isDone) { - deadline.markAsDone(); - } - break; - case "E": - Event event = new Event(description, parts[3], parts[4]); - addTaskNoText(event); - if (isDone) { - event.markAsDone(); - } - break; - default: - throw new IllegalArgumentException("Invalid task type"); - } - } catch (Exception e) { - System.out.println("Skipping corrupted entry: " + line); - } - } - - fileScanner.close(); - } catch (FileNotFoundException e) { - System.out.println("File not found, starting fresh."); - } - } - - private static void markTask(int index) { - System.out.println("____________________________________________________________"); - if (index < 0 || index > tasks.size()) { - System.out.println(" Invalid index"); - } else { - System.out.println(" Nice! I've marked this task as done:"); - tasks.get(index - 1).markAsDone(); - System.out.println(" " + tasks.get(index - 1)); - } - System.out.println("____________________________________________________________"); - } +public class Flaaaash { - private static void unmarkTask(int index) { - System.out.println("____________________________________________________________"); - if (index < 0 || index > tasks.size()) { - System.out.println(" Invalid index"); - } else { - System.out.println(" OK, I've marked this task as not done yet:"); - tasks.get(index - 1).unmarkAsDone(); - System.out.println(" " + tasks.get(index - 1)); - } - System.out.println("____________________________________________________________"); - } + public final String VERSION = "Chatbot Flaaaash Version-2.0"; - private static void addTask(Task task) { - tasks.add(task); + private TextUi ui; + private TaskList taskList; + private StorageFile storage; - System.out.println("____________________________________________________________"); - System.out.println(" Got it. I've added this task:"); - System.out.println(" " + task); - System.out.println(" Now you have " + tasks.size() + " tasks in the list."); - System.out.println("____________________________________________________________"); - } + public static void main(String[] args) { new Flaaaash().run(); } - private static void deleteTask(int index) { - System.out.println("____________________________________________________________"); - System.out.println("Noted. I've removed this task:"); - System.out.println(" " + tasks.get(index - 1)); - tasks.remove(tasks.get(index - 1)); - System.out.println(" Now you have " + tasks.size() + " tasks in the list."); - System.out.println("____________________________________________________________"); + public void run() { + start(); + runCommandLoopUntilExitCommand(); + exit(); } - private static void addTaskNoText(Task task) { - tasks.add(task); - } - - private static void list() { - System.out.println("____________________________________________________________"); - if (tasks.isEmpty()) { - System.out.println(" List is empty!"); - System.out.println("____________________________________________________________"); - } else { - System.out.println(" Here are the tasks in your list:"); - for (int i = 0; i < tasks.size(); i += 1) { - System.out.println(" " + (i + 1) + ". " + tasks.get(i)); - } - System.out.println("____________________________________________________________"); + public void start() { + try { + this.ui = new TextUi(); + this.storage = new StorageFile(); + this.taskList = storage.load(); + ui.showWelcomeMessage(VERSION); + } catch (StorageOperationException e) { + ui.showInitFailedMessage(); + throw new RuntimeException(e); } } - private static void repeat(String input) { - System.out.println("____________________________________________________________"); - System.out.println(" " + input); - System.out.println("____________________________________________________________"); - } - - private static void reportUnknownCommand(String input) { - System.out.println("____________________________________________________________"); - System.out.println(" <" + input + "> is an unknown command. Please Refer to the user guide."); - System.out.println("____________________________________________________________"); + public void runCommandLoopUntilExitCommand() { + Command command; + do { + String userCommandText = ui.getUserCommand(); + command = new Parser().parseCommand(userCommandText); + String result = executeCommand(command); + ui.showToUser(result); + } while (!ExitCommand.isExit(command)); } - private static void greet() { - System.out.println("____________________________________________________________"); - System.out.println(" Hello! I'm Flaaaash"); - System.out.println(" What can I do for you?"); - System.out.println("____________________________________________________________"); + private void exit() { + ui.showGoodbyeMessage(); + System.exit(0); } - private static void exit() { - saveToFile(); - System.out.println("____________________________________________________________"); - System.out.println(" Bye. Hope to see you again soon!"); - System.out.println("____________________________________________________________"); - } - - public static void main(String[] args) { - loadFromFile(); - greet(); - Scanner scanner = new Scanner(System.in); - - while (true) { - String input = scanner.nextLine().trim(); - String[] inputParts = input.split(" ", 2); - String command = inputParts[0].toLowerCase(); - - switch (command) { - case "bye": - exit(); - scanner.close(); - return; - - case "list": - list(); - break; - - case "mark": - try { - markTask(Integer.parseInt(inputParts[1])); - break; - } catch (ArrayIndexOutOfBoundsException e) { - System.out.println("Invalid format! Use: mark "); - } - break; - - case "unmark": - try { - unmarkTask(Integer.parseInt(inputParts[1])); - break; - } catch (ArrayIndexOutOfBoundsException e) { - System.out.println("Invalid format! Use: unmark "); - } - break; - - case "delete": - try { - deleteTask(Integer.parseInt(inputParts[1])); - break; - } catch (ArrayIndexOutOfBoundsException e) { - System.out.println("Invalid format! Use: delete "); - } - break; - - case "todo": - if (inputParts.length < 2) { - System.out.println("Task description cannot be empty."); - } else { - addTask(new Todo(inputParts[1])); - } - break; - - case "deadline": - try { - String[] deadlineInputDetails = inputParts[1].split(" /by ", 2); - String deadlineName = deadlineInputDetails[0]; - String deadLine = deadlineInputDetails[1]; - addTask(new Deadline(deadlineName, deadLine)); - break; - } catch (ArrayIndexOutOfBoundsException e) { - System.out.println("Invalid format! Use: deadline /by "); - } - break; - - - case "event": - try { - String[] eventInputDetails = inputParts[1].split(" /from | /to ", 3); - String eventName = eventInputDetails[0]; - String from = eventInputDetails[1]; - String to = eventInputDetails[2]; - addTask(new Event(eventName, from, to)); - break; - } catch (ArrayIndexOutOfBoundsException e) { - System.out.println("Invalid format! Use: event /from /to "); - } - break; - - default: - reportUnknownCommand(command); - } + public String executeCommand(Command command) { + try { + command.setTaskList(taskList); + String result = command.execute(); + storage.save(taskList); + return result; + } catch (Exception e) { + ui.showToUser(e.getMessage()); + throw new RuntimeException(e); } } } diff --git a/src/main/java/commands/Command.java b/src/main/java/commands/Command.java new file mode 100644 index 000000000..d4a7c13aa --- /dev/null +++ b/src/main/java/commands/Command.java @@ -0,0 +1,19 @@ +package commands; + +import data.TaskList; + +public class Command { + protected TaskList taskList; + + /** + * Executes the command and returns the result. + */ + public String execute() { + throw new UnsupportedOperationException("This method is to be implemented by child classes"); + }; + + public void setTaskList(TaskList taskList) { + this.taskList = taskList; + } + +} diff --git a/src/main/java/commands/DeadlineCommand.java b/src/main/java/commands/DeadlineCommand.java new file mode 100644 index 000000000..f185b709a --- /dev/null +++ b/src/main/java/commands/DeadlineCommand.java @@ -0,0 +1,29 @@ +package commands; + +import data.task.Deadline; + + +public class DeadlineCommand extends Command { + + public static final String COMMAND_WORD = "deadline"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a deadline task to the task list.\n" + + "Parameters: TASK_NAME /by DEADLINE\n" + + " -Example: " + COMMAND_WORD + + " Return a book /by Saturday"; + + private final Deadline deadline; + + public DeadlineCommand(String taskName, String deadline) { + this.deadline = new Deadline(taskName, deadline); + } + + public String execute() { + taskList.addTask(deadline); + StringBuilder sb = new StringBuilder(); + sb.append("Got it. I've added this task:").append("\n"); + sb.append(" ").append(deadline.toString()).append("\n"); + sb.append("Now you have ").append(taskList.size()).append(" tasks in the list."); + return sb.toString(); + } +} diff --git a/src/main/java/commands/DeleteCommand.java b/src/main/java/commands/DeleteCommand.java new file mode 100644 index 000000000..4de35972c --- /dev/null +++ b/src/main/java/commands/DeleteCommand.java @@ -0,0 +1,24 @@ +package commands; + +public class DeleteCommand extends Command { + + public static final String COMMAND_WORD = "delete"; + + private final int taskIndex; + + public DeleteCommand(int taskIndex) { + this.taskIndex = taskIndex; + } + + public String execute() { + if (taskIndex < 1 || taskIndex > taskList.size()) { + return String.format("Index %d is invalid", taskIndex); + } + StringBuilder sb = new StringBuilder(); + sb.append("Noted. I've removed this tasks:").append("\n") + .append(" ").append(taskList.get(taskIndex - 1).toString()).append("\n"); + taskList.deleteTask(taskIndex - 1); + sb.append("Now you have ").append(taskList.size()).append(" tasks in the list."); + return sb.toString(); + } +} diff --git a/src/main/java/commands/EventCommand.java b/src/main/java/commands/EventCommand.java new file mode 100644 index 000000000..e4c96057d --- /dev/null +++ b/src/main/java/commands/EventCommand.java @@ -0,0 +1,28 @@ +package commands; + +import data.task.Event; + +public class EventCommand extends Command { + + public static final String COMMAND_WORD = "event"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a event task to the task list.\n" + + "Parameters: TASK_NAME /from EVENT_START_TIME /to EVENT_END_TIME\n" + + " -Example: " + COMMAND_WORD + + " Final exam /from 29 April 2025 2:00pm /to 4:00pm"; + + private final Event event; + + public EventCommand(String taskName, String from, String to) { + this.event = new Event(taskName, from, to); + } + + public String execute() { + taskList.addTask(event); + StringBuilder sb = new StringBuilder(); + sb.append("Got it. I've added this task:").append("\n"); + sb.append(" ").append(event.toString()).append("\n"); + sb.append("Now you have ").append(taskList.size()).append(" tasks in the list."); + return sb.toString(); + } +} diff --git a/src/main/java/commands/ExitCommand.java b/src/main/java/commands/ExitCommand.java new file mode 100644 index 000000000..0e3ea19c7 --- /dev/null +++ b/src/main/java/commands/ExitCommand.java @@ -0,0 +1,19 @@ +package commands; + +public class ExitCommand extends Command { + + public static final String COMMAND_WORD = "exit"; + + public static final String MESSAGE_EXIT = "Exiting program as requested ..."; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Exits the program."; + + @Override + public String execute() { + return MESSAGE_EXIT; + } + + public static boolean isExit(Command command) { + return command instanceof ExitCommand; // instanceof returns false if it is null + } +} diff --git a/src/main/java/commands/HelpCommand.java b/src/main/java/commands/HelpCommand.java new file mode 100644 index 000000000..bfd4fcb22 --- /dev/null +++ b/src/main/java/commands/HelpCommand.java @@ -0,0 +1,29 @@ +package commands; + +/** + * Shows help instructions. + */ +public class HelpCommand extends Command { + + public static final String COMMAND_WORD = "help"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions."; + + @Override + public String execute() { + return ExitCommand.MESSAGE_USAGE + + "\n" + HelpCommand.MESSAGE_USAGE + + "\n" + ListCommand.MESSAGE_USAGE + + "\n" + TodoCommand.MESSAGE_USAGE; + } +// return AddCommand.MESSAGE_USAGE +// + "\n" + DeleteCommand.MESSAGE_USAGE +// + "\n" + ClearCommand.MESSAGE_USAGE +// + "\n" + FindCommand.MESSAGE_USAGE +// + "\n" + ListCommand.MESSAGE_USAGE +// + "\n" + ViewCommand.MESSAGE_USAGE +// + "\n" + ViewAllCommand.MESSAGE_USAGE +// + "\n" + HelpCommand.MESSAGE_USAGE +// + "\n" + ExitCommand.MESSAGE_USAGE + +} diff --git a/src/main/java/commands/InvalidCommand.java b/src/main/java/commands/InvalidCommand.java new file mode 100644 index 000000000..bdd96c60a --- /dev/null +++ b/src/main/java/commands/InvalidCommand.java @@ -0,0 +1,16 @@ +package commands; + +public class InvalidCommand extends Command { + + private final String invalidCommand; + + public static final String MESSAGE_INVALID_COMMAND = "<%1$s> is an unknown command. Please enter for the list of commands."; + + public InvalidCommand(String invalidCommand) { + this.invalidCommand = invalidCommand; + } + + public String execute() { + return String.format(MESSAGE_INVALID_COMMAND, invalidCommand); + } +} diff --git a/src/main/java/commands/ListCommand.java b/src/main/java/commands/ListCommand.java new file mode 100644 index 000000000..461c72ee6 --- /dev/null +++ b/src/main/java/commands/ListCommand.java @@ -0,0 +1,26 @@ +package commands; + +public class ListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Displays all tasks as a list with index numbers."; + + /** Offset required to convert between 1-indexing and 0-indexing. */ + public static final int DISPLAYED_INDEX_OFFSET = 1; + + public String execute() { + if (taskList.isEmpty()) { + return "You don't have any task yet."; + } + StringBuilder sb = new StringBuilder(); + sb.append("Here are the tasks in your list:").append("\n"); + int size = taskList.size(); + for (int i = 0; i < size - DISPLAYED_INDEX_OFFSET; i += 1) { + sb.append((i + DISPLAYED_INDEX_OFFSET)).append(". ").append(taskList.get(i).toString()).append("\n"); + } + sb.append(size).append(". ").append(taskList.get(size - 1).toString()); + return sb.toString(); + } +} diff --git a/src/main/java/commands/MarkCommand.java b/src/main/java/commands/MarkCommand.java new file mode 100644 index 000000000..5f902dd3c --- /dev/null +++ b/src/main/java/commands/MarkCommand.java @@ -0,0 +1,21 @@ +package commands; + +public class MarkCommand extends Command { + + public static final String COMMAND_WORD = "mark"; + + public int taskIndex; + + public MarkCommand(int taskIndex) { + this.taskIndex = taskIndex; + } + + @Override + public String execute() { + if (taskIndex < 1 || taskIndex > taskList.size()) { + return String.format("Index %d is invalid", taskIndex); + } + taskList.get(taskIndex - 1).markAsDone(); + return "Nice! I've marked this task as done:" + "\n" + " " + taskList.get(taskIndex - 1); + } +} diff --git a/src/main/java/commands/TodoCommand.java b/src/main/java/commands/TodoCommand.java new file mode 100644 index 000000000..3f712e23c --- /dev/null +++ b/src/main/java/commands/TodoCommand.java @@ -0,0 +1,27 @@ +package commands; + +import data.task.Todo; + +public class TodoCommand extends Command { + + public static final String COMMAND_WORD = "todo"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a todo task to the task list.\n" + + " -Example: " + COMMAND_WORD + + " Read a book"; + + private final Todo todo; + + public TodoCommand(String taskName) { + this.todo = new Todo(taskName); + } + + public String execute() { + taskList.addTask(todo); + StringBuilder sb = new StringBuilder(); + sb.append("Got it. I've added this task:").append("\n"); + sb.append(" ").append(todo.toString()).append("\n"); + sb.append("Now you have ").append(taskList.size()).append(" tasks in the list."); + return sb.toString(); + } +} diff --git a/src/main/java/commands/UnmarkCommand.java b/src/main/java/commands/UnmarkCommand.java new file mode 100644 index 000000000..37031ea24 --- /dev/null +++ b/src/main/java/commands/UnmarkCommand.java @@ -0,0 +1,21 @@ +package commands; + +public class UnmarkCommand extends Command { + + public static final String COMMAND_WORD = "unmark"; + + public int taskIndex; + + public UnmarkCommand(int taskIndex) { + this.taskIndex = taskIndex; + } + + @Override + public String execute() { + if (taskIndex < 1 || taskIndex > taskList.size()) { + return String.format("Index %d is invalid", taskIndex); + } + taskList.get(taskIndex - 1).unmarkAsDone(); + return "OK, I've marked this task as not done yet:" + "\n" + " " + taskList.get(taskIndex - 1); + } +} \ No newline at end of file diff --git a/src/main/java/commands/WrongFormatCommand.java b/src/main/java/commands/WrongFormatCommand.java new file mode 100644 index 000000000..a9acfb1d6 --- /dev/null +++ b/src/main/java/commands/WrongFormatCommand.java @@ -0,0 +1,15 @@ +package commands; + +public class WrongFormatCommand extends Command { + + private final String feedbackToUSer; + + public WrongFormatCommand(String feedbackToUser) { + this.feedbackToUSer = feedbackToUser; + } + + @Override + public String execute() { + return feedbackToUSer; + } +} diff --git a/src/main/java/common/Messages.java b/src/main/java/common/Messages.java new file mode 100644 index 000000000..15911e1d8 --- /dev/null +++ b/src/main/java/common/Messages.java @@ -0,0 +1,9 @@ +package common; + +public class Messages { + + public static final String MESSAGE_WELCOME = "Hello! I'm Flaaaash, what can I do for you?"; + public static final String MESSAGE_GOODBYE = "Bye. Hope to see you again soon!"; + public static final String MESSAGE_INIT_FAILED = "Failed to initialise Flaaaash. Exiting..."; + +} \ No newline at end of file diff --git a/src/main/java/data/TaskList.java b/src/main/java/data/TaskList.java new file mode 100644 index 000000000..83403756b --- /dev/null +++ b/src/main/java/data/TaskList.java @@ -0,0 +1,44 @@ +package data; + +import data.task.Task; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TaskList { + + private final List tasks; + + public TaskList() { + this.tasks = new ArrayList<>(); + } + + public TaskList(List tasks) { + this.tasks = tasks; + } + + public boolean isEmpty() { + return tasks.isEmpty(); + } + + public int size() { + return tasks.size(); + } + + public Task get(int index) { + return tasks.get(index); + } + + public void addTask(Task toAdd) { + tasks.add(toAdd); + } + + public void deleteTask(int taskIndex) { + tasks.remove(taskIndex); + } + + public List getAllTasks() { + return this.tasks; + } +} diff --git a/src/main/java/Deadline.java b/src/main/java/data/task/Deadline.java similarity index 95% rename from src/main/java/Deadline.java rename to src/main/java/data/task/Deadline.java index 3189930dc..f64b45683 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/data/task/Deadline.java @@ -1,3 +1,5 @@ +package data.task; + public class Deadline extends Task { protected String deadline; diff --git a/src/main/java/Event.java b/src/main/java/data/task/Event.java similarity index 96% rename from src/main/java/Event.java rename to src/main/java/data/task/Event.java index e3dc94b5e..de06aa53e 100644 --- a/src/main/java/Event.java +++ b/src/main/java/data/task/Event.java @@ -1,3 +1,5 @@ +package data.task; + public class Event extends Task { protected String from; protected String to; diff --git a/src/main/java/Task.java b/src/main/java/data/task/Task.java similarity index 96% rename from src/main/java/Task.java rename to src/main/java/data/task/Task.java index 146affba7..8ec70b023 100644 --- a/src/main/java/Task.java +++ b/src/main/java/data/task/Task.java @@ -1,3 +1,5 @@ +package data.task; + public abstract class Task { protected String taskName; protected boolean isDone; diff --git a/src/main/java/Todo.java b/src/main/java/data/task/Todo.java similarity index 94% rename from src/main/java/Todo.java rename to src/main/java/data/task/Todo.java index a9fe9edfc..c55b031b0 100644 --- a/src/main/java/Todo.java +++ b/src/main/java/data/task/Todo.java @@ -1,3 +1,5 @@ +package data.task; + public class Todo extends Task { public Todo(String taskName) { super(taskName); diff --git a/src/main/java/exceptions/EmptyArgumentException.java b/src/main/java/exceptions/EmptyArgumentException.java new file mode 100644 index 000000000..fc2178848 --- /dev/null +++ b/src/main/java/exceptions/EmptyArgumentException.java @@ -0,0 +1,7 @@ +package exceptions; + +public class EmptyArgumentException extends ArrayIndexOutOfBoundsException { + public EmptyArgumentException(String message) { + super(message); + } +} diff --git a/src/main/java/exceptions/IllegalValueException.java b/src/main/java/exceptions/IllegalValueException.java new file mode 100644 index 000000000..44a4838ca --- /dev/null +++ b/src/main/java/exceptions/IllegalValueException.java @@ -0,0 +1,13 @@ +package exceptions; + +/** + * Signals that some given data does not fulfill some constraints. + */ +public class IllegalValueException extends Exception { + /** + * @param message should contain relevant information on the failed constraint(s) + */ + public IllegalValueException(String message) { + super(message); + } +} diff --git a/src/main/java/parser/Parser.java b/src/main/java/parser/Parser.java new file mode 100644 index 000000000..419033664 --- /dev/null +++ b/src/main/java/parser/Parser.java @@ -0,0 +1,93 @@ +package parser; + +import commands.Command; +import commands.DeadlineCommand; +import commands.DeleteCommand; +import commands.EventCommand; +import commands.ExitCommand; +import commands.HelpCommand; +import commands.InvalidCommand; +import commands.ListCommand; +import commands.MarkCommand; +import commands.TodoCommand; +import commands.UnmarkCommand; +import commands.WrongFormatCommand; + + +public class Parser { + + public Command parseCommand(String userInput) { + String[] inputParts = userInput.split(" ", 2); + String commandWord = inputParts[0].toLowerCase(); + + switch (commandWord) { + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); + + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + + case ListCommand.COMMAND_WORD: + return new ListCommand(); + + case TodoCommand.COMMAND_WORD: + try { + return new TodoCommand(inputParts[1]); + } catch (ArrayIndexOutOfBoundsException e) { + return new WrongFormatCommand("Task description cannot be empty."); + } + + case DeadlineCommand.COMMAND_WORD: + try { + String[] deadlineInputDetails = inputParts[1].split(" /by ", 2); + String taskName = deadlineInputDetails[0]; + String deadline = deadlineInputDetails[1]; + return new DeadlineCommand(taskName, deadline); + } catch (ArrayIndexOutOfBoundsException e) { + return new WrongFormatCommand("Invalid format! " + + "Use: deadline /by "); + } + + case EventCommand.COMMAND_WORD: + try { + String[] eventInputDetails = inputParts[1].split(" /from | /to ", 3); + String taskName = eventInputDetails[0]; + String from = eventInputDetails[1]; + String to = eventInputDetails[2]; + return new EventCommand(taskName, from, to); + } catch (ArrayIndexOutOfBoundsException e) { + return new WrongFormatCommand("Invalid format! " + + "Use: event /from /to "); + } + + case MarkCommand.COMMAND_WORD: + try { + int taskIndex = Integer.parseInt(inputParts[1]); + return new MarkCommand(taskIndex); + } catch (ArrayIndexOutOfBoundsException e) { + return new WrongFormatCommand("Invalid format! Use: mark "); + } + + case UnmarkCommand.COMMAND_WORD: + try { + int taskIndex = Integer.parseInt(inputParts[1]); + return new UnmarkCommand(taskIndex); + } catch (ArrayIndexOutOfBoundsException e) { + return new WrongFormatCommand("Invalid format! Use: unmark "); + } + + case DeleteCommand.COMMAND_WORD: + try { + int taskIndex = Integer.parseInt(inputParts[1]); + return new DeleteCommand(taskIndex); + } catch (ArrayIndexOutOfBoundsException e) { + return new WrongFormatCommand("Invalid format! Use: delete "); + } + + default: + return new InvalidCommand(commandWord); + } + + } +} diff --git a/src/main/java/storage/StorageFile.java b/src/main/java/storage/StorageFile.java new file mode 100644 index 000000000..9e4c7f96c --- /dev/null +++ b/src/main/java/storage/StorageFile.java @@ -0,0 +1,60 @@ +package storage; + +import data.TaskList; +import exceptions.IllegalValueException; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +public class StorageFile { + + public static final String DEFAULT_STORAGE_FILEPATH = System.getProperty("user.home") + + File.separator + "Desktop" + File.separator + "Flaaaash.txt"; + + public final Path path; + + public StorageFile() { + this.path = Paths.get(DEFAULT_STORAGE_FILEPATH); + } + + public void save(TaskList taskList) throws StorageOperationException { + try { + List encodedTaskList = TaskEncoder.encodeTaskList(taskList); + Files.write(path, encodedTaskList); + } catch (IOException ioe) { + throw new StorageOperationException("Error writing to file: " + path); + } + } + + public TaskList load() throws StorageOperationException { + if (!Files.exists(path) || !Files.isRegularFile(path)) { + return new TaskList(); + } + + try { + return TaskDecoder.decodeTaskList(Files.readAllLines(path)); + } catch (FileNotFoundException fnfe) { + throw new AssertionError("A non-existent file scenario is already handled earlier."); + // other errors + } catch (IOException ioe) { + throw new StorageOperationException("Error writing to file: " + path); + } catch (IllegalValueException ive) { + throw new StorageOperationException("File contains illegal data values; data type constraints not met"); + } + } + + /** + * Signals that some error has occured while trying to convert and read/write data between the application + * and the storage file. + */ + public static class StorageOperationException extends Exception { + public StorageOperationException(String message) { + super(message); + } + } +} diff --git a/src/main/java/storage/TaskDecoder.java b/src/main/java/storage/TaskDecoder.java new file mode 100644 index 000000000..83d6f3086 --- /dev/null +++ b/src/main/java/storage/TaskDecoder.java @@ -0,0 +1,68 @@ +package storage; + +import data.TaskList; +import data.task.Deadline; +import data.task.Event; +import data.task.Task; +import data.task.Todo; +import exceptions.IllegalValueException; +import storage.StorageFile.StorageOperationException; + +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class TaskDecoder { + + public static final Pattern TODO_DATA_FORMAT = Pattern.compile("T / [01] / .+"); + + public static TaskList decodeTaskList(List encodedTasks) + throws IllegalValueException, StorageOperationException { + final List decodedTasks = new ArrayList<>(); + for (String encodedTask : encodedTasks) { + decodedTasks.add(decodeTaskFromString(encodedTask)); + } + return new TaskList(decodedTasks); + } + + private static Task decodeTaskFromString(String encodedTask) + throws IllegalValueException, StorageOperationException { + String[] encodedParts = encodedTask.split(" / "); + try { + String type = encodedParts[0]; + boolean isDone = encodedParts[1].equals("1"); + String description = encodedParts[2]; + + switch (type) { + case "T": + Todo todo = new Todo(description); + if (isDone) { + todo.markAsDone(); + } + return todo; + + case "D": + Deadline deadline = new Deadline(description, encodedParts[3]); + if (isDone) { + deadline.markAsDone(); + } + return deadline; + + case "E": + Event event = new Event(description, encodedParts[3], encodedParts[4]); + if (isDone) { + event.markAsDone(); + } + return event; + + default: + throw new IllegalValueException("Invalid task type"); + } + } catch (Exception e) { + throw new StorageOperationException("Encoded task in invalid format. Unable to decode."); + } + } +} + diff --git a/src/main/java/storage/TaskEncoder.java b/src/main/java/storage/TaskEncoder.java new file mode 100644 index 000000000..63edd2ae7 --- /dev/null +++ b/src/main/java/storage/TaskEncoder.java @@ -0,0 +1,14 @@ +package storage; + +import data.TaskList; + +import java.util.ArrayList; +import java.util.List; + +public class TaskEncoder { + public static List encodeTaskList(TaskList toSave) { + final List encodedTasks = new ArrayList<>(); + toSave.getAllTasks().forEach(task -> encodedTasks.add(task.toFileString())); + return encodedTasks; + } +} diff --git a/src/main/java/ui/TextUi.java b/src/main/java/ui/TextUi.java new file mode 100644 index 000000000..3d9bd63d4 --- /dev/null +++ b/src/main/java/ui/TextUi.java @@ -0,0 +1,58 @@ +package ui; + +import java.io.InputStream; +import java.io.PrintStream; +import java.util.Scanner; + +import static common.Messages.MESSAGE_GOODBYE; +import static common.Messages.MESSAGE_INIT_FAILED; +import static common.Messages.MESSAGE_WELCOME; + +public class TextUi { + private static final String DIVIDER = "____________________________________________________________"; + + /** Offset required to convert between 1-indexing and 0-indexing. */ + public static final int DISPLAYED_INDEX_OFFSET = 1; + + private final Scanner in; + private final PrintStream out; + + public TextUi() { + this(System.in, System.out); + } + + public TextUi(InputStream in, PrintStream out) { + this.in = new Scanner(in); + this.out = out; + } + + public void showWelcomeMessage(String version) { + showToUser( + DIVIDER, + version, + MESSAGE_WELCOME, + DIVIDER); + } + + public String getUserCommand() { + out.print("Enter command: "); + String fullInputLine = in.nextLine().trim(); + out.println("[Command entered: " + fullInputLine + "]"); + return fullInputLine; + } + + /** Shows message(s) to the user */ + public void showToUser(String... message) { + out.println(DIVIDER); + for (String m : message) { + out.println(m); + } + out.println(DIVIDER); + } + + public void showGoodbyeMessage() { + showToUser(MESSAGE_GOODBYE); + } + + public void showInitFailedMessage() { showToUser(MESSAGE_INIT_FAILED);} +} From 7dc40ad0c11538b07b7f7aef457677545fecf18f Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 14 Mar 2025 06:58:58 +0800 Subject: [PATCH 11/19] Completed HelpCommand class. Write MESSGAE_USAGE for all command subclasses. --- src/main/java/commands/DeleteCommand.java | 6 ++++++ src/main/java/commands/HelpCommand.java | 17 +++++++---------- src/main/java/commands/MarkCommand.java | 5 +++++ src/main/java/commands/UnmarkCommand.java | 6 ++++++ 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/main/java/commands/DeleteCommand.java b/src/main/java/commands/DeleteCommand.java index 4de35972c..7f8063157 100644 --- a/src/main/java/commands/DeleteCommand.java +++ b/src/main/java/commands/DeleteCommand.java @@ -4,6 +4,12 @@ public class DeleteCommand extends Command { public static final String COMMAND_WORD = "delete"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Delete the task with target index number.\n" + + " -Example: " + COMMAND_WORD + + " 1"; + + private final int taskIndex; public DeleteCommand(int taskIndex) { diff --git a/src/main/java/commands/HelpCommand.java b/src/main/java/commands/HelpCommand.java index bfd4fcb22..7b44ce958 100644 --- a/src/main/java/commands/HelpCommand.java +++ b/src/main/java/commands/HelpCommand.java @@ -13,17 +13,14 @@ public class HelpCommand extends Command { public String execute() { return ExitCommand.MESSAGE_USAGE + "\n" + HelpCommand.MESSAGE_USAGE + + "\n" + ExitCommand.MESSAGE_USAGE + "\n" + ListCommand.MESSAGE_USAGE - + "\n" + TodoCommand.MESSAGE_USAGE; + + "\n" + TodoCommand.MESSAGE_USAGE + + "\n" + DeadlineCommand.MESSAGE_USAGE + + "\n" + EventCommand.MESSAGE_USAGE + + "\n" + MarkCommand.MESSAGE_USAGE + + "\n" + UnmarkCommand.MESSAGE_USAGE + + "\n" + DeleteCommand.MESSAGE_USAGE; } -// return AddCommand.MESSAGE_USAGE -// + "\n" + DeleteCommand.MESSAGE_USAGE -// + "\n" + ClearCommand.MESSAGE_USAGE -// + "\n" + FindCommand.MESSAGE_USAGE -// + "\n" + ListCommand.MESSAGE_USAGE -// + "\n" + ViewCommand.MESSAGE_USAGE -// + "\n" + ViewAllCommand.MESSAGE_USAGE -// + "\n" + HelpCommand.MESSAGE_USAGE -// + "\n" + ExitCommand.MESSAGE_USAGE } diff --git a/src/main/java/commands/MarkCommand.java b/src/main/java/commands/MarkCommand.java index 5f902dd3c..d84b1fe30 100644 --- a/src/main/java/commands/MarkCommand.java +++ b/src/main/java/commands/MarkCommand.java @@ -4,6 +4,11 @@ public class MarkCommand extends Command { public static final String COMMAND_WORD = "mark"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Mark the task with target index number as done.\n" + + " -Example: " + COMMAND_WORD + + " 1"; + public int taskIndex; public MarkCommand(int taskIndex) { diff --git a/src/main/java/commands/UnmarkCommand.java b/src/main/java/commands/UnmarkCommand.java index 37031ea24..f540b4d19 100644 --- a/src/main/java/commands/UnmarkCommand.java +++ b/src/main/java/commands/UnmarkCommand.java @@ -4,6 +4,12 @@ public class UnmarkCommand extends Command { public static final String COMMAND_WORD = "unmark"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Mark the task with target index number as undone.\n" + + " -Example: " + COMMAND_WORD + + " 1"; + + public int taskIndex; public UnmarkCommand(int taskIndex) { From b02cb4c3afdd19bb06ae2e95e2f9fe0681e6cf33 Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 14 Mar 2025 07:16:49 +0800 Subject: [PATCH 12/19] Add FindCommand class to handle level-9 task finding matching task. Update relevant classes (HelpCommand, Parser) for newly added class. --- src/main/java/commands/FindCommand.java | 42 +++++++++++++++++++++++++ src/main/java/commands/HelpCommand.java | 1 + src/main/java/data/TaskList.java | 11 ++++++- src/main/java/data/task/Task.java | 4 +++ src/main/java/parser/Parser.java | 9 ++++++ 5 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/main/java/commands/FindCommand.java diff --git a/src/main/java/commands/FindCommand.java b/src/main/java/commands/FindCommand.java new file mode 100644 index 000000000..0c7e7969b --- /dev/null +++ b/src/main/java/commands/FindCommand.java @@ -0,0 +1,42 @@ +package commands; + +import data.task.Task; + +import java.util.ArrayList; +import java.util.List; + +public class FindCommand extends Command { + + public static final String COMMAND_WORD = "find"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Displays all tasks as a list with target key word(s)." + + " -Example: " + COMMAND_WORD + + " read"; + + /** Offset required to convert between 1-indexing and 0-indexing. */ + public static final int DISPLAYED_INDEX_OFFSET = 1; + + private final String keyword; + + public FindCommand(String keyword) { + this.keyword = keyword; + } + + public String execute() { + List matchingTasks = taskList.findMatch(keyword); + + if (matchingTasks.isEmpty()) { + return "You don't have any matching task in your list."; + } + + StringBuilder sb = new StringBuilder(); + sb.append("Here are the matching tasks in your list:").append("\n"); + int size = matchingTasks.size(); + for (int i = 0; i < size - DISPLAYED_INDEX_OFFSET; i += 1) { + sb.append((i + DISPLAYED_INDEX_OFFSET)).append(". ").append(matchingTasks.get(i).toString()).append("\n"); + } + sb.append(size).append(". ").append(matchingTasks.get(size - 1).toString()); + return sb.toString(); + } +} diff --git a/src/main/java/commands/HelpCommand.java b/src/main/java/commands/HelpCommand.java index 7b44ce958..105c24a51 100644 --- a/src/main/java/commands/HelpCommand.java +++ b/src/main/java/commands/HelpCommand.java @@ -18,6 +18,7 @@ public String execute() { + "\n" + TodoCommand.MESSAGE_USAGE + "\n" + DeadlineCommand.MESSAGE_USAGE + "\n" + EventCommand.MESSAGE_USAGE + + "\n" + FindCommand.MESSAGE_USAGE + "\n" + MarkCommand.MESSAGE_USAGE + "\n" + UnmarkCommand.MESSAGE_USAGE + "\n" + DeleteCommand.MESSAGE_USAGE; diff --git a/src/main/java/data/TaskList.java b/src/main/java/data/TaskList.java index 83403756b..c561a172f 100644 --- a/src/main/java/data/TaskList.java +++ b/src/main/java/data/TaskList.java @@ -3,7 +3,6 @@ import data.task.Task; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; public class TaskList { @@ -41,4 +40,14 @@ public void deleteTask(int taskIndex) { public List getAllTasks() { return this.tasks; } + + public List findMatch(String keyWork) { + List internalList = new ArrayList<>(); + for (Task task : tasks) { + if (task.getTaskName().contains(keyWork)) { + internalList.add(task); + } + } + return internalList; + } } diff --git a/src/main/java/data/task/Task.java b/src/main/java/data/task/Task.java index 8ec70b023..e70eb960d 100644 --- a/src/main/java/data/task/Task.java +++ b/src/main/java/data/task/Task.java @@ -9,6 +9,10 @@ public Task(String taskName) { this.isDone = false; } + public String getTaskName() { + return taskName; + } + public String getStatusIcon() { return (isDone ? "X" : " "); // mark done task with X } diff --git a/src/main/java/parser/Parser.java b/src/main/java/parser/Parser.java index 419033664..f09e96bb4 100644 --- a/src/main/java/parser/Parser.java +++ b/src/main/java/parser/Parser.java @@ -5,6 +5,7 @@ import commands.DeleteCommand; import commands.EventCommand; import commands.ExitCommand; +import commands.FindCommand; import commands.HelpCommand; import commands.InvalidCommand; import commands.ListCommand; @@ -61,6 +62,14 @@ public Command parseCommand(String userInput) { "Use: event /from /to "); } + case FindCommand.COMMAND_WORD: + try { + String keyword = inputParts[1]; + return new FindCommand(keyword); + } catch (ArrayIndexOutOfBoundsException e) { + return new WrongFormatCommand("Invalid format! Use: Find "); + } + case MarkCommand.COMMAND_WORD: try { int taskIndex = Integer.parseInt(inputParts[1]); From 4bcfc505f3d5f74155a6b6d8c65b9d875da4dcb8 Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 14 Mar 2025 07:49:10 +0800 Subject: [PATCH 13/19] Add header comments to most non-private classes/methods, and non-trivial private methods. --- src/main/java/Flaaaash.java | 21 ++++++++++--- src/main/java/commands/Command.java | 6 ++++ src/main/java/commands/DeadlineCommand.java | 4 ++- src/main/java/commands/DeleteCommand.java | 3 ++ src/main/java/commands/EventCommand.java | 3 ++ src/main/java/commands/ExitCommand.java | 3 ++ src/main/java/commands/FindCommand.java | 4 +++ src/main/java/commands/InvalidCommand.java | 3 ++ src/main/java/commands/ListCommand.java | 3 ++ src/main/java/commands/MarkCommand.java | 3 ++ src/main/java/commands/TodoCommand.java | 3 ++ src/main/java/commands/UnmarkCommand.java | 3 ++ .../java/commands/WrongFormatCommand.java | 3 ++ src/main/java/common/Messages.java | 3 ++ src/main/java/data/TaskList.java | 31 +++++++++++++++++-- src/main/java/data/task/Deadline.java | 8 +++++ src/main/java/data/task/Event.java | 9 ++++++ src/main/java/data/task/Task.java | 15 +++++++++ src/main/java/data/task/Todo.java | 3 ++ .../exceptions/EmptyArgumentException.java | 7 ----- src/main/java/parser/Parser.java | 10 +++++- src/main/java/storage/StorageFile.java | 18 +++++++++-- src/main/java/storage/TaskDecoder.java | 19 +++++++++--- src/main/java/storage/TaskEncoder.java | 7 +++++ src/main/java/ui/TextUi.java | 16 ++++++++-- 25 files changed, 183 insertions(+), 25 deletions(-) delete mode 100644 src/main/java/exceptions/EmptyArgumentException.java diff --git a/src/main/java/Flaaaash.java b/src/main/java/Flaaaash.java index 7fe5eaacb..7e6eb04a7 100644 --- a/src/main/java/Flaaaash.java +++ b/src/main/java/Flaaaash.java @@ -6,12 +6,13 @@ import storage.StorageFile.StorageOperationException; import ui.TextUi; - -import java.io.File; - - +/** + * Entry point of the Flaaaash chatbot application. + * Initialises the application and starts the interaction with the user. + */ public class Flaaaash { + /** Version info of the program. */ public final String VERSION = "Chatbot Flaaaash Version-2.0"; private TextUi ui; @@ -20,12 +21,16 @@ public class Flaaaash { public static void main(String[] args) { new Flaaaash().run(); } + /** Runs the program until termination. */ public void run() { start(); runCommandLoopUntilExitCommand(); exit(); } + /** + * Sets up the required objects, loads up the data from the storage file, and prints the welcome message. + */ public void start() { try { this.ui = new TextUi(); @@ -38,6 +43,7 @@ public void start() { } } + /** Reads the user command and executes it, until the user issues the exit command. */ public void runCommandLoopUntilExitCommand() { Command command; do { @@ -48,11 +54,18 @@ public void runCommandLoopUntilExitCommand() { } while (!ExitCommand.isExit(command)); } + /** Prints the Goodbye message and terminates the program. */ private void exit() { ui.showGoodbyeMessage(); System.exit(0); } + /** + * Executes the command and returns the result. + * + * @param command user command + * @return result string of the command + */ public String executeCommand(Command command) { try { command.setTaskList(taskList); diff --git a/src/main/java/commands/Command.java b/src/main/java/commands/Command.java index d4a7c13aa..03bd5f69b 100644 --- a/src/main/java/commands/Command.java +++ b/src/main/java/commands/Command.java @@ -2,6 +2,9 @@ import data.TaskList; +/** + * Represents an executable command. + */ public class Command { protected TaskList taskList; @@ -12,6 +15,9 @@ public String execute() { throw new UnsupportedOperationException("This method is to be implemented by child classes"); }; + /** + * Supplies the data the command will operate on. + */ public void setTaskList(TaskList taskList) { this.taskList = taskList; } diff --git a/src/main/java/commands/DeadlineCommand.java b/src/main/java/commands/DeadlineCommand.java index f185b709a..a633e51ab 100644 --- a/src/main/java/commands/DeadlineCommand.java +++ b/src/main/java/commands/DeadlineCommand.java @@ -2,7 +2,9 @@ import data.task.Deadline; - +/** + * Adds a deadline task to the task list. + */ public class DeadlineCommand extends Command { public static final String COMMAND_WORD = "deadline"; diff --git a/src/main/java/commands/DeleteCommand.java b/src/main/java/commands/DeleteCommand.java index 7f8063157..60ad7c659 100644 --- a/src/main/java/commands/DeleteCommand.java +++ b/src/main/java/commands/DeleteCommand.java @@ -1,5 +1,8 @@ package commands; +/** + * Deletes a task identified using its index in the task list. + */ public class DeleteCommand extends Command { public static final String COMMAND_WORD = "delete"; diff --git a/src/main/java/commands/EventCommand.java b/src/main/java/commands/EventCommand.java index e4c96057d..76f0f873a 100644 --- a/src/main/java/commands/EventCommand.java +++ b/src/main/java/commands/EventCommand.java @@ -2,6 +2,9 @@ import data.task.Event; +/** + * Adds an event task to the task list. + */ public class EventCommand extends Command { public static final String COMMAND_WORD = "event"; diff --git a/src/main/java/commands/ExitCommand.java b/src/main/java/commands/ExitCommand.java index 0e3ea19c7..7b317d64f 100644 --- a/src/main/java/commands/ExitCommand.java +++ b/src/main/java/commands/ExitCommand.java @@ -1,5 +1,8 @@ package commands; +/** + * Terminates the program. + */ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; diff --git a/src/main/java/commands/FindCommand.java b/src/main/java/commands/FindCommand.java index 0c7e7969b..6a1f09ee0 100644 --- a/src/main/java/commands/FindCommand.java +++ b/src/main/java/commands/FindCommand.java @@ -5,6 +5,10 @@ import java.util.ArrayList; import java.util.List; +/** + * Finds and lists all tasks in task list of which the name contains the argument keyword(s). + * Keyword matching is case-sensitive. + */ public class FindCommand extends Command { public static final String COMMAND_WORD = "find"; diff --git a/src/main/java/commands/InvalidCommand.java b/src/main/java/commands/InvalidCommand.java index bdd96c60a..10b353fdd 100644 --- a/src/main/java/commands/InvalidCommand.java +++ b/src/main/java/commands/InvalidCommand.java @@ -1,5 +1,8 @@ package commands; +/** + * Represents an invalid command. Upon execution, produces some feedback to the user. + */ public class InvalidCommand extends Command { private final String invalidCommand; diff --git a/src/main/java/commands/ListCommand.java b/src/main/java/commands/ListCommand.java index 461c72ee6..8557ba6ac 100644 --- a/src/main/java/commands/ListCommand.java +++ b/src/main/java/commands/ListCommand.java @@ -1,5 +1,8 @@ package commands; +/** + * Lists all tasks in the task list to the user. + */ public class ListCommand extends Command { public static final String COMMAND_WORD = "list"; diff --git a/src/main/java/commands/MarkCommand.java b/src/main/java/commands/MarkCommand.java index d84b1fe30..96f3076f2 100644 --- a/src/main/java/commands/MarkCommand.java +++ b/src/main/java/commands/MarkCommand.java @@ -1,5 +1,8 @@ package commands; +/** + * Mark the task specified by the user as done. + */ public class MarkCommand extends Command { public static final String COMMAND_WORD = "mark"; diff --git a/src/main/java/commands/TodoCommand.java b/src/main/java/commands/TodoCommand.java index 3f712e23c..f22616af2 100644 --- a/src/main/java/commands/TodoCommand.java +++ b/src/main/java/commands/TodoCommand.java @@ -2,6 +2,9 @@ import data.task.Todo; +/** + * Adds a todo task to the task list. + */ public class TodoCommand extends Command { public static final String COMMAND_WORD = "todo"; diff --git a/src/main/java/commands/UnmarkCommand.java b/src/main/java/commands/UnmarkCommand.java index f540b4d19..1a62b95ff 100644 --- a/src/main/java/commands/UnmarkCommand.java +++ b/src/main/java/commands/UnmarkCommand.java @@ -1,5 +1,8 @@ package commands; +/** + * Mark the task specified by the user as undone. + */ public class UnmarkCommand extends Command { public static final String COMMAND_WORD = "unmark"; diff --git a/src/main/java/commands/WrongFormatCommand.java b/src/main/java/commands/WrongFormatCommand.java index a9acfb1d6..f21a3b2fd 100644 --- a/src/main/java/commands/WrongFormatCommand.java +++ b/src/main/java/commands/WrongFormatCommand.java @@ -1,5 +1,8 @@ package commands; +/** + * Represents a command with wrong format. Upon execution, produces some feedback to the user. + */ public class WrongFormatCommand extends Command { private final String feedbackToUSer; diff --git a/src/main/java/common/Messages.java b/src/main/java/common/Messages.java index 15911e1d8..8bb45ebb5 100644 --- a/src/main/java/common/Messages.java +++ b/src/main/java/common/Messages.java @@ -1,5 +1,8 @@ package common; +/** + * Container for user visible messages. + */ public class Messages { public static final String MESSAGE_WELCOME = "Hello! I'm Flaaaash, what can I do for you?"; diff --git a/src/main/java/data/TaskList.java b/src/main/java/data/TaskList.java index c561a172f..bff392ff8 100644 --- a/src/main/java/data/TaskList.java +++ b/src/main/java/data/TaskList.java @@ -5,14 +5,24 @@ import java.util.ArrayList; import java.util.List; +/** + * Manages a list of tasks. + */ public class TaskList { private final List tasks; + /** + * Constructs an empty TaskList. + */ public TaskList() { this.tasks = new ArrayList<>(); } + /** + * Constructs a TaskList with an existing list of tasks. + * @param tasks List of tasks. + */ public TaskList(List tasks) { this.tasks = tasks; } @@ -29,22 +39,39 @@ public Task get(int index) { return tasks.get(index); } + /** + * Adds a new task to the list. + * @param toAdd Task to add. + */ public void addTask(Task toAdd) { tasks.add(toAdd); } + /** + * Removes a task at a specific index. + * @param taskIndex Index of the task to remove. + */ public void deleteTask(int taskIndex) { tasks.remove(taskIndex); } + /** + * Gets all tasks in the list. + * @return List of tasks. + */ public List getAllTasks() { return this.tasks; } - public List findMatch(String keyWork) { + /** + * Finds tasks that contain a specific keyword. + * @param keyword The search keyword. + * @return List of matching tasks. + */ + public List findMatch(String keyword) { List internalList = new ArrayList<>(); for (Task task : tasks) { - if (task.getTaskName().contains(keyWork)) { + if (task.getTaskName().contains(keyword)) { internalList.add(task); } } diff --git a/src/main/java/data/task/Deadline.java b/src/main/java/data/task/Deadline.java index f64b45683..16e1c4c71 100644 --- a/src/main/java/data/task/Deadline.java +++ b/src/main/java/data/task/Deadline.java @@ -1,8 +1,16 @@ package data.task; +/** + * Represents a Deadline task with a due time. + */ public class Deadline extends Task { protected String deadline; + /** + * Constructs a Deadline task with a task name and deadline. + * @param taskName Name of the deadline task. + * @param deadline Due time of the task. + */ public Deadline(String taskName, String deadline) { super(taskName); this.deadline = deadline; diff --git a/src/main/java/data/task/Event.java b/src/main/java/data/task/Event.java index de06aa53e..c08e55829 100644 --- a/src/main/java/data/task/Event.java +++ b/src/main/java/data/task/Event.java @@ -1,9 +1,18 @@ package data.task; +/** + * Represents an Event task with a start and end time. + */ public class Event extends Task { protected String from; protected String to; + /** + * Constructs an Event task with a task name, start time, and end time. + * @param taskName Name of the event task. + * @param from Start time of the event. + * @param to End time of the event. + */ public Event(String taskName, String from, String to) { super(taskName); this.from = from; diff --git a/src/main/java/data/task/Task.java b/src/main/java/data/task/Task.java index e70eb960d..6f94c87b2 100644 --- a/src/main/java/data/task/Task.java +++ b/src/main/java/data/task/Task.java @@ -1,9 +1,16 @@ package data.task; +/** + * Represents a task with a name and completion status. + */ public abstract class Task { protected String taskName; protected boolean isDone; + /** + * Constructs a new Task with the given task name. + * @param taskName Name of the task. + */ public Task(String taskName) { this.taskName = taskName; this.isDone = false; @@ -13,6 +20,10 @@ public String getTaskName() { return taskName; } + /** + * Gets the status icon representing whether the task is done. + * @return "X" if done, otherwise a space " ". + */ public String getStatusIcon() { return (isDone ? "X" : " "); // mark done task with X } @@ -30,5 +41,9 @@ public String toString() { return "[" + getStatusIcon() + "] " + taskName; } + /** + * Returns the task formatted as a string for file storage. + * @return String representation for file storage. + */ public abstract String toFileString(); } diff --git a/src/main/java/data/task/Todo.java b/src/main/java/data/task/Todo.java index c55b031b0..aa2fc20c1 100644 --- a/src/main/java/data/task/Todo.java +++ b/src/main/java/data/task/Todo.java @@ -1,5 +1,8 @@ package data.task; +/** + * Represents a Todo task without any specific date or time. + */ public class Todo extends Task { public Todo(String taskName) { super(taskName); diff --git a/src/main/java/exceptions/EmptyArgumentException.java b/src/main/java/exceptions/EmptyArgumentException.java deleted file mode 100644 index fc2178848..000000000 --- a/src/main/java/exceptions/EmptyArgumentException.java +++ /dev/null @@ -1,7 +0,0 @@ -package exceptions; - -public class EmptyArgumentException extends ArrayIndexOutOfBoundsException { - public EmptyArgumentException(String message) { - super(message); - } -} diff --git a/src/main/java/parser/Parser.java b/src/main/java/parser/Parser.java index f09e96bb4..fc32bec06 100644 --- a/src/main/java/parser/Parser.java +++ b/src/main/java/parser/Parser.java @@ -14,9 +14,17 @@ import commands.UnmarkCommand; import commands.WrongFormatCommand; - +/** + * Parses user input. + */ public class Parser { + /** + * Parses user input into command for execution. + * + * @param userInput full user input string + * @return the command based on the user input + */ public Command parseCommand(String userInput) { String[] inputParts = userInput.split(" ", 2); String commandWord = inputParts[0].toLowerCase(); diff --git a/src/main/java/storage/StorageFile.java b/src/main/java/storage/StorageFile.java index 9e4c7f96c..2be7b497d 100644 --- a/src/main/java/storage/StorageFile.java +++ b/src/main/java/storage/StorageFile.java @@ -11,6 +11,9 @@ import java.nio.file.Paths; import java.util.List; +/** + * Represents the file used to store task list data. + */ public class StorageFile { public static final String DEFAULT_STORAGE_FILEPATH = System.getProperty("user.home") @@ -22,6 +25,11 @@ public StorageFile() { this.path = Paths.get(DEFAULT_STORAGE_FILEPATH); } + /** + * Saves the {@code TaskList} data to the storage file. + * + * @throws StorageOperationException if there were errors converting and/or storing data to file. + */ public void save(TaskList taskList) throws StorageOperationException { try { List encodedTaskList = TaskEncoder.encodeTaskList(taskList); @@ -31,6 +39,12 @@ public void save(TaskList taskList) throws StorageOperationException { } } + /** + * Loads the {@code TaskList} data from this storage file, and then returns it. + * Returns an empty {@code TaskList} if the file does not exist, or is not a regular file. + * + * @throws StorageOperationException if there were errors reading and/or converting data from file. + */ public TaskList load() throws StorageOperationException { if (!Files.exists(path) || !Files.isRegularFile(path)) { return new TaskList(); @@ -43,13 +57,11 @@ public TaskList load() throws StorageOperationException { // other errors } catch (IOException ioe) { throw new StorageOperationException("Error writing to file: " + path); - } catch (IllegalValueException ive) { - throw new StorageOperationException("File contains illegal data values; data type constraints not met"); } } /** - * Signals that some error has occured while trying to convert and read/write data between the application + * Signals that some error has occurred while trying to convert and read/write data between the application * and the storage file. */ public static class StorageOperationException extends Exception { diff --git a/src/main/java/storage/TaskDecoder.java b/src/main/java/storage/TaskDecoder.java index 83d6f3086..486062f20 100644 --- a/src/main/java/storage/TaskDecoder.java +++ b/src/main/java/storage/TaskDecoder.java @@ -10,16 +10,22 @@ import java.util.ArrayList; import java.util.List; -import java.util.Scanner; -import java.util.regex.Matcher; import java.util.regex.Pattern; +/** + * Decodes the storage data file into an {@code TaskList} object. + */ public class TaskDecoder { public static final Pattern TODO_DATA_FORMAT = Pattern.compile("T / [01] / .+"); + /** + * Decodes {@code encodedTaskList} into an {@code TaskList} containing the decoded tasks. + * + * @throws StorageOperationException if the {@code encodedTaskList} is in an invalid format. + */ public static TaskList decodeTaskList(List encodedTasks) - throws IllegalValueException, StorageOperationException { + throws StorageOperationException { final List decodedTasks = new ArrayList<>(); for (String encodedTask : encodedTasks) { decodedTasks.add(decodeTaskFromString(encodedTask)); @@ -27,8 +33,13 @@ public static TaskList decodeTaskList(List encodedTasks) return new TaskList(decodedTasks); } + /** + * Decodes {@code encodedTask} into a {@code Task}. + * + * @throws StorageOperationException if {@code encodedTask} is in an invalid format. + */ private static Task decodeTaskFromString(String encodedTask) - throws IllegalValueException, StorageOperationException { + throws StorageOperationException { String[] encodedParts = encodedTask.split(" / "); try { String type = encodedParts[0]; diff --git a/src/main/java/storage/TaskEncoder.java b/src/main/java/storage/TaskEncoder.java index 63edd2ae7..6a98c9546 100644 --- a/src/main/java/storage/TaskEncoder.java +++ b/src/main/java/storage/TaskEncoder.java @@ -5,7 +5,14 @@ import java.util.ArrayList; import java.util.List; +/** + * Encodes the {@code TaskList} object into a data file for storage. + */ public class TaskEncoder { + /** + * Encodes all the {@code Task} in the {@code toSave} into a list of decodable and readable string presentation + * for storage. + */ public static List encodeTaskList(TaskList toSave) { final List encodedTasks = new ArrayList<>(); toSave.getAllTasks().forEach(task -> encodedTasks.add(task.toFileString())); diff --git a/src/main/java/ui/TextUi.java b/src/main/java/ui/TextUi.java index 3d9bd63d4..905b2d887 100644 --- a/src/main/java/ui/TextUi.java +++ b/src/main/java/ui/TextUi.java @@ -8,12 +8,12 @@ import static common.Messages.MESSAGE_INIT_FAILED; import static common.Messages.MESSAGE_WELCOME; +/** + * Text UI of the application. + */ public class TextUi { private static final String DIVIDER = "____________________________________________________________"; - /** Offset required to convert between 1-indexing and 0-indexing. */ - public static final int DISPLAYED_INDEX_OFFSET = 1; - private final Scanner in; private final PrintStream out; @@ -26,6 +26,10 @@ public TextUi(InputStream in, PrintStream out) { this.out = out; } + /** + * Generates and prints the welcome message upon the start of the application. + * @param version current version of the application. + */ public void showWelcomeMessage(String version) { showToUser( DIVIDER, @@ -34,6 +38,11 @@ public void showWelcomeMessage(String version) { DIVIDER); } + /** + * Prompts for the command and reads the text entered by the user. + * Echos the command back to the user. + * @return command (full line) entered by the user + */ public String getUserCommand() { out.print("Enter command: "); String fullInputLine = in.nextLine().trim(); @@ -54,5 +63,6 @@ public void showGoodbyeMessage() { showToUser(MESSAGE_GOODBYE); } + /** Shows message to the user when failed to initialise the program */ public void showInitFailedMessage() { showToUser(MESSAGE_INIT_FAILED);} } From 8d595d96a0c504a01ee48730b15266f95289884c Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 14 Mar 2025 08:24:33 +0800 Subject: [PATCH 14/19] Update README.md (unfinished). --- docs/README.md | 49 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/docs/README.md b/docs/README.md index 47b9f984f..aafb8a8d5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,30 +1,53 @@ -# Duke User Guide +# Flaaaash User Guide -// Update the title above to match the actual product name +Flaaaash is a simple command-line task management application +that helps users keep track of their tasks efficiently. +Users can create tasks, mark them as done, delete them, +and list all tasks. The application also supports saving and +loading tasks from a file to persist data between sessions. -// Product screenshot goes here +-------------------------------------------------------------- -// Product intro goes here +## Quick Start -## Adding deadlines +1. Ensure you have Java 17 or above installed in your Computer.\ + Mac users: Ensure you have the precise JDK version prescribed [here](https://se-education.org/guides/tutorials/javaInstallationMac.html). -// Describe the action and its outcome. +2. Download the latest .jar file from [here](google.com). -// Give examples of usage +3. Copy the file to the folder you want to use as the home folder for your Flaaaash chatbot. -Example: `keyword (optional arguments)` +4. Open a command terminal, cd into the folder you put the jar file in, and use the java -jar Flaaaash.jar command to run the application. + +5. Type the command in the command box and press Enter to execute it. e.g. typing help and pressing Enter will open the help window. +Some example commands you can try: + + - `list` : Lists all tasks. + + - `todo Read a book`: Adds a todo task named Read a book to the task list. + + - `delete 1` : Deletes the 1st task shown in the current list. + + - `exit` : Exits the app. + +6. Refer to the Features below for details of each command. -// A description of the expected outcome goes here + +## Features ``` -expected output +:notebook_with_decorative_cover: Notes about the command format: + - Words in UPPER_CASE are the parameters to be supplied by the user. + e.g. in add n/NAME, NAME is a parameter which can be used as add n/John Doe. ``` -## Feature ABC +## Feature XYZ // Feature details -## Feature XYZ +// A description of the expected outcome goes here -// Feature details \ No newline at end of file +``` +expected output +``` \ No newline at end of file From 0884152586ea12c1c14d4b92f8bb278b140382a9 Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 14 Mar 2025 08:34:45 +0800 Subject: [PATCH 15/19] Minor changes. --- docs/README.md | 11 ++++++----- src/main/java/Flaaaash.java | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/README.md b/docs/README.md index aafb8a8d5..6b33a82ee 100644 --- a/docs/README.md +++ b/docs/README.md @@ -35,11 +35,12 @@ Some example commands you can try: ## Features -``` -:notebook_with_decorative_cover: Notes about the command format: - - Words in UPPER_CASE are the parameters to be supplied by the user. - e.g. in add n/NAME, NAME is a parameter which can be used as add n/John Doe. -``` + +Notes about the command format: + - Words in `UPPER_CASE` are the parameters to be supplied by the user.\ + e.g. in `todo TASK_NAME`, `TASK_NAME` is a parameter which can be used as `todo Read a Book`. + - Parameters have a fixed order. + e.g. if the command specifies `/from START_TIME /to END_TIME`, no other order of input is acceptable. ## Feature XYZ diff --git a/src/main/java/Flaaaash.java b/src/main/java/Flaaaash.java index 7e6eb04a7..195627f43 100644 --- a/src/main/java/Flaaaash.java +++ b/src/main/java/Flaaaash.java @@ -13,7 +13,7 @@ public class Flaaaash { /** Version info of the program. */ - public final String VERSION = "Chatbot Flaaaash Version-2.0"; + public final String VERSION = "Chatbot Flaaaash Version-0.2"; private TextUi ui; private TaskList taskList; From 989918e7444cc6dbd596146d28b55d7ab2aef81b Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 14 Mar 2025 09:10:09 +0800 Subject: [PATCH 16/19] Finished User Guide. --- docs/README.md | 83 +++++++++++++++++++++++++++----- src/main/java/data/TaskList.java | 2 +- src/main/java/parser/Parser.java | 2 +- 3 files changed, 74 insertions(+), 13 deletions(-) diff --git a/docs/README.md b/docs/README.md index 6b33a82ee..1f250a01f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -32,6 +32,7 @@ Some example commands you can try: 6. Refer to the Features below for details of each command. +-------------------------------------------------------------------------- ## Features @@ -41,14 +42,74 @@ Notes about the command format: e.g. in `todo TASK_NAME`, `TASK_NAME` is a parameter which can be used as `todo Read a Book`. - Parameters have a fixed order. e.g. if the command specifies `/from START_TIME /to END_TIME`, no other order of input is acceptable. - -## Feature XYZ - -// Feature details - - -// A description of the expected outcome goes here - -``` -expected output -``` \ No newline at end of file + - Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, and `exit`) will be ignored. + e.g. if the command specifies `help 123`, it will be interpreted as `help`. + +### Viewing help : `help` +Shows a message explaining all available commands.\ +Format: `help` + +### Terminating the program : `exit` +Exit the program.\ +Format: `exit` + +### Adding a todo task: `todo` +Adds a task with no specified time constraints to the task list.\ +Format: `todo TASK_NAME`\ +Example: +- `todo Read a book` +- `todo Exercise` + +### Adding a deadline task: `deadline` +Adds a task with a deadline to the task list.\ +Format: `deadline TASK_NAME /by DEADLINE`\ +Example: +- `deadline Return the book /by this Saturday` +- `deadline Finish math homework /by tomorrow` + +### Adding a event task: `event` +Adds a task with a specified start time and end time to the task list.\ +Format: `event TASK_NAME /from START_TIME /to END_TIME`\ +Example: +- `event Attend book fair /from 24th April 2025 2pm /to 4pm ` + +### Listing all tasks : `list` +Shows a list of all tasks in the task list.\ +Format: `list` + +### Finding tasks with specified keyword(s): `find` +Finds tasks of which contents contain the given keywords.\ +Format: `find KEYWORD` +- The search is case-insensitive. e.g read will match Read. +- The order of the keywords **MATTERS**. +- Only the task name is searched. + +### Deleting a task : delete +Deletes the specified task from the task list.\ +Format: `delete INDEX` + +- Deletes the task at the specified INDEX. +- The index refers to the index number shown in the task list (not applicable to the find list). +- The index must be a positive integer. + +Examples: +- `delete 2` deletes the 2nd task in the task list. + +### Marking a task as done : `mark` +Mark a task as done.\ +Format: `mark INDEX` + +- Marks the task at the specified INDEX. +- The index refers to the index number shown in the task list (not applicable to the find list). +- The index must be a positive integer. + +### Marking a task as undone : `unmark` +Mark a task as undone.\ +Format: `unmark INDEX` + +- Unmarks the task at the specified INDEX. +- The index refers to the index number shown in the task list (not applicable to the find list). +- The index must be a positive integer. + +## Saving the data +Task List data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. \ No newline at end of file diff --git a/src/main/java/data/TaskList.java b/src/main/java/data/TaskList.java index bff392ff8..462ebe3aa 100644 --- a/src/main/java/data/TaskList.java +++ b/src/main/java/data/TaskList.java @@ -71,7 +71,7 @@ public List getAllTasks() { public List findMatch(String keyword) { List internalList = new ArrayList<>(); for (Task task : tasks) { - if (task.getTaskName().contains(keyword)) { + if (task.getTaskName().toLowerCase().contains(keyword)) { internalList.add(task); } } diff --git a/src/main/java/parser/Parser.java b/src/main/java/parser/Parser.java index fc32bec06..a1206b7d4 100644 --- a/src/main/java/parser/Parser.java +++ b/src/main/java/parser/Parser.java @@ -72,7 +72,7 @@ public Command parseCommand(String userInput) { case FindCommand.COMMAND_WORD: try { - String keyword = inputParts[1]; + String keyword = inputParts[1].trim().toLowerCase(); return new FindCommand(keyword); } catch (ArrayIndexOutOfBoundsException e) { return new WrongFormatCommand("Invalid format! Use: Find "); From 1e5f56c3a7c9cf2f3089e0e055f62eaa468fb13d Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Fri, 14 Mar 2025 09:12:43 +0800 Subject: [PATCH 17/19] Update Release link in README.md. --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 1f250a01f..8bc70e483 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,7 +13,7 @@ loading tasks from a file to persist data between sessions. 1. Ensure you have Java 17 or above installed in your Computer.\ Mac users: Ensure you have the precise JDK version prescribed [here](https://se-education.org/guides/tutorials/javaInstallationMac.html). -2. Download the latest .jar file from [here](google.com). +2. Download the latest .jar file from [here](https://github.com/Flaaaash/ip/releases). 3. Copy the file to the folder you want to use as the home folder for your Flaaaash chatbot. From ab5fc6918471bcc70f42c45016052780226ef93e Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Mon, 31 Mar 2025 22:46:11 +0800 Subject: [PATCH 18/19] Change StorageFile default storage path to save by file name. Improve the level of abstraction of Parser class --- data/Flaaaash.txt | 5 + src/main/java/Flaaaash.java | 7 +- src/main/java/common/Messages.java | 3 +- src/main/java/parser/Parser.java | 167 ++++++++++++++++++------- src/main/java/storage/StorageFile.java | 20 ++- 5 files changed, 145 insertions(+), 57 deletions(-) create mode 100644 data/Flaaaash.txt diff --git a/data/Flaaaash.txt b/data/Flaaaash.txt new file mode 100644 index 000000000..1d5fb4593 --- /dev/null +++ b/data/Flaaaash.txt @@ -0,0 +1,5 @@ +T / 1 / Submit +E / 0 / Final exam / 29 April 2025 2:00pm / 4:00pm +T / 0 / test +D / 0 / test / test +E / 0 / something / 1pm / 2pm diff --git a/src/main/java/Flaaaash.java b/src/main/java/Flaaaash.java index 195627f43..64a0af369 100644 --- a/src/main/java/Flaaaash.java +++ b/src/main/java/Flaaaash.java @@ -14,6 +14,7 @@ public class Flaaaash { /** Version info of the program. */ public final String VERSION = "Chatbot Flaaaash Version-0.2"; + public static final String DEFAULT_STORAGE_FILEPATH = "./data/Flaaaash.txt"; private TextUi ui; private TaskList taskList; @@ -34,12 +35,12 @@ public void run() { public void start() { try { this.ui = new TextUi(); - this.storage = new StorageFile(); - this.taskList = storage.load(); ui.showWelcomeMessage(VERSION); + this.storage = new StorageFile(DEFAULT_STORAGE_FILEPATH); + this.taskList = storage.load(); } catch (StorageOperationException e) { ui.showInitFailedMessage(); - throw new RuntimeException(e); + this.taskList = new TaskList(); } } diff --git a/src/main/java/common/Messages.java b/src/main/java/common/Messages.java index 8bb45ebb5..345d0ced0 100644 --- a/src/main/java/common/Messages.java +++ b/src/main/java/common/Messages.java @@ -7,6 +7,7 @@ public class Messages { public static final String MESSAGE_WELCOME = "Hello! I'm Flaaaash, what can I do for you?"; public static final String MESSAGE_GOODBYE = "Bye. Hope to see you again soon!"; - public static final String MESSAGE_INIT_FAILED = "Failed to initialise Flaaaash. Exiting..."; + public static final String MESSAGE_INIT_FAILED = "Failed to initialise Flaaaash memory. " + + "Creating a new save file."; } \ No newline at end of file diff --git a/src/main/java/parser/Parser.java b/src/main/java/parser/Parser.java index a1206b7d4..d18c72bb4 100644 --- a/src/main/java/parser/Parser.java +++ b/src/main/java/parser/Parser.java @@ -41,70 +41,141 @@ public Command parseCommand(String userInput) { return new ListCommand(); case TodoCommand.COMMAND_WORD: - try { - return new TodoCommand(inputParts[1]); - } catch (ArrayIndexOutOfBoundsException e) { - return new WrongFormatCommand("Task description cannot be empty."); - } + return prepareAddTodo(inputParts[1]); case DeadlineCommand.COMMAND_WORD: - try { - String[] deadlineInputDetails = inputParts[1].split(" /by ", 2); - String taskName = deadlineInputDetails[0]; - String deadline = deadlineInputDetails[1]; - return new DeadlineCommand(taskName, deadline); - } catch (ArrayIndexOutOfBoundsException e) { - return new WrongFormatCommand("Invalid format! " + - "Use: deadline /by "); - } + return prepareAddDeadline(inputParts[1]); case EventCommand.COMMAND_WORD: - try { - String[] eventInputDetails = inputParts[1].split(" /from | /to ", 3); - String taskName = eventInputDetails[0]; - String from = eventInputDetails[1]; - String to = eventInputDetails[2]; - return new EventCommand(taskName, from, to); - } catch (ArrayIndexOutOfBoundsException e) { - return new WrongFormatCommand("Invalid format! " + - "Use: event /from /to "); - } + return prepareAddEvent(inputParts[1]); case FindCommand.COMMAND_WORD: - try { - String keyword = inputParts[1].trim().toLowerCase(); - return new FindCommand(keyword); - } catch (ArrayIndexOutOfBoundsException e) { - return new WrongFormatCommand("Invalid format! Use: Find "); - } + return prepareFind(inputParts[1]); case MarkCommand.COMMAND_WORD: - try { - int taskIndex = Integer.parseInt(inputParts[1]); - return new MarkCommand(taskIndex); - } catch (ArrayIndexOutOfBoundsException e) { - return new WrongFormatCommand("Invalid format! Use: mark "); - } + return prepareMark(inputParts[1]); case UnmarkCommand.COMMAND_WORD: - try { - int taskIndex = Integer.parseInt(inputParts[1]); - return new UnmarkCommand(taskIndex); - } catch (ArrayIndexOutOfBoundsException e) { - return new WrongFormatCommand("Invalid format! Use: unmark "); - } + return prepareUnmark(inputParts[1]); case DeleteCommand.COMMAND_WORD: - try { - int taskIndex = Integer.parseInt(inputParts[1]); - return new DeleteCommand(taskIndex); - } catch (ArrayIndexOutOfBoundsException e) { - return new WrongFormatCommand("Invalid format! Use: delete "); - } + return prepareDelete(inputParts[1]); default: return new InvalidCommand(commandWord); } } + + /** + * Parses arguments in the context of the todo command. + * + * @param args full command args string + * @return the prepared command + */ + public Command prepareAddTodo(String args) { + try { + return new TodoCommand(args); + } catch (ArrayIndexOutOfBoundsException e) { + return new WrongFormatCommand("Task description cannot be empty."); + } + } + + /** + * Parses arguments in the context of the deadline command. + * + * @param args full command args string + * @return the prepared command + */ + public Command prepareAddDeadline(String args) { + try { + String[] deadlineInputDetails = args.split(" /by ", 2); + String taskName = deadlineInputDetails[0]; + String deadline = deadlineInputDetails[1]; + return new DeadlineCommand(taskName, deadline); + } catch (ArrayIndexOutOfBoundsException e) { + return new WrongFormatCommand("Invalid format! " + + "Use: deadline /by "); + } + } + + /** + * Parses arguments in the context of the event command. + * + * @param args full command args string + * @return the prepared command + */ + public Command prepareAddEvent(String args) { + try { + String[] eventInputDetails = args.split(" /from | /to ", 3); + String taskName = eventInputDetails[0]; + String from = eventInputDetails[1]; + String to = eventInputDetails[2]; + return new EventCommand(taskName, from, to); + } catch (ArrayIndexOutOfBoundsException e) { + return new WrongFormatCommand("Invalid format! " + + "Use: event /from /to "); + } + } + + /** + * Parses arguments in the context of the find command. + * + * @param args full command args string + * @return the prepared command + */ + public Command prepareFind(String args) { + try { + String keyword = args.trim().toLowerCase(); + return new FindCommand(keyword); + } catch (ArrayIndexOutOfBoundsException e) { + return new WrongFormatCommand("Invalid format! Use: Find "); + } + } + + /** + * Parses arguments in the context of the mark command. + * + * @param args full command args string + * @return the prepared command + */ + public Command prepareMark(String args) { + try { + int taskIndex = Integer.parseInt(args); + return new MarkCommand(taskIndex); + } catch (ArrayIndexOutOfBoundsException e) { + return new WrongFormatCommand("Invalid format! Use: mark "); + } + } + + /** + * Parses arguments in the context of the unmark command. + * + * @param args full command args string + * @return the prepared command + */ + public Command prepareUnmark(String args) { + try { + int taskIndex = Integer.parseInt(args); + return new UnmarkCommand(taskIndex); + } catch (ArrayIndexOutOfBoundsException e) { + return new WrongFormatCommand("Invalid format! Use: unmark "); + } + } + + /** + * Parses arguments in the context of the delete command. + * + * @param args full command args string + * @return the prepared command + */ + public Command prepareDelete(String args) { + try { + int taskIndex = Integer.parseInt(args); + return new DeleteCommand(taskIndex); + } catch (ArrayIndexOutOfBoundsException e) { + return new WrongFormatCommand("Invalid format! Use: delete "); + } + } + } diff --git a/src/main/java/storage/StorageFile.java b/src/main/java/storage/StorageFile.java index 2be7b497d..13750f26f 100644 --- a/src/main/java/storage/StorageFile.java +++ b/src/main/java/storage/StorageFile.java @@ -16,13 +16,22 @@ */ public class StorageFile { - public static final String DEFAULT_STORAGE_FILEPATH = System.getProperty("user.home") - + File.separator + "Desktop" + File.separator + "Flaaaash.txt"; - public final Path path; - public StorageFile() { - this.path = Paths.get(DEFAULT_STORAGE_FILEPATH); + public StorageFile(String filePath) { + this.path = Paths.get(filePath); + createLocalStorage(); + } + + private void createLocalStorage() { + try { + if (!Files.exists(path)) { + Files.createDirectories(path.getParent()); + Files.createFile(path); + } + } catch (IOException e) { + System.out.println("Error creating file: " + e.getMessage()); + } } /** @@ -31,6 +40,7 @@ public StorageFile() { * @throws StorageOperationException if there were errors converting and/or storing data to file. */ public void save(TaskList taskList) throws StorageOperationException { + try { List encodedTaskList = TaskEncoder.encodeTaskList(taskList); Files.write(path, encodedTaskList); From 02d6c4be9cb50ffac61d61f1aa1d6d588d06724a Mon Sep 17 00:00:00 2001 From: Liu Xuyan <1739606183@qq.com> Date: Mon, 31 Mar 2025 22:50:23 +0800 Subject: [PATCH 19/19] Remove local data. --- data/Flaaaash.txt | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 data/Flaaaash.txt diff --git a/data/Flaaaash.txt b/data/Flaaaash.txt deleted file mode 100644 index 1d5fb4593..000000000 --- a/data/Flaaaash.txt +++ /dev/null @@ -1,5 +0,0 @@ -T / 1 / Submit -E / 0 / Final exam / 29 April 2025 2:00pm / 4:00pm -T / 0 / test -D / 0 / test / test -E / 0 / something / 1pm / 2pm