From db7b3b9da66a9a25e77ff408a94f26beb1d27cff Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Wed, 19 Jan 2022 16:35:42 +0800 Subject: [PATCH 01/47] Added a FastReader --- src/main/java/FastReader.java | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/main/java/FastReader.java diff --git a/src/main/java/FastReader.java b/src/main/java/FastReader.java new file mode 100644 index 0000000000..53172a82f1 --- /dev/null +++ b/src/main/java/FastReader.java @@ -0,0 +1,51 @@ +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Scanner; +import java.util.StringTokenizer; + +public class FastReader { + BufferedReader br; + StringTokenizer st; + + public FastReader() + { + br = new BufferedReader( + new InputStreamReader(System.in)); + } + + String next() + { + while (st == null || !st.hasMoreElements()) { + try { + st = new StringTokenizer(br.readLine()); + } + catch (IOException e) { + e.printStackTrace(); + } + } + return st.nextToken(); + } + + int nextInt() { return Integer.parseInt(next()); } + + long nextLong() { return Long.parseLong(next()); } + + double nextDouble() + { + return Double.parseDouble(next()); + } + + String nextLine() + { + String str = ""; + try { + str = br.readLine(); + } + catch (IOException e) { + e.printStackTrace(); + } + return str; + } +} + From f5cc97c246bd2261eab0365cc1ab5104e7b278a4 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Wed, 19 Jan 2022 16:36:28 +0800 Subject: [PATCH 02/47] Level-1 --- src/main/java/Duke.java | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 5d313334cc..f4073d3c64 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,3 +1,5 @@ +import java.util.*; + public class Duke { public static void main(String[] args) { String logo = " ____ _ \n" @@ -6,5 +8,37 @@ public static void main(String[] args) { + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n"; System.out.println("Hello from\n" + logo); + System.out.println(" ____________________________________________________________"); + System.out.println(" Hello! I'm Duke"); + System.out.println(" What can I do for you?"); + System.out.println(" ____________________________________________________________"); + boolean StillIn = true; + FastReader fr = new FastReader(); + while(StillIn) { + String input = fr.nextLine(); + System.out.println(" ____________________________________________________________"); + if(input.equals("list")) { + System.out.println(list()); + } else if(input.equals("blah")) { + System.out.println(blah()); + } else if(input.equals("bye")) { + System.out.println(bye()); + StillIn = false; + } + System.out.println(" ____________________________________________________________"); + } + } + + public static String list() { + return " list"; + } + + public static String blah() { + return " blah"; + } + + public static String bye() { + return " Bye. Hope to see you again soon!"; + } } From 3a030481c1e17667f2e17fdeec7bf4c852779523 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Wed, 19 Jan 2022 16:59:58 +0800 Subject: [PATCH 03/47] Level-2 --- src/main/java/Duke.java | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index f4073d3c64..5d8d82e297 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -14,23 +14,33 @@ public static void main(String[] args) { System.out.println(" ____________________________________________________________"); boolean StillIn = true; FastReader fr = new FastReader(); + ArrayList list = new ArrayList(100); while(StillIn) { String input = fr.nextLine(); System.out.println(" ____________________________________________________________"); - if(input.equals("list")) { - System.out.println(list()); - } else if(input.equals("blah")) { - System.out.println(blah()); - } else if(input.equals("bye")) { + if(input.equals("bye")) { System.out.println(bye()); StillIn = false; + continue; + } else if(input.equals("list")) { + list(list); + continue; + } + + if(list.contains(input)) { + System.out.println(input); + } else { + list.add(input); + System.out.println("added: " + input); } System.out.println(" ____________________________________________________________"); } } - public static String list() { - return " list"; + public static void list(ArrayList lst) { + for(int i=0; i < lst.size(); i++) { + System.out.printf("%d. %s\n", i + 1, lst.get(i)); + } } public static String blah() { From 98dd74fd9aa393ccea588a9f7e7db6fbee641f46 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Thu, 20 Jan 2022 21:00:38 +0800 Subject: [PATCH 04/47] added new Class Task --- src/main/java/Task.java | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/main/java/Task.java diff --git a/src/main/java/Task.java b/src/main/java/Task.java new file mode 100644 index 0000000000..1ba66904b2 --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,46 @@ +public class Task { + private String task; + private boolean done; + + public Task(String task) { + this.task = task; + this.done = false; + } + + public String getTask() { + return task; + } + + public void setDone() { + this.done = true; + } + + public void setUndone() { + this.done = false; + } + + @Override + public int hashCode() { + return task.hashCode(); + } + + @Override + public boolean equals(Object other) { + if(other instanceof Task){ + Task toCompare = (Task) other; + return this.task.equals(toCompare.getTask()); + } + return false; + } + + @Override + public String toString() { + String temp; + if(this.done) { + temp = "X"; + } else { + temp = " "; + } + return "[" + temp + "] " + this.task; + } +} From 82e3f0bd95392d3b11459c73146462db8cd9b89d Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Thu, 20 Jan 2022 21:00:51 +0800 Subject: [PATCH 05/47] Level-3 --- src/main/java/Duke.java | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 5d8d82e297..ae448d825a 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -14,30 +14,49 @@ public static void main(String[] args) { System.out.println(" ____________________________________________________________"); boolean StillIn = true; FastReader fr = new FastReader(); - ArrayList list = new ArrayList(100); + ArrayList list = new ArrayList(100); while(StillIn) { - String input = fr.nextLine(); + String in = fr.nextLine(); + String[] input = in.split(" "); + Task task = new Task(in); + boolean printed = false; System.out.println(" ____________________________________________________________"); - if(input.equals("bye")) { + if(input[0].equals("bye")) { System.out.println(bye()); StillIn = false; - continue; - } else if(input.equals("list")) { + printed = true; + } else if(input[0].equals("list")) { list(list); + printed = true; + } else if(input[0].equals("mark")) { + int num = Integer.parseInt(input[1]); + list.get(num - 1).setDone(); + System.out.println("Nice! I've marked this task as done:"); + System.out.println("[X] " + list.get(num - 1).getTask()); + printed = true; + } else if(input[0].equals("unmark")) { + int num = Integer.parseInt(input[1]); + list.get(num - 1).setUndone(); + System.out.println("OK, I've marked this task as not done yet:"); + System.out.println("[ ] " + list.get(num - 1).getTask()); + } + + if(printed) { + System.out.println(" ____________________________________________________________"); continue; } - if(list.contains(input)) { - System.out.println(input); + if(list.contains(task)) { + System.out.println(task); } else { - list.add(input); - System.out.println("added: " + input); + list.add(task); + System.out.println("added: " + in); } System.out.println(" ____________________________________________________________"); } } - public static void list(ArrayList lst) { + public static void list(ArrayList lst) { for(int i=0; i < lst.size(); i++) { System.out.printf("%d. %s\n", i + 1, lst.get(i)); } @@ -51,4 +70,4 @@ public static String bye() { return " Bye. Hope to see you again soon!"; } -} +} \ No newline at end of file From 666bf39b2e13877c66d83aeb8d67a8bd95815701 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Thu, 20 Jan 2022 23:05:51 +0800 Subject: [PATCH 06/47] Level-4 Added enum and additional functions --- src/main/java/Duke.java | 93 +++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 32 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index ae448d825a..47a7ce4530 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,6 +1,17 @@ import java.util.*; public class Duke { + private enum Command { + BYE, + LIST, + MARK, + UNMARK, + TODO, + DEADLINE, + EVENT + ; + } + public static void main(String[] args) { String logo = " ____ _ \n" + "| _ \\ _ _| | _____ \n" @@ -17,40 +28,58 @@ public static void main(String[] args) { ArrayList list = new ArrayList(100); while(StillIn) { String in = fr.nextLine(); - String[] input = in.split(" "); - Task task = new Task(in); - boolean printed = false; + String[] input = in.split(" ", 2); + Command cmd = Command.valueOf(input[0].toUpperCase()); System.out.println(" ____________________________________________________________"); - if(input[0].equals("bye")) { - System.out.println(bye()); - StillIn = false; - printed = true; - } else if(input[0].equals("list")) { - list(list); - printed = true; - } else if(input[0].equals("mark")) { - int num = Integer.parseInt(input[1]); - list.get(num - 1).setDone(); - System.out.println("Nice! I've marked this task as done:"); - System.out.println("[X] " + list.get(num - 1).getTask()); - printed = true; - } else if(input[0].equals("unmark")) { - int num = Integer.parseInt(input[1]); - list.get(num - 1).setUndone(); - System.out.println("OK, I've marked this task as not done yet:"); - System.out.println("[ ] " + list.get(num - 1).getTask()); - } + switch(cmd) { + case BYE: + System.out.println(bye()); + StillIn = false; + break; - if(printed) { - System.out.println(" ____________________________________________________________"); - continue; - } + case LIST: + list(list); + break; + + case MARK: + int numMark = Integer.parseInt(input[1]); + list.get(numMark - 1).setDone(); + System.out.println(" Nice! I've marked this task as done:"); + System.out.println(list.get(numMark - 1)); + break; + + case UNMARK: + int numUnmark = Integer.parseInt(input[1]); + list.get(numUnmark - 1).setUndone(); + System.out.println(" OK, I've marked this task as not done yet:"); + System.out.println(list.get(numUnmark - 1)); + break; + + case TODO: + Task todo = new ToDo(input[1]); + list.add(todo); + System.out.println(" Got it. I've added this task:"); + System.out.println(todo); + System.out.printf(" Now you have %d task(s) in the list\n", list.size()); + break; + + case DEADLINE: + String bydate = input[1].split("/by ")[1]; + Task deadline = new Deadline(input[1], bydate); + list.add(deadline); + System.out.println(" Got it. I've added this task:"); + System.out.println(deadline); + System.out.printf(" Now you have %d task(s) in the list\n", list.size()); + break; - if(list.contains(task)) { - System.out.println(task); - } else { - list.add(task); - System.out.println("added: " + in); + case EVENT: + String atdate = input[1].split("/at ")[1]; + Task event = new Event(input[1], atdate); + list.add(event); + System.out.println(" Got it. I've added this task:"); + System.out.println(event); + System.out.printf(" Now you have %d task(s) in the list\n", list.size()); + break; } System.out.println(" ____________________________________________________________"); } @@ -58,7 +87,7 @@ public static void main(String[] args) { public static void list(ArrayList lst) { for(int i=0; i < lst.size(); i++) { - System.out.printf("%d. %s\n", i + 1, lst.get(i)); + System.out.printf(" %d. %s\n", i + 1, lst.get(i)); } } From 3e42bcc29a27540e79d29dc2cd0ae486fc3aa860 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Thu, 20 Jan 2022 23:06:56 +0800 Subject: [PATCH 07/47] added a default constructor --- src/main/java/Task.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 1ba66904b2..381e41a99c 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -2,6 +2,10 @@ public class Task { private String task; private boolean done; + public Task() { + this.done = false; + } + public Task(String task) { this.task = task; this.done = false; From 2623e50f8de43976c47b420eea6a821eb309fe69 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Thu, 20 Jan 2022 23:07:15 +0800 Subject: [PATCH 08/47] Added Deadline class --- src/main/java/Deadline.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/java/Deadline.java diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java new file mode 100644 index 0000000000..1dc020cacd --- /dev/null +++ b/src/main/java/Deadline.java @@ -0,0 +1,13 @@ +public class Deadline extends Task { + private String by; + + public Deadline(String task, String by) { + super(task); + this.by = by; + } + + @Override + public String toString() { + return " [D]" + super.toString() + " (by: " + by + ")"; + } +} From 2f2eae3c0170b973ff424c1c0777114b5b83f28e Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Thu, 20 Jan 2022 23:07:24 +0800 Subject: [PATCH 09/47] added Event class --- src/main/java/Event.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/java/Event.java diff --git a/src/main/java/Event.java b/src/main/java/Event.java new file mode 100644 index 0000000000..4686e727ff --- /dev/null +++ b/src/main/java/Event.java @@ -0,0 +1,13 @@ +public class Event extends Task { + private String at; + + public Event(String task, String at) { + super(task); + this.at = at; + } + + @Override + public String toString() { + return " [E]" + super.toString() + " (at: " + at + ")"; + } +} From c6c76f93476c59e4be82aebfedfebe49c11567a3 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Thu, 20 Jan 2022 23:07:31 +0800 Subject: [PATCH 10/47] added ToDo class --- src/main/java/ToDo.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/java/ToDo.java diff --git a/src/main/java/ToDo.java b/src/main/java/ToDo.java new file mode 100644 index 0000000000..943c1df72e --- /dev/null +++ b/src/main/java/ToDo.java @@ -0,0 +1,10 @@ +public class ToDo extends Task { + public ToDo(String task) { + super(task); + } + + @Override + public String toString() { + return " [T]" + super.toString(); + } +} From 7253df224f8cbcae869670bf1b1ae55f38981c55 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Sat, 22 Jan 2022 09:50:43 +0800 Subject: [PATCH 11/47] Remove spaces on toString() method Fix display --- src/main/java/Deadline.java | 2 +- src/main/java/Event.java | 2 +- src/main/java/ToDo.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 1dc020cacd..05b0579037 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -8,6 +8,6 @@ public Deadline(String task, String by) { @Override public String toString() { - return " [D]" + super.toString() + " (by: " + by + ")"; + return "[D]" + super.toString() + " (by: " + by + ")"; } } diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 4686e727ff..04e1fd3366 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -8,6 +8,6 @@ public Event(String task, String at) { @Override public String toString() { - return " [E]" + super.toString() + " (at: " + at + ")"; + return "[E]" + super.toString() + " (at: " + at + ")"; } } diff --git a/src/main/java/ToDo.java b/src/main/java/ToDo.java index 943c1df72e..a22b5bdca6 100644 --- a/src/main/java/ToDo.java +++ b/src/main/java/ToDo.java @@ -5,6 +5,6 @@ public ToDo(String task) { @Override public String toString() { - return " [T]" + super.toString(); + return "[T]" + super.toString(); } } From cd2cec9ff7b7c86fb813ee63719bd30c2486f627 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Sat, 22 Jan 2022 09:53:57 +0800 Subject: [PATCH 12/47] Update accessor of enum, add error handling, and change display. Change the enum accessor so that it is accessible. Add error handling to prevent program from terminating due to illegal user input. Change display to look neater. --- src/main/java/Duke.java | 149 ++++++++++++++++++++++++++-------------- 1 file changed, 98 insertions(+), 51 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 47a7ce4530..37192eac8f 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,7 +1,7 @@ import java.util.*; public class Duke { - private enum Command { + enum Command { BYE, LIST, MARK, @@ -31,55 +31,96 @@ public static void main(String[] args) { String[] input = in.split(" ", 2); Command cmd = Command.valueOf(input[0].toUpperCase()); System.out.println(" ____________________________________________________________"); - switch(cmd) { - case BYE: - System.out.println(bye()); - StillIn = false; - break; - - case LIST: - list(list); - break; - - case MARK: - int numMark = Integer.parseInt(input[1]); - list.get(numMark - 1).setDone(); - System.out.println(" Nice! I've marked this task as done:"); - System.out.println(list.get(numMark - 1)); - break; - - case UNMARK: - int numUnmark = Integer.parseInt(input[1]); - list.get(numUnmark - 1).setUndone(); - System.out.println(" OK, I've marked this task as not done yet:"); - System.out.println(list.get(numUnmark - 1)); - break; - - case TODO: - Task todo = new ToDo(input[1]); - list.add(todo); - System.out.println(" Got it. I've added this task:"); - System.out.println(todo); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); - break; - - case DEADLINE: - String bydate = input[1].split("/by ")[1]; - Task deadline = new Deadline(input[1], bydate); - list.add(deadline); - System.out.println(" Got it. I've added this task:"); - System.out.println(deadline); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); - break; - - case EVENT: - String atdate = input[1].split("/at ")[1]; - Task event = new Event(input[1], atdate); - list.add(event); - System.out.println(" Got it. I've added this task:"); - System.out.println(event); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); - break; + try { + switch (cmd) { + case BYE: + System.out.println(bye()); + StillIn = false; + break; + + case LIST: + list(list); + break; + + case MARK: + if(input.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know what to mark"); + } + + int numMark = Integer.parseInt(input[1]); + + if(numMark > list.size()) { + throw new DukeException(" ☹ OOPS!!! I don't see your task"); + } + + list.get(numMark - 1).setDone(); + System.out.println(" Nice! I've marked this task as done:"); + System.out.println(" " + list.get(numMark - 1)); + break; + + case UNMARK: + if(input.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know what to unmark"); + } + + int numUnmark = Integer.parseInt(input[1]); + + if(numUnmark > list.size()) { + throw new DukeException(" ☹ OOPS!!! I don't see your task"); + } + + list.get(numUnmark - 1).setUndone(); + System.out.println(" OK, I've marked this task as not done yet:"); + System.out.println(" " + list.get(numUnmark - 1)); + break; + + case TODO: + if(input.length < 2) { + throw new DukeException(" ☹ OOPS!!! The description of a todo cannot be empty"); + } + Task todo = new ToDo(input[1]); + list.add(todo); + System.out.println(" Got it. I've added this task:"); + System.out.println(" " + todo); + System.out.printf(" Now you have %d task(s) in the list\n", list.size()); + break; + + case DEADLINE: + if(input.length < 2) { + throw new DukeException(" ☹ OOPS!!! The description of a deadline cannot be empty"); + } + String[] splitbydate = input[1].split(" /by "); + if(splitbydate.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know when your task is due"); + } + String bydate = splitbydate[1]; + Task deadline = new Deadline(splitbydate[0], bydate); + list.add(deadline); + System.out.println(" Got it. I've added this task:"); + System.out.println(" " + deadline); + System.out.printf(" Now you have %d task(s) in the list\n", list.size()); + break; + + case EVENT: + if(input.length < 2) { + throw new DukeException(" ☹ OOPS!!! The description of an event cannot be empty"); + } + String[] splitatdate = input[1].split(" /at "); + if(splitatdate.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know when your event happens"); + } + String atdate = splitatdate[1]; + Task event = new Event(splitatdate[0], atdate); + list.add(event); + System.out.println(" Got it. I've added this task:"); + System.out.println(" " + event); + System.out.printf(" Now you have %d task(s) in the list\n", list.size()); + break; + } + } catch(DukeException err) { + System.out.println(err.getMessage()); + } catch(NumberFormatException err) { + System.out.println(" ☹ OOPS!!! Task number given is not suitable"); } System.out.println(" ____________________________________________________________"); } @@ -87,7 +128,13 @@ public static void main(String[] args) { public static void list(ArrayList lst) { for(int i=0; i < lst.size(); i++) { - System.out.printf(" %d. %s\n", i + 1, lst.get(i)); + if(i == 0) { + System.out.println(" Here are the tasks in your list:"); + } + System.out.printf(" %d. %s\n", i + 1, lst.get(i)); + } + if(lst.size() == 0) { + System.out.println(" There's nothing in your list"); } } From 074b3d56e1f9341fb617e073974634950b72126c Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Sat, 22 Jan 2022 09:55:08 +0800 Subject: [PATCH 13/47] Add test. Add a semi-automated test to check correctness of code and reduce human error. --- text-ui-test/EXPECTED.TXT | 90 +++++++++++++++++++++++++++++++++++++++ text-ui-test/input.txt | 21 +++++++++ 2 files changed, 111 insertions(+) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e7..42b90c6e19 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -5,3 +5,93 @@ Hello from | |_| | |_| | < __/ |____/ \__,_|_|\_\___| + ____________________________________________________________ + Hello! I'm Duke + What can I do for you? + ____________________________________________________________ + ____________________________________________________________ + There's nothing in your list + ____________________________________________________________ + ____________________________________________________________ + ☹ OOPS!!! The description of a todo cannot be empty + ____________________________________________________________ + ____________________________________________________________ + Got it. I've added this task: + [T][ ] read book + Now you have 1 task(s) in the list + ____________________________________________________________ + ____________________________________________________________ + ☹ OOPS!!! The description of a deadline cannot be empty + ____________________________________________________________ + ____________________________________________________________ + ☹ OOPS!!! I don't know when your task is due + ____________________________________________________________ + ____________________________________________________________ + Got it. I've added this task: + [D][ ] return book (by: June 6th) + Now you have 2 task(s) in the list + ____________________________________________________________ + ____________________________________________________________ + ☹ OOPS!!! The description of an event cannot be empty + ____________________________________________________________ + ____________________________________________________________ + ☹ OOPS!!! I don't know when your event happens + ____________________________________________________________ + ____________________________________________________________ + Got it. I've added this task: + [E][ ] project meeting (at: Aug 6th 2-4pm) + Now you have 3 task(s) in the list + ____________________________________________________________ + ____________________________________________________________ + Got it. I've added this task: + [T][ ] join sports club + Now you have 4 task(s) in the list + ____________________________________________________________ + ____________________________________________________________ + Got it. I've added this task: + [T][ ] borrow book + Now you have 5 task(s) in the list + ____________________________________________________________ + ____________________________________________________________ + ☹ OOPS!!! I don't know what to mark + ____________________________________________________________ + ____________________________________________________________ + ☹ OOPS!!! I don't see your task + ____________________________________________________________ + ____________________________________________________________ + ☹ OOPS!!! Task number given is not suitable + ____________________________________________________________ + ____________________________________________________________ + Nice! I've marked this task as done: + [T][X] read book + ____________________________________________________________ + ____________________________________________________________ + Nice! I've marked this task as done: + [T][X] join sports club + ____________________________________________________________ + ____________________________________________________________ + Here are the tasks in your list: + 1. [T][X] read book + 2. [D][ ] return book (by: June 6th) + 3. [E][ ] project meeting (at: Aug 6th 2-4pm) + 4. [T][X] join sports club + 5. [T][ ] borrow book + ____________________________________________________________ + ____________________________________________________________ + Got it. I've added this task: + [D][ ] return book (by: Sunday) + Now you have 6 task(s) in the list + ____________________________________________________________ + ____________________________________________________________ + Got it. I've added this task: + [E][ ] project meeting (at: Mon 2-4pm) + Now you have 7 task(s) in the list + ____________________________________________________________ + ____________________________________________________________ + Got it. I've added this task: + [D][ ] do homework (by: no idea :-p) + Now you have 8 task(s) in the list + ____________________________________________________________ + ____________________________________________________________ + Bye. Hope to see you again soon! + ____________________________________________________________ \ No newline at end of file diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb2..d2a5739478 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,21 @@ +list +todo +todo read book +deadline +deadline return book +deadline return book /by June 6th +event +event project meeting +event project meeting /at Aug 6th 2-4pm +todo join sports club +todo borrow book +mark +mark 6 +mark a +mark 1 +mark 4 +list +deadline return book /by Sunday +event project meeting /at Mon 2-4pm +deadline do homework /by no idea :-p +bye From 220ea0db039ba747db71d7a890e15c20bf7d0c2d Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Sat, 22 Jan 2022 09:55:47 +0800 Subject: [PATCH 14/47] Add DukeException class Add Exception class to deal with exceptions unique to program. --- src/main/java/DukeException.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/main/java/DukeException.java diff --git a/src/main/java/DukeException.java b/src/main/java/DukeException.java new file mode 100644 index 0000000000..fe8837e3a3 --- /dev/null +++ b/src/main/java/DukeException.java @@ -0,0 +1,5 @@ +public class DukeException extends Exception { + public DukeException(String message) { + super(message); + } +} From 575afdb4a5f152332b56541f3bb7bdf04b52d903 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Sat, 22 Jan 2022 13:21:15 +0800 Subject: [PATCH 15/47] Add delete feature Add delete feature to remove a task from the list. --- src/main/java/Duke.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 37192eac8f..76fedd9829 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -8,7 +8,8 @@ enum Command { UNMARK, TODO, DEADLINE, - EVENT + EVENT, + DELETE ; } @@ -116,6 +117,22 @@ public static void main(String[] args) { System.out.println(" " + event); System.out.printf(" Now you have %d task(s) in the list\n", list.size()); break; + + case DELETE: + if(input.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know what to delete"); + } + + int removenum = Integer.parseInt(input[1]); + + if(removenum > list.size()) { + throw new DukeException(" ☹ OOPS!!! I don't see your task"); + } + + Task removedtask = list.remove(removenum - 1); + System.out.println(" Noted. I've removed this task:"); + System.out.println(" " + removedtask); + System.out.printf(" Now you have %d task(s) in the list\n", list.size()); } } catch(DukeException err) { System.out.println(err.getMessage()); From 53d349013307e423a78ea986e71a6f4be008b081 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Sat, 22 Jan 2022 13:22:19 +0800 Subject: [PATCH 16/47] Add test cases for delete feature Add test cases for delete feature to ensure correct display an exception handling --- text-ui-test/EXPECTED.TXT | 34 ++++++++++++++++++++++++++++++++++ text-ui-test/input.txt | 7 +++++++ 2 files changed, 41 insertions(+) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 42b90c6e19..a6e87d6d6f 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -93,5 +93,39 @@ Hello from Now you have 8 task(s) in the list ____________________________________________________________ ____________________________________________________________ + ☹ OOPS!!! I don't know what to delete + ____________________________________________________________ + ____________________________________________________________ + ☹ OOPS!!! I don't see your task + ____________________________________________________________ + ____________________________________________________________ + Noted. I've removed this task: + [D][ ] do homework (by: no idea :-p) + Now you have 7 task(s) in the list + ____________________________________________________________ + ____________________________________________________________ + Noted. I've removed this task: + [E][ ] project meeting (at: Mon 2-4pm) + Now you have 6 task(s) in the list + ____________________________________________________________ + ____________________________________________________________ + Noted. I've removed this task: + [D][ ] return book (by: Sunday) + Now you have 5 task(s) in the list + ____________________________________________________________ + ____________________________________________________________ + Here are the tasks in your list: + 1. [T][X] read book + 2. [D][ ] return book (by: June 6th) + 3. [E][ ] project meeting (at: Aug 6th 2-4pm) + 4. [T][X] join sports club + 5. [T][ ] borrow book + ____________________________________________________________ + ____________________________________________________________ + Noted. I've removed this task: + [E][ ] project meeting (at: Aug 6th 2-4pm) + Now you have 4 task(s) in the list + ____________________________________________________________ + ____________________________________________________________ Bye. Hope to see you again soon! ____________________________________________________________ \ No newline at end of file diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index d2a5739478..41ee8df05f 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -18,4 +18,11 @@ list deadline return book /by Sunday event project meeting /at Mon 2-4pm deadline do homework /by no idea :-p +delete +delete 9 +delete 8 +delete 7 +delete 6 +list +delete 3 bye From d355645f3ba732b496733fc2e9bac5066d1bbf13 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Mon, 31 Jan 2022 21:46:47 +0800 Subject: [PATCH 17/47] Add different display method for tasks for record purposes Add toRecord method for save method to write in a different display from the output on console --- src/main/java/Deadline.java | 5 + src/main/java/Duke.java | 295 ++++++++++++++++++++++-------------- src/main/java/Event.java | 5 + src/main/java/Task.java | 26 +++- 4 files changed, 211 insertions(+), 120 deletions(-) diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 05b0579037..02a2a9fb37 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -10,4 +10,9 @@ public Deadline(String task, String by) { public String toString() { return "[D]" + super.toString() + " (by: " + by + ")"; } + + @Override + public String toRecord() { + return "D | " + super.toRecord() + " | " + by; + } } diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 76fedd9829..d33c7d9bbe 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,4 +1,12 @@ -import java.util.*; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import java.util.ArrayList; public class Duke { enum Command { @@ -10,7 +18,6 @@ enum Command { DEADLINE, EVENT, DELETE - ; } public static void main(String[] args) { @@ -24,133 +31,70 @@ public static void main(String[] args) { System.out.println(" Hello! I'm Duke"); System.out.println(" What can I do for you?"); System.out.println(" ____________________________________________________________"); - boolean StillIn = true; + boolean isStillIn = true; FastReader fr = new FastReader(); - ArrayList list = new ArrayList(100); - while(StillIn) { + ArrayList tasks = new ArrayList<>(100); + + while (isStillIn) { String in = fr.nextLine(); - String[] input = in.split(" ", 2); - Command cmd = Command.valueOf(input[0].toUpperCase()); + String[] inputs = in.split(" ", 2); + Command cmd = Command.valueOf(inputs[0].toUpperCase()); System.out.println(" ____________________________________________________________"); + try { switch (cmd) { - case BYE: - System.out.println(bye()); - StillIn = false; - break; - - case LIST: - list(list); - break; - - case MARK: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know what to mark"); - } - - int numMark = Integer.parseInt(input[1]); - - if(numMark > list.size()) { - throw new DukeException(" ☹ OOPS!!! I don't see your task"); - } - - list.get(numMark - 1).setDone(); - System.out.println(" Nice! I've marked this task as done:"); - System.out.println(" " + list.get(numMark - 1)); - break; - - case UNMARK: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know what to unmark"); - } - - int numUnmark = Integer.parseInt(input[1]); - - if(numUnmark > list.size()) { - throw new DukeException(" ☹ OOPS!!! I don't see your task"); - } - - list.get(numUnmark - 1).setUndone(); - System.out.println(" OK, I've marked this task as not done yet:"); - System.out.println(" " + list.get(numUnmark - 1)); - break; - - case TODO: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! The description of a todo cannot be empty"); - } - Task todo = new ToDo(input[1]); - list.add(todo); - System.out.println(" Got it. I've added this task:"); - System.out.println(" " + todo); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); - break; - - case DEADLINE: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! The description of a deadline cannot be empty"); - } - String[] splitbydate = input[1].split(" /by "); - if(splitbydate.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know when your task is due"); - } - String bydate = splitbydate[1]; - Task deadline = new Deadline(splitbydate[0], bydate); - list.add(deadline); - System.out.println(" Got it. I've added this task:"); - System.out.println(" " + deadline); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); - break; - - case EVENT: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! The description of an event cannot be empty"); - } - String[] splitatdate = input[1].split(" /at "); - if(splitatdate.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know when your event happens"); - } - String atdate = splitatdate[1]; - Task event = new Event(splitatdate[0], atdate); - list.add(event); - System.out.println(" Got it. I've added this task:"); - System.out.println(" " + event); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); - break; - - case DELETE: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know what to delete"); - } - - int removenum = Integer.parseInt(input[1]); - - if(removenum > list.size()) { - throw new DukeException(" ☹ OOPS!!! I don't see your task"); - } - - Task removedtask = list.remove(removenum - 1); - System.out.println(" Noted. I've removed this task:"); - System.out.println(" " + removedtask); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); + case BYE: + System.out.println(bye()); + isStillIn = false; + break; + case LIST: + list(tasks); + break; + case MARK: + mark(tasks, inputs); + save(tasks); + break; + case UNMARK: + unmark(tasks, inputs); + save(tasks); + break; + case TODO: + displayToDo(tasks, inputs); + save(tasks); + break; + case DEADLINE: + displayDeadline(tasks, inputs); + save(tasks); + break; + case EVENT: + displayEvent(tasks, inputs); + save(tasks); + break; + case DELETE: + delete(tasks, inputs); + save(tasks); + break; } - } catch(DukeException err) { + } catch (DukeException err) { System.out.println(err.getMessage()); - } catch(NumberFormatException err) { + } catch (NumberFormatException err) { System.out.println(" ☹ OOPS!!! Task number given is not suitable"); } + System.out.println(" ____________________________________________________________"); } } - public static void list(ArrayList lst) { - for(int i=0; i < lst.size(); i++) { - if(i == 0) { + public static void list(ArrayList tasks) { + for (int i = 0; i < tasks.size(); i++) { + if (i == 0) { System.out.println(" Here are the tasks in your list:"); } - System.out.printf(" %d. %s\n", i + 1, lst.get(i)); + + System.out.printf(" %d. %s\n", i + 1, tasks.get(i)); } - if(lst.size() == 0) { + + if (tasks.size() == 0) { System.out.println(" There's nothing in your list"); } } @@ -161,6 +105,131 @@ public static String blah() { public static String bye() { return " Bye. Hope to see you again soon!"; + } + + public static void mark(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know what to mark"); + } + + int numMark = Integer.parseInt(inputs[1]); + + if (numMark > tasks.size()) { + throw new DukeException(" ☹ OOPS!!! I don't see your task"); + } + + tasks.get(numMark - 1).setDone(); + System.out.println(" Nice! I've marked this task as done:"); + System.out.println(" " + tasks.get(numMark - 1)); + } + + public static void unmark(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know what to unmark"); + } + + int numUnmark = Integer.parseInt(inputs[1]); + + if (numUnmark > tasks.size()) { + throw new DukeException(" ☹ OOPS!!! I don't see your task"); + } + + tasks.get(numUnmark - 1).setUndone(); + System.out.println(" OK, I've marked this task as not done yet:"); + System.out.println(" " + tasks.get(numUnmark - 1)); + } + + public static void displayToDo(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(" ☹ OOPS!!! The description of a todo cannot be empty"); + } + + Task toDo = new ToDo(inputs[1]); + tasks.add(toDo); + System.out.println(" Got it. I've added this task:"); + System.out.println(" " + toDo); + System.out.printf(" Now you have %d task(s) in the list\n", tasks.size()); + } + + public static void displayDeadline(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(" ☹ OOPS!!! The description of a deadline cannot be empty"); + } + + String[] splitByDate = inputs[1].split(" /by "); + + if (splitByDate.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know when your task is due"); + } + + String byDate = splitByDate[1]; + Task deadline = new Deadline(splitByDate[0], byDate); + tasks.add(deadline); + System.out.println(" Got it. I've added this task:"); + System.out.println(" " + deadline); + System.out.printf(" Now you have %d task(s) in the list\n", tasks.size()); + } + + public static void displayEvent(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(" ☹ OOPS!!! The description of an event cannot be empty"); + } + + String[] splitAtDate = inputs[1].split(" /at "); + + if (splitAtDate.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know when your event happens"); + } + String atDate = splitAtDate[1]; + Task event = new Event(splitAtDate[0], atDate); + tasks.add(event); + System.out.println(" Got it. I've added this task:"); + System.out.println(" " + event); + System.out.printf(" Now you have %d task(s) in the list\n", tasks.size()); + } + + public static void delete(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know what to delete"); + } + + int removeNum = Integer.parseInt(inputs[1]); + + if (removeNum > tasks.size()) { + throw new DukeException(" ☹ OOPS!!! I don't see your task"); + } + + Task removedTask = tasks.remove(removeNum - 1); + System.out.println(" Noted. I've removed this task:"); + System.out.println(" " + removedTask); + System.out.printf(" Now you have %d task(s) in the list\n", tasks.size()); + } + + public static void save(ArrayList tasks) { + String working = System.getProperty("user.dir"); + Path path = Paths.get(working, "data"); + path.toFile().mkdirs(); + File file = new File(path + "/duke.txt"); + + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(file)); + StringBuilder output = new StringBuilder(); + + for (int i = 0; i < tasks.size(); i++) { + output.append(tasks.get(0).toRecord()).append("\n"); + } + + writer.write(output.toString()); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } } } \ No newline at end of file diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 04e1fd3366..560e2a8737 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -10,4 +10,9 @@ public Event(String task, String at) { public String toString() { return "[E]" + super.toString() + " (at: " + at + ")"; } + + @Override + public String toRecord() { + return "E | " + super.toRecord() + " | " + at; + } } diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 381e41a99c..88deef6912 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -1,14 +1,14 @@ public class Task { private String task; - private boolean done; + private boolean isDone; public Task() { - this.done = false; + this.isDone = false; } public Task(String task) { this.task = task; - this.done = false; + this.isDone = false; } public String getTask() { @@ -16,11 +16,11 @@ public String getTask() { } public void setDone() { - this.done = true; + this.isDone = true; } public void setUndone() { - this.done = false; + this.isDone = false; } @Override @@ -30,7 +30,7 @@ public int hashCode() { @Override public boolean equals(Object other) { - if(other instanceof Task){ + if (other instanceof Task){ Task toCompare = (Task) other; return this.task.equals(toCompare.getTask()); } @@ -40,11 +40,23 @@ public boolean equals(Object other) { @Override public String toString() { String temp; - if(this.done) { + if (this.isDone) { temp = "X"; } else { temp = " "; } return "[" + temp + "] " + this.task; } + + public String toRecord() { + int temp; + + if (this.isDone) { + temp = 1; + } else { + temp = 0; + } + + return temp + " | " + this.task; + } } From fb0bb088399275a008826f61fab65f3cae8cdd8d Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Mon, 31 Jan 2022 21:49:40 +0800 Subject: [PATCH 18/47] Revert "Add different display method for tasks for record purposes" This reverts commit d355645f3ba732b496733fc2e9bac5066d1bbf13. --- src/main/java/Deadline.java | 5 - src/main/java/Duke.java | 295 ++++++++++++++---------------------- src/main/java/Event.java | 5 - src/main/java/Task.java | 26 +--- 4 files changed, 120 insertions(+), 211 deletions(-) diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 02a2a9fb37..05b0579037 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -10,9 +10,4 @@ public Deadline(String task, String by) { public String toString() { return "[D]" + super.toString() + " (by: " + by + ")"; } - - @Override - public String toRecord() { - return "D | " + super.toRecord() + " | " + by; - } } diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index d33c7d9bbe..76fedd9829 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,12 +1,4 @@ -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import java.util.ArrayList; +import java.util.*; public class Duke { enum Command { @@ -18,6 +10,7 @@ enum Command { DEADLINE, EVENT, DELETE + ; } public static void main(String[] args) { @@ -31,70 +24,133 @@ public static void main(String[] args) { System.out.println(" Hello! I'm Duke"); System.out.println(" What can I do for you?"); System.out.println(" ____________________________________________________________"); - boolean isStillIn = true; + boolean StillIn = true; FastReader fr = new FastReader(); - ArrayList tasks = new ArrayList<>(100); - - while (isStillIn) { + ArrayList list = new ArrayList(100); + while(StillIn) { String in = fr.nextLine(); - String[] inputs = in.split(" ", 2); - Command cmd = Command.valueOf(inputs[0].toUpperCase()); + String[] input = in.split(" ", 2); + Command cmd = Command.valueOf(input[0].toUpperCase()); System.out.println(" ____________________________________________________________"); - try { switch (cmd) { - case BYE: - System.out.println(bye()); - isStillIn = false; - break; - case LIST: - list(tasks); - break; - case MARK: - mark(tasks, inputs); - save(tasks); - break; - case UNMARK: - unmark(tasks, inputs); - save(tasks); - break; - case TODO: - displayToDo(tasks, inputs); - save(tasks); - break; - case DEADLINE: - displayDeadline(tasks, inputs); - save(tasks); - break; - case EVENT: - displayEvent(tasks, inputs); - save(tasks); - break; - case DELETE: - delete(tasks, inputs); - save(tasks); - break; + case BYE: + System.out.println(bye()); + StillIn = false; + break; + + case LIST: + list(list); + break; + + case MARK: + if(input.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know what to mark"); + } + + int numMark = Integer.parseInt(input[1]); + + if(numMark > list.size()) { + throw new DukeException(" ☹ OOPS!!! I don't see your task"); + } + + list.get(numMark - 1).setDone(); + System.out.println(" Nice! I've marked this task as done:"); + System.out.println(" " + list.get(numMark - 1)); + break; + + case UNMARK: + if(input.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know what to unmark"); + } + + int numUnmark = Integer.parseInt(input[1]); + + if(numUnmark > list.size()) { + throw new DukeException(" ☹ OOPS!!! I don't see your task"); + } + + list.get(numUnmark - 1).setUndone(); + System.out.println(" OK, I've marked this task as not done yet:"); + System.out.println(" " + list.get(numUnmark - 1)); + break; + + case TODO: + if(input.length < 2) { + throw new DukeException(" ☹ OOPS!!! The description of a todo cannot be empty"); + } + Task todo = new ToDo(input[1]); + list.add(todo); + System.out.println(" Got it. I've added this task:"); + System.out.println(" " + todo); + System.out.printf(" Now you have %d task(s) in the list\n", list.size()); + break; + + case DEADLINE: + if(input.length < 2) { + throw new DukeException(" ☹ OOPS!!! The description of a deadline cannot be empty"); + } + String[] splitbydate = input[1].split(" /by "); + if(splitbydate.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know when your task is due"); + } + String bydate = splitbydate[1]; + Task deadline = new Deadline(splitbydate[0], bydate); + list.add(deadline); + System.out.println(" Got it. I've added this task:"); + System.out.println(" " + deadline); + System.out.printf(" Now you have %d task(s) in the list\n", list.size()); + break; + + case EVENT: + if(input.length < 2) { + throw new DukeException(" ☹ OOPS!!! The description of an event cannot be empty"); + } + String[] splitatdate = input[1].split(" /at "); + if(splitatdate.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know when your event happens"); + } + String atdate = splitatdate[1]; + Task event = new Event(splitatdate[0], atdate); + list.add(event); + System.out.println(" Got it. I've added this task:"); + System.out.println(" " + event); + System.out.printf(" Now you have %d task(s) in the list\n", list.size()); + break; + + case DELETE: + if(input.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know what to delete"); + } + + int removenum = Integer.parseInt(input[1]); + + if(removenum > list.size()) { + throw new DukeException(" ☹ OOPS!!! I don't see your task"); + } + + Task removedtask = list.remove(removenum - 1); + System.out.println(" Noted. I've removed this task:"); + System.out.println(" " + removedtask); + System.out.printf(" Now you have %d task(s) in the list\n", list.size()); } - } catch (DukeException err) { + } catch(DukeException err) { System.out.println(err.getMessage()); - } catch (NumberFormatException err) { + } catch(NumberFormatException err) { System.out.println(" ☹ OOPS!!! Task number given is not suitable"); } - System.out.println(" ____________________________________________________________"); } } - public static void list(ArrayList tasks) { - for (int i = 0; i < tasks.size(); i++) { - if (i == 0) { + public static void list(ArrayList lst) { + for(int i=0; i < lst.size(); i++) { + if(i == 0) { System.out.println(" Here are the tasks in your list:"); } - - System.out.printf(" %d. %s\n", i + 1, tasks.get(i)); + System.out.printf(" %d. %s\n", i + 1, lst.get(i)); } - - if (tasks.size() == 0) { + if(lst.size() == 0) { System.out.println(" There's nothing in your list"); } } @@ -105,131 +161,6 @@ public static String blah() { public static String bye() { return " Bye. Hope to see you again soon!"; - } - - public static void mark(ArrayList tasks, String[] inputs) throws DukeException { - if (inputs.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know what to mark"); - } - - int numMark = Integer.parseInt(inputs[1]); - - if (numMark > tasks.size()) { - throw new DukeException(" ☹ OOPS!!! I don't see your task"); - } - - tasks.get(numMark - 1).setDone(); - System.out.println(" Nice! I've marked this task as done:"); - System.out.println(" " + tasks.get(numMark - 1)); - } - - public static void unmark(ArrayList tasks, String[] inputs) throws DukeException { - if (inputs.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know what to unmark"); - } - - int numUnmark = Integer.parseInt(inputs[1]); - - if (numUnmark > tasks.size()) { - throw new DukeException(" ☹ OOPS!!! I don't see your task"); - } - - tasks.get(numUnmark - 1).setUndone(); - System.out.println(" OK, I've marked this task as not done yet:"); - System.out.println(" " + tasks.get(numUnmark - 1)); - } - - public static void displayToDo(ArrayList tasks, String[] inputs) throws DukeException { - if (inputs.length < 2) { - throw new DukeException(" ☹ OOPS!!! The description of a todo cannot be empty"); - } - - Task toDo = new ToDo(inputs[1]); - tasks.add(toDo); - System.out.println(" Got it. I've added this task:"); - System.out.println(" " + toDo); - System.out.printf(" Now you have %d task(s) in the list\n", tasks.size()); - } - - public static void displayDeadline(ArrayList tasks, String[] inputs) throws DukeException { - if (inputs.length < 2) { - throw new DukeException(" ☹ OOPS!!! The description of a deadline cannot be empty"); - } - - String[] splitByDate = inputs[1].split(" /by "); - - if (splitByDate.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know when your task is due"); - } - - String byDate = splitByDate[1]; - Task deadline = new Deadline(splitByDate[0], byDate); - tasks.add(deadline); - System.out.println(" Got it. I've added this task:"); - System.out.println(" " + deadline); - System.out.printf(" Now you have %d task(s) in the list\n", tasks.size()); - } - - public static void displayEvent(ArrayList tasks, String[] inputs) throws DukeException { - if (inputs.length < 2) { - throw new DukeException(" ☹ OOPS!!! The description of an event cannot be empty"); - } - - String[] splitAtDate = inputs[1].split(" /at "); - - if (splitAtDate.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know when your event happens"); - } - String atDate = splitAtDate[1]; - Task event = new Event(splitAtDate[0], atDate); - tasks.add(event); - System.out.println(" Got it. I've added this task:"); - System.out.println(" " + event); - System.out.printf(" Now you have %d task(s) in the list\n", tasks.size()); - } - - public static void delete(ArrayList tasks, String[] inputs) throws DukeException { - if (inputs.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know what to delete"); - } - - int removeNum = Integer.parseInt(inputs[1]); - - if (removeNum > tasks.size()) { - throw new DukeException(" ☹ OOPS!!! I don't see your task"); - } - - Task removedTask = tasks.remove(removeNum - 1); - System.out.println(" Noted. I've removed this task:"); - System.out.println(" " + removedTask); - System.out.printf(" Now you have %d task(s) in the list\n", tasks.size()); - } - - public static void save(ArrayList tasks) { - String working = System.getProperty("user.dir"); - Path path = Paths.get(working, "data"); - path.toFile().mkdirs(); - File file = new File(path + "/duke.txt"); - - try { - file.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - - try { - BufferedWriter writer = new BufferedWriter(new FileWriter(file)); - StringBuilder output = new StringBuilder(); - - for (int i = 0; i < tasks.size(); i++) { - output.append(tasks.get(0).toRecord()).append("\n"); - } - - writer.write(output.toString()); - writer.close(); - } catch (IOException e) { - e.printStackTrace(); - } } } \ No newline at end of file diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 560e2a8737..04e1fd3366 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -10,9 +10,4 @@ public Event(String task, String at) { public String toString() { return "[E]" + super.toString() + " (at: " + at + ")"; } - - @Override - public String toRecord() { - return "E | " + super.toRecord() + " | " + at; - } } diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 88deef6912..381e41a99c 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -1,14 +1,14 @@ public class Task { private String task; - private boolean isDone; + private boolean done; public Task() { - this.isDone = false; + this.done = false; } public Task(String task) { this.task = task; - this.isDone = false; + this.done = false; } public String getTask() { @@ -16,11 +16,11 @@ public String getTask() { } public void setDone() { - this.isDone = true; + this.done = true; } public void setUndone() { - this.isDone = false; + this.done = false; } @Override @@ -30,7 +30,7 @@ public int hashCode() { @Override public boolean equals(Object other) { - if (other instanceof Task){ + if(other instanceof Task){ Task toCompare = (Task) other; return this.task.equals(toCompare.getTask()); } @@ -40,23 +40,11 @@ public boolean equals(Object other) { @Override public String toString() { String temp; - if (this.isDone) { + if(this.done) { temp = "X"; } else { temp = " "; } return "[" + temp + "] " + this.task; } - - public String toRecord() { - int temp; - - if (this.isDone) { - temp = 1; - } else { - temp = 0; - } - - return temp + " | " + this.task; - } } From 613ba0f58e6373b5c9deb68eaea21c9f1d9fe784 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Mon, 31 Jan 2022 21:54:44 +0800 Subject: [PATCH 19/47] Add different display method for tasks for record purposes Add toRecord method for save method to write in a different display from the output on console --- src/main/java/Deadline.java | 5 +++++ src/main/java/Event.java | 5 +++++ src/main/java/Task.java | 26 +++++++++++++++++++------- src/main/java/ToDo.java | 5 +++++ 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 05b0579037..02a2a9fb37 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -10,4 +10,9 @@ public Deadline(String task, String by) { public String toString() { return "[D]" + super.toString() + " (by: " + by + ")"; } + + @Override + public String toRecord() { + return "D | " + super.toRecord() + " | " + by; + } } diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 04e1fd3366..560e2a8737 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -10,4 +10,9 @@ public Event(String task, String at) { public String toString() { return "[E]" + super.toString() + " (at: " + at + ")"; } + + @Override + public String toRecord() { + return "E | " + super.toRecord() + " | " + at; + } } diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 381e41a99c..88deef6912 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -1,14 +1,14 @@ public class Task { private String task; - private boolean done; + private boolean isDone; public Task() { - this.done = false; + this.isDone = false; } public Task(String task) { this.task = task; - this.done = false; + this.isDone = false; } public String getTask() { @@ -16,11 +16,11 @@ public String getTask() { } public void setDone() { - this.done = true; + this.isDone = true; } public void setUndone() { - this.done = false; + this.isDone = false; } @Override @@ -30,7 +30,7 @@ public int hashCode() { @Override public boolean equals(Object other) { - if(other instanceof Task){ + if (other instanceof Task){ Task toCompare = (Task) other; return this.task.equals(toCompare.getTask()); } @@ -40,11 +40,23 @@ public boolean equals(Object other) { @Override public String toString() { String temp; - if(this.done) { + if (this.isDone) { temp = "X"; } else { temp = " "; } return "[" + temp + "] " + this.task; } + + public String toRecord() { + int temp; + + if (this.isDone) { + temp = 1; + } else { + temp = 0; + } + + return temp + " | " + this.task; + } } diff --git a/src/main/java/ToDo.java b/src/main/java/ToDo.java index a22b5bdca6..52984cf282 100644 --- a/src/main/java/ToDo.java +++ b/src/main/java/ToDo.java @@ -7,4 +7,9 @@ public ToDo(String task) { public String toString() { return "[T]" + super.toString(); } + + @Override + public String toRecord() { + return "T | " + super.toRecord(); + } } From 7ab38ce3b259e681332c24f46c5cac7d17c04de8 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Mon, 31 Jan 2022 21:56:47 +0800 Subject: [PATCH 20/47] Add save method and fix to coding standard Add save method so that whenever list is changed, it is updated to a text file. Adjust code to fit coding standard. --- src/main/java/Duke.java | 295 +++++++++++++++++++++++++--------------- 1 file changed, 182 insertions(+), 113 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 76fedd9829..d33c7d9bbe 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,4 +1,12 @@ -import java.util.*; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import java.util.ArrayList; public class Duke { enum Command { @@ -10,7 +18,6 @@ enum Command { DEADLINE, EVENT, DELETE - ; } public static void main(String[] args) { @@ -24,133 +31,70 @@ public static void main(String[] args) { System.out.println(" Hello! I'm Duke"); System.out.println(" What can I do for you?"); System.out.println(" ____________________________________________________________"); - boolean StillIn = true; + boolean isStillIn = true; FastReader fr = new FastReader(); - ArrayList list = new ArrayList(100); - while(StillIn) { + ArrayList tasks = new ArrayList<>(100); + + while (isStillIn) { String in = fr.nextLine(); - String[] input = in.split(" ", 2); - Command cmd = Command.valueOf(input[0].toUpperCase()); + String[] inputs = in.split(" ", 2); + Command cmd = Command.valueOf(inputs[0].toUpperCase()); System.out.println(" ____________________________________________________________"); + try { switch (cmd) { - case BYE: - System.out.println(bye()); - StillIn = false; - break; - - case LIST: - list(list); - break; - - case MARK: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know what to mark"); - } - - int numMark = Integer.parseInt(input[1]); - - if(numMark > list.size()) { - throw new DukeException(" ☹ OOPS!!! I don't see your task"); - } - - list.get(numMark - 1).setDone(); - System.out.println(" Nice! I've marked this task as done:"); - System.out.println(" " + list.get(numMark - 1)); - break; - - case UNMARK: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know what to unmark"); - } - - int numUnmark = Integer.parseInt(input[1]); - - if(numUnmark > list.size()) { - throw new DukeException(" ☹ OOPS!!! I don't see your task"); - } - - list.get(numUnmark - 1).setUndone(); - System.out.println(" OK, I've marked this task as not done yet:"); - System.out.println(" " + list.get(numUnmark - 1)); - break; - - case TODO: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! The description of a todo cannot be empty"); - } - Task todo = new ToDo(input[1]); - list.add(todo); - System.out.println(" Got it. I've added this task:"); - System.out.println(" " + todo); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); - break; - - case DEADLINE: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! The description of a deadline cannot be empty"); - } - String[] splitbydate = input[1].split(" /by "); - if(splitbydate.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know when your task is due"); - } - String bydate = splitbydate[1]; - Task deadline = new Deadline(splitbydate[0], bydate); - list.add(deadline); - System.out.println(" Got it. I've added this task:"); - System.out.println(" " + deadline); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); - break; - - case EVENT: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! The description of an event cannot be empty"); - } - String[] splitatdate = input[1].split(" /at "); - if(splitatdate.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know when your event happens"); - } - String atdate = splitatdate[1]; - Task event = new Event(splitatdate[0], atdate); - list.add(event); - System.out.println(" Got it. I've added this task:"); - System.out.println(" " + event); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); - break; - - case DELETE: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know what to delete"); - } - - int removenum = Integer.parseInt(input[1]); - - if(removenum > list.size()) { - throw new DukeException(" ☹ OOPS!!! I don't see your task"); - } - - Task removedtask = list.remove(removenum - 1); - System.out.println(" Noted. I've removed this task:"); - System.out.println(" " + removedtask); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); + case BYE: + System.out.println(bye()); + isStillIn = false; + break; + case LIST: + list(tasks); + break; + case MARK: + mark(tasks, inputs); + save(tasks); + break; + case UNMARK: + unmark(tasks, inputs); + save(tasks); + break; + case TODO: + displayToDo(tasks, inputs); + save(tasks); + break; + case DEADLINE: + displayDeadline(tasks, inputs); + save(tasks); + break; + case EVENT: + displayEvent(tasks, inputs); + save(tasks); + break; + case DELETE: + delete(tasks, inputs); + save(tasks); + break; } - } catch(DukeException err) { + } catch (DukeException err) { System.out.println(err.getMessage()); - } catch(NumberFormatException err) { + } catch (NumberFormatException err) { System.out.println(" ☹ OOPS!!! Task number given is not suitable"); } + System.out.println(" ____________________________________________________________"); } } - public static void list(ArrayList lst) { - for(int i=0; i < lst.size(); i++) { - if(i == 0) { + public static void list(ArrayList tasks) { + for (int i = 0; i < tasks.size(); i++) { + if (i == 0) { System.out.println(" Here are the tasks in your list:"); } - System.out.printf(" %d. %s\n", i + 1, lst.get(i)); + + System.out.printf(" %d. %s\n", i + 1, tasks.get(i)); } - if(lst.size() == 0) { + + if (tasks.size() == 0) { System.out.println(" There's nothing in your list"); } } @@ -161,6 +105,131 @@ public static String blah() { public static String bye() { return " Bye. Hope to see you again soon!"; + } + + public static void mark(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know what to mark"); + } + + int numMark = Integer.parseInt(inputs[1]); + + if (numMark > tasks.size()) { + throw new DukeException(" ☹ OOPS!!! I don't see your task"); + } + + tasks.get(numMark - 1).setDone(); + System.out.println(" Nice! I've marked this task as done:"); + System.out.println(" " + tasks.get(numMark - 1)); + } + + public static void unmark(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know what to unmark"); + } + + int numUnmark = Integer.parseInt(inputs[1]); + + if (numUnmark > tasks.size()) { + throw new DukeException(" ☹ OOPS!!! I don't see your task"); + } + + tasks.get(numUnmark - 1).setUndone(); + System.out.println(" OK, I've marked this task as not done yet:"); + System.out.println(" " + tasks.get(numUnmark - 1)); + } + + public static void displayToDo(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(" ☹ OOPS!!! The description of a todo cannot be empty"); + } + + Task toDo = new ToDo(inputs[1]); + tasks.add(toDo); + System.out.println(" Got it. I've added this task:"); + System.out.println(" " + toDo); + System.out.printf(" Now you have %d task(s) in the list\n", tasks.size()); + } + + public static void displayDeadline(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(" ☹ OOPS!!! The description of a deadline cannot be empty"); + } + + String[] splitByDate = inputs[1].split(" /by "); + + if (splitByDate.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know when your task is due"); + } + + String byDate = splitByDate[1]; + Task deadline = new Deadline(splitByDate[0], byDate); + tasks.add(deadline); + System.out.println(" Got it. I've added this task:"); + System.out.println(" " + deadline); + System.out.printf(" Now you have %d task(s) in the list\n", tasks.size()); + } + + public static void displayEvent(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(" ☹ OOPS!!! The description of an event cannot be empty"); + } + + String[] splitAtDate = inputs[1].split(" /at "); + + if (splitAtDate.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know when your event happens"); + } + String atDate = splitAtDate[1]; + Task event = new Event(splitAtDate[0], atDate); + tasks.add(event); + System.out.println(" Got it. I've added this task:"); + System.out.println(" " + event); + System.out.printf(" Now you have %d task(s) in the list\n", tasks.size()); + } + + public static void delete(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(" ☹ OOPS!!! I don't know what to delete"); + } + + int removeNum = Integer.parseInt(inputs[1]); + + if (removeNum > tasks.size()) { + throw new DukeException(" ☹ OOPS!!! I don't see your task"); + } + + Task removedTask = tasks.remove(removeNum - 1); + System.out.println(" Noted. I've removed this task:"); + System.out.println(" " + removedTask); + System.out.printf(" Now you have %d task(s) in the list\n", tasks.size()); + } + + public static void save(ArrayList tasks) { + String working = System.getProperty("user.dir"); + Path path = Paths.get(working, "data"); + path.toFile().mkdirs(); + File file = new File(path + "/duke.txt"); + + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(file)); + StringBuilder output = new StringBuilder(); + + for (int i = 0; i < tasks.size(); i++) { + output.append(tasks.get(0).toRecord()).append("\n"); + } + + writer.write(output.toString()); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } } } \ No newline at end of file From c5d5d5e54e62896aea03047a237a5335b0d44af4 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Mon, 31 Jan 2022 21:57:15 +0800 Subject: [PATCH 21/47] Fix code to coding standard Adjust code to fit coding standard --- src/main/java/FastReader.java | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/java/FastReader.java b/src/main/java/FastReader.java index 53172a82f1..480de882c9 100644 --- a/src/main/java/FastReader.java +++ b/src/main/java/FastReader.java @@ -8,43 +8,45 @@ public class FastReader { BufferedReader br; StringTokenizer st; - public FastReader() - { + public FastReader() { br = new BufferedReader( new InputStreamReader(System.in)); } - String next() - { + String next() { while (st == null || !st.hasMoreElements()) { try { st = new StringTokenizer(br.readLine()); - } - catch (IOException e) { + } catch (IOException e) { e.printStackTrace(); } } + return st.nextToken(); } - int nextInt() { return Integer.parseInt(next()); } + int nextInt() { + return Integer.parseInt(next()); + } - long nextLong() { return Long.parseLong(next()); } + long nextLong() { + return Long.parseLong(next()); + } - double nextDouble() - { + double nextDouble() { return Double.parseDouble(next()); } - String nextLine() - { + String nextLine() { String str = ""; + try { str = br.readLine(); } catch (IOException e) { e.printStackTrace(); } + return str; } } From d4181086e12437da3cdb39f09eb64988ead29b45 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Tue, 8 Feb 2022 22:49:29 +0800 Subject: [PATCH 22/47] Allow dates to be recognized as the object 'Date' Allow dates to be recognized as 'Date' objects to allow more capabilities like sorting around dates --- src/main/java/Deadline.java | 38 +++++++++++++++++++++--- src/main/java/Event.java | 59 ++++++++++++++++++++++++++++++++++--- 2 files changed, 89 insertions(+), 8 deletions(-) diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 05b0579037..62c43651ff 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -1,13 +1,43 @@ +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.DateTimeFormatterBuilder; +import org.joda.time.format.DateTimeParser; + public class Deadline extends Task { - private String by; + private String byString; + private DateTime byDate; - public Deadline(String task, String by) { + public Deadline(String task, String byString) { super(task); - this.by = by; + this.byString = byString; + + try { + DateTimeParser[] dateParsers = { + DateTimeFormat.forPattern("d/MM/yyyy").getParser(), + DateTimeFormat.forPattern("yyyy-MM-dd").getParser(), + DateTimeFormat.forPattern("HHmm").getParser(), + DateTimeFormat.forPattern("d/MM/yyyy HHmm").getParser(), + DateTimeFormat.forPattern("yyyy-MM-dd HHmm").getParser(), + }; + DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, dateParsers).toFormatter(); + DateTime date = formatter.parseDateTime(byString); + this.byDate = date; + } catch (UnsupportedOperationException e) { + this.byDate = null; + } catch (IllegalArgumentException e) { + this.byDate = null; + } } @Override public String toString() { - return "[D]" + super.toString() + " (by: " + by + ")"; + if (this.byDate == null) { + return "[D]" + super.toString() + " (by: " + byString + ")\n If you wanted me to recognize your date, " + + "you might want to\n use a date time format like: '2/12/2021 1800'"; + } else { + String formattedDate = DateTimeFormat.forPattern("MMM dd yyyy h:mm a").print(byDate); + return "[D]" + super.toString() + " (by: " + formattedDate + ")"; + } } } diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 04e1fd3366..41c975e131 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -1,13 +1,64 @@ +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.DateTimeFormatterBuilder; +import org.joda.time.format.DateTimeParser; + public class Event extends Task { - private String at; + private String atString; + private DateTime[] atDate; - public Event(String task, String at) { + public Event(String task, String atString) { super(task); - this.at = at; + this.atString = atString; + String arr[] = atString.split("\\s+-\\s+", 2); + + try { + DateTimeParser[] dateParsers = { + DateTimeFormat.forPattern("d/MM/yyyy").getParser(), + DateTimeFormat.forPattern("yyyy-MM-dd").getParser(), + DateTimeFormat.forPattern("HHmm").getParser(), + DateTimeFormat.forPattern("d/MM/yyyy HHmm").getParser(), + DateTimeFormat.forPattern("yyyy-MM-dd HHmm").getParser(), + }; + DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, dateParsers).toFormatter(); + atDate = new DateTime[2]; + + for (int i = 0; i < 2; i++) { + DateTime date = formatter.parseDateTime(arr[i]); + this.atDate[i] = date; + } + } catch (UnsupportedOperationException e) { + this.atDate[0] = null; + this.atDate[1] = null; + } catch (IllegalArgumentException e) { + this.atDate[0] = null; + this.atDate[1] = null; + } } @Override public String toString() { - return "[E]" + super.toString() + " (at: " + at + ")"; + String formattedDate = ""; + if (this.atDate[0] == null && this.atDate[1] == null) { + return "[E]" + super.toString() + " (at: " + atString + ")"; + } else if (this.atDate[0] != null && this.atDate[1] != null) { + if (this.atDate[1].isAfter(this.atDate[0])) { + formattedDate = DateTimeFormat.forPattern("MMM dd yyyy h:mm a").print(atDate[0]) + + " - " + DateTimeFormat.forPattern("MMM dd yyyy h:mm a").print(atDate[1]); + } else { + formattedDate = DateTimeFormat.forPattern("MMM dd yyyy h:mm a").print(atDate[0]) + + " - " + DateTimeFormat.forPattern("h:mm a").print(atDate[1]); + } + } else if (this.atDate[0] == null) { + String arr[] = atString.split("\\s+-\\s+", 2); + formattedDate = arr[0] + "-" + + DateTimeFormat.forPattern("MMM dd yyyy h:mm a").print(atDate[1]); + } else { + String arr[] = atString.split("\\s+-\\s+", 2); + formattedDate = DateTimeFormat.forPattern("MMM dd yyyy h:mm a").print(atDate[0]) + + "-" + arr[1]; + } + return "[E]" + super.toString() + " (at: " + formattedDate + ")"; } } From 74b2b4dea9a4dc4ca94fbc8164260b06d4e5341b Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Tue, 8 Feb 2022 22:49:52 +0800 Subject: [PATCH 23/47] Fix coding standards --- src/main/java/Duke.java | 278 ++++++++++++++++++++++------------------ 1 file changed, 154 insertions(+), 124 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 76fedd9829..da88bd1075 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,6 +1,8 @@ -import java.util.*; +import java.util.ArrayList; public class Duke { + private static final String INDENT = " "; + private static final String SPACE = " "; enum Command { BYE, LIST, @@ -10,7 +12,6 @@ enum Command { DEADLINE, EVENT, DELETE - ; } public static void main(String[] args) { @@ -20,147 +21,176 @@ public static void main(String[] args) { + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n"; System.out.println("Hello from\n" + logo); - System.out.println(" ____________________________________________________________"); - System.out.println(" Hello! I'm Duke"); - System.out.println(" What can I do for you?"); - System.out.println(" ____________________________________________________________"); - boolean StillIn = true; + System.out.println(INDENT + "____________________________________________________________"); + System.out.println(INDENT + "Hello! I'm Duke"); + System.out.println(INDENT + "What can I do for you?"); + System.out.println(INDENT + "____________________________________________________________"); + boolean isStillIn = true; FastReader fr = new FastReader(); - ArrayList list = new ArrayList(100); - while(StillIn) { + ArrayList tasks = new ArrayList<>(100); + + while (isStillIn) { String in = fr.nextLine(); - String[] input = in.split(" ", 2); - Command cmd = Command.valueOf(input[0].toUpperCase()); - System.out.println(" ____________________________________________________________"); + String[] inputs = in.split(" ", 2); + Command cmd = Command.valueOf(inputs[0].toUpperCase()); + System.out.println(INDENT + "____________________________________________________________"); + try { switch (cmd) { - case BYE: - System.out.println(bye()); - StillIn = false; - break; - - case LIST: - list(list); - break; - - case MARK: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know what to mark"); - } - - int numMark = Integer.parseInt(input[1]); - - if(numMark > list.size()) { - throw new DukeException(" ☹ OOPS!!! I don't see your task"); - } - - list.get(numMark - 1).setDone(); - System.out.println(" Nice! I've marked this task as done:"); - System.out.println(" " + list.get(numMark - 1)); - break; - - case UNMARK: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know what to unmark"); - } - - int numUnmark = Integer.parseInt(input[1]); - - if(numUnmark > list.size()) { - throw new DukeException(" ☹ OOPS!!! I don't see your task"); - } - - list.get(numUnmark - 1).setUndone(); - System.out.println(" OK, I've marked this task as not done yet:"); - System.out.println(" " + list.get(numUnmark - 1)); - break; - - case TODO: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! The description of a todo cannot be empty"); - } - Task todo = new ToDo(input[1]); - list.add(todo); - System.out.println(" Got it. I've added this task:"); - System.out.println(" " + todo); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); - break; - - case DEADLINE: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! The description of a deadline cannot be empty"); - } - String[] splitbydate = input[1].split(" /by "); - if(splitbydate.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know when your task is due"); - } - String bydate = splitbydate[1]; - Task deadline = new Deadline(splitbydate[0], bydate); - list.add(deadline); - System.out.println(" Got it. I've added this task:"); - System.out.println(" " + deadline); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); - break; - - case EVENT: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! The description of an event cannot be empty"); - } - String[] splitatdate = input[1].split(" /at "); - if(splitatdate.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know when your event happens"); - } - String atdate = splitatdate[1]; - Task event = new Event(splitatdate[0], atdate); - list.add(event); - System.out.println(" Got it. I've added this task:"); - System.out.println(" " + event); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); - break; - - case DELETE: - if(input.length < 2) { - throw new DukeException(" ☹ OOPS!!! I don't know what to delete"); - } - - int removenum = Integer.parseInt(input[1]); - - if(removenum > list.size()) { - throw new DukeException(" ☹ OOPS!!! I don't see your task"); - } - - Task removedtask = list.remove(removenum - 1); - System.out.println(" Noted. I've removed this task:"); - System.out.println(" " + removedtask); - System.out.printf(" Now you have %d task(s) in the list\n", list.size()); + case BYE: + System.out.println(bye()); + isStillIn = false; + break; + case LIST: + list(tasks); + break; + case MARK: + mark(tasks, inputs); + break; + case UNMARK: + unmark(tasks, inputs); + break; + case TODO: + displayToDo(tasks, inputs); + break; + case DEADLINE: + displayDeadline(tasks, inputs); + break; + case EVENT: + displayEvent(tasks, inputs); + break; + case DELETE: + delete(tasks, inputs); + break; } - } catch(DukeException err) { + } catch (DukeException err) { System.out.println(err.getMessage()); - } catch(NumberFormatException err) { - System.out.println(" ☹ OOPS!!! Task number given is not suitable"); + } catch (NumberFormatException err) { + System.out.println(INDENT + "☹ OOPS!!! Task number given is not suitable"); } - System.out.println(" ____________________________________________________________"); + + System.out.println(INDENT + "____________________________________________________________"); } } - public static void list(ArrayList lst) { - for(int i=0; i < lst.size(); i++) { - if(i == 0) { - System.out.println(" Here are the tasks in your list:"); + public static void list(ArrayList tasks) { + for (int i = 0; i < tasks.size(); i++) { + if (i == 0) { + System.out.println(INDENT + "Here are the tasks in your list:"); } - System.out.printf(" %d. %s\n", i + 1, lst.get(i)); + + System.out.printf(INDENT + SPACE + "%d. %s\n", i + 1, tasks.get(i)); } - if(lst.size() == 0) { - System.out.println(" There's nothing in your list"); + + if (tasks.size() == 0) { + System.out.println(INDENT + "There's nothing in your list"); } } public static String blah() { - return " blah"; + return INDENT + "blah"; } public static String bye() { - return " Bye. Hope to see you again soon!"; + return INDENT + "Bye. Hope to see you again soon!"; + } + + public static void mark(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(INDENT + "☹ OOPS!!! I don't know what to mark"); + } + + int numMark = Integer.parseInt(inputs[1]); + + if (numMark > tasks.size()) { + throw new DukeException(INDENT + "☹ OOPS!!! I don't see your task"); + } + + tasks.get(numMark - 1).setDone(); + System.out.println(INDENT + "Nice! I've marked this task as done:"); + System.out.println(INDENT + SPACE + tasks.get(numMark - 1)); + } + + public static void unmark(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(INDENT + "☹ OOPS!!! I don't know what to unmark"); + } + + int numUnmark = Integer.parseInt(inputs[1]); + + if (numUnmark > tasks.size()) { + throw new DukeException(INDENT + "☹ OOPS!!! I don't see your task"); + } + + tasks.get(numUnmark - 1).setUndone(); + System.out.println(INDENT + "OK, I've marked this task as not done yet:"); + System.out.println(INDENT + SPACE + tasks.get(numUnmark - 1)); + } + + public static void displayToDo(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(INDENT + "☹ OOPS!!! The description of a todo cannot be empty"); + } + + Task toDo = new ToDo(inputs[1]); + tasks.add(toDo); + System.out.println(INDENT + "Got it. I've added this task:"); + System.out.println(INDENT + SPACE + toDo); + System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); + } + + public static void displayDeadline(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(INDENT + "☹ OOPS!!! The description of a deadline cannot be empty"); + } + + String[] splitByDate = inputs[1].split(" /by "); + + if (splitByDate.length < 2) { + throw new DukeException(INDENT + "☹ OOPS!!! I don't know when your task is due"); + } + + String byDate = splitByDate[1]; + Task deadline = new Deadline(splitByDate[0], byDate); + tasks.add(deadline); + System.out.println(INDENT + "Got it. I've added this task:"); + System.out.println(INDENT + SPACE + deadline); + System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); + } + + public static void displayEvent(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(INDENT + "☹ OOPS!!! The description of an event cannot be empty"); + } + + String[] splitAtDate = inputs[1].split(" /at "); + + if (splitAtDate.length < 2) { + throw new DukeException(INDENT + "☹ OOPS!!! I don't know when your event happens"); + } + + String atDate = splitAtDate[1]; + Task event = new Event(splitAtDate[0], atDate); + tasks.add(event); + System.out.println(INDENT + "Got it. I've added this task:"); + System.out.println(INDENT + SPACE + event); + System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); + } + + public static void delete(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(INDENT + "☹ OOPS!!! I don't know what to delete"); + } + + int removeNum = Integer.parseInt(inputs[1]); + + if (removeNum > tasks.size()) { + throw new DukeException(INDENT + "☹ OOPS!!! I don't see your task"); + } + Task removedTask = tasks.remove(removeNum - 1); + System.out.println(INDENT + "Noted. I've removed this task:"); + System.out.println(INDENT + SPACE + removedTask); + System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); } } \ No newline at end of file From 70a8771d15f9800b1ffcf321b72b018442e6128e Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Thu, 24 Feb 2022 22:59:40 +0800 Subject: [PATCH 24/47] Adjust how date is displayed and recorded Adjust how date works due to complement save implementation --- src/main/java/Deadline.java | 5 ++--- src/main/java/Event.java | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 91408be9b8..e65dd07c4b 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -33,8 +33,7 @@ public Deadline(String task, String byString) { @Override public String toString() { if (this.byDate == null) { - return "[D]" + super.toString() + " (by: " + byString + ")\n If you wanted me to recognize your date, " + - "you might want to\n use a date time format like: '2/12/2021 1800'"; + return "[D]" + super.toString() + " (by: " + byString + ")"; } else { String formattedDate = DateTimeFormat.forPattern("MMM dd yyyy h:mm a").print(byDate); return "[D]" + super.toString() + " (by: " + formattedDate + ")"; @@ -43,6 +42,6 @@ public String toString() { @Override public String toRecord() { - return "D | " + super.toRecord() + " | " + by; + return "D | " + super.toRecord() + " | " + byString; } } diff --git a/src/main/java/Event.java b/src/main/java/Event.java index ec1b186632..a2c544848c 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -64,6 +64,6 @@ public String toString() { @Override public String toRecord() { - return "E | " + super.toRecord() + " | " + at; + return "E | " + super.toRecord() + " | " + atString; } } From 603ab9674e0fad224afe9d247dbfdfdba657ecfb Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Thu, 24 Feb 2022 23:01:02 +0800 Subject: [PATCH 25/47] Make program more OOP like Seperate operations into different classes that do specific things --- src/main/java/Command.java | 51 ++++++++ src/main/java/Duke.java | 254 ++++++++---------------------------- src/main/java/Parser.java | 113 ++++++++++++++++ src/main/java/Storage.java | 70 ++++++++++ src/main/java/TaskList.java | 75 +++++++++++ src/main/java/UI.java | 112 ++++++++++++++++ 6 files changed, 472 insertions(+), 203 deletions(-) create mode 100644 src/main/java/Command.java create mode 100644 src/main/java/Parser.java create mode 100644 src/main/java/Storage.java create mode 100644 src/main/java/TaskList.java create mode 100644 src/main/java/UI.java diff --git a/src/main/java/Command.java b/src/main/java/Command.java new file mode 100644 index 0000000000..18016ccd5f --- /dev/null +++ b/src/main/java/Command.java @@ -0,0 +1,51 @@ +public enum Command { + BYE { + public boolean isRunning() { + return false; + } + }, + LIST { + public boolean isRunning() { + return true; + } + }, + MARK { + public boolean isRunning() { + return true; + } + }, + UNMARK { + public boolean isRunning() { + return true; + } + }, + TODO { + public boolean isRunning() { + return true; + } + }, + DEADLINE { + public boolean isRunning() { + return true; + } + }, + EVENT { + public boolean isRunning() { + return true; + } + }, + DELETE { + public boolean isRunning() { + return true; + } + }, + DATEFORMAT { + public boolean isRunning() { + return true; + } + }; + + public abstract boolean isRunning(); +} + + diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 06d2e50f9c..6cce9797ed 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -9,230 +9,78 @@ import java.util.ArrayList; public class Duke { - private static final String INDENT = " "; - private static final String SPACE = " "; - enum Command { - BYE, - LIST, - MARK, - UNMARK, - TODO, - DEADLINE, - EVENT, - DELETE + private UI ui; + private Storage storage; + private TaskList tasks; + public Duke(String filePath) { + ui = new UI(); + storage = new Storage(); + try { + tasks = new TaskList(storage.load()); + } catch (DukeException e) { + ui.showLoadingError(e.getMessage()); + tasks = new TaskList(); + } } - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - System.out.println(INDENT + "____________________________________________________________"); - System.out.println(INDENT + "Hello! I'm Duke"); - System.out.println(INDENT + "What can I do for you?"); - System.out.println(INDENT + "____________________________________________________________"); - - boolean isStillIn = true; - FastReader fr = new FastReader(); - ArrayList tasks = new ArrayList<>(100); + public void run() { + ui.startScreen(); + boolean isRunning = true; + while (isRunning) { + String[] inputs = Parser.parseInput(ui); + Command cmd = Parser.parseCommand(inputs); + UI.startLine(); + this.execute(cmd, inputs); + isRunning = cmd.isRunning(); + UI.endLine(); + } + } - while (isStillIn) { - String in = fr.nextLine(); - String[] inputs = in.split(" ", 2); - Command cmd = Command.valueOf(inputs[0].toUpperCase()); - System.out.println(INDENT + "____________________________________________________________"); + public static void main(String[] args) { + new Duke("data/tasks.txt").run(); + } - try { - switch (cmd) { + public void execute(Command cmd, String[] inputs) { + try { + switch (cmd) { case BYE: - System.out.println(bye()); - isStillIn = false; + ui.byeDisplay(); break; case LIST: - list(tasks); + tasks.list(); break; case MARK: - mark(tasks, inputs); - save(tasks); + tasks.mark(inputs); + storage.save(tasks.getTasks()); break; case UNMARK: - unmark(tasks, inputs); - save(tasks); + tasks.unmark(inputs); + storage.save(tasks.getTasks()); break; case TODO: - displayToDo(tasks, inputs); - save(tasks); + tasks.addToDo(inputs); + storage.save(tasks.getTasks()); break; case DEADLINE: - displayDeadline(tasks, inputs); - save(tasks); + tasks.addDeadline(inputs); + storage.save(tasks.getTasks()); break; case EVENT: - displayEvent(tasks, inputs); - save(tasks); + tasks.addEvent(inputs); + storage.save(tasks.getTasks()); break; case DELETE: - delete(tasks, inputs); - save(tasks); + tasks.delete(inputs); + storage.save(tasks.getTasks()); + break; + case DATEFORMAT: + ui.showDateFormats(); break; - } - } catch (DukeException err) { - System.out.println(err.getMessage()); - } catch (NumberFormatException err) { - System.out.println(INDENT + "☹ OOPS!!! Task number given is not suitable"); - } - - System.out.println(INDENT + "____________________________________________________________"); - } - } - - public static void list(ArrayList tasks) { - for (int i = 0; i < tasks.size(); i++) { - if (i == 0) { - System.out.println(INDENT + "Here are the tasks in your list:"); - } - - System.out.printf(INDENT + SPACE + "%d. %s\n", i + 1, tasks.get(i)); - } - - if (tasks.size() == 0) { - System.out.println(INDENT + "There's nothing in your list"); - } - } - - public static String blah() { - return INDENT + "blah"; - } - - public static String bye() { - return INDENT + "Bye. Hope to see you again soon!"; - } - - public static void mark(ArrayList tasks, String[] inputs) throws DukeException { - if (inputs.length < 2) { - throw new DukeException(INDENT + "☹ OOPS!!! I don't know what to mark"); - } - - int numMark = Integer.parseInt(inputs[1]); - - if (numMark > tasks.size()) { - throw new DukeException(INDENT + "☹ OOPS!!! I don't see your task"); - } - - tasks.get(numMark - 1).setDone(); - System.out.println(INDENT + "Nice! I've marked this task as done:"); - System.out.println(INDENT + SPACE + tasks.get(numMark - 1)); - } - - public static void unmark(ArrayList tasks, String[] inputs) throws DukeException { - if (inputs.length < 2) { - throw new DukeException(INDENT + "☹ OOPS!!! I don't know what to unmark"); - } - - int numUnmark = Integer.parseInt(inputs[1]); - - if (numUnmark > tasks.size()) { - throw new DukeException(INDENT + "☹ OOPS!!! I don't see your task"); - } - - tasks.get(numUnmark - 1).setUndone(); - System.out.println(INDENT + "OK, I've marked this task as not done yet:"); - System.out.println(INDENT + SPACE + tasks.get(numUnmark - 1)); - } - - public static void displayToDo(ArrayList tasks, String[] inputs) throws DukeException { - if (inputs.length < 2) { - throw new DukeException(INDENT + "☹ OOPS!!! The description of a todo cannot be empty"); - } - - Task toDo = new ToDo(inputs[1]); - tasks.add(toDo); - System.out.println(INDENT + "Got it. I've added this task:"); - System.out.println(INDENT + SPACE + toDo); - System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); - } - - public static void displayDeadline(ArrayList tasks, String[] inputs) throws DukeException { - if (inputs.length < 2) { - throw new DukeException(INDENT + "☹ OOPS!!! The description of a deadline cannot be empty"); - } - - String[] splitByDate = inputs[1].split(" /by "); - - if (splitByDate.length < 2) { - throw new DukeException(INDENT + "☹ OOPS!!! I don't know when your task is due"); - } - - String byDate = splitByDate[1]; - Task deadline = new Deadline(splitByDate[0], byDate); - tasks.add(deadline); - System.out.println(INDENT + "Got it. I've added this task:"); - System.out.println(INDENT + SPACE + deadline); - System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); - } - - public static void displayEvent(ArrayList tasks, String[] inputs) throws DukeException { - if (inputs.length < 2) { - throw new DukeException(INDENT + "☹ OOPS!!! The description of an event cannot be empty"); - } - - String[] splitAtDate = inputs[1].split(" /at "); - - if (splitAtDate.length < 2) { - throw new DukeException(INDENT + "☹ OOPS!!! I don't know when your event happens"); - } - - String atDate = splitAtDate[1]; - Task event = new Event(splitAtDate[0], atDate); - tasks.add(event); - System.out.println(INDENT + "Got it. I've added this task:"); - System.out.println(INDENT + SPACE + event); - System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); - } - - public static void delete(ArrayList tasks, String[] inputs) throws DukeException { - if (inputs.length < 2) { - throw new DukeException(INDENT + "☹ OOPS!!! I don't know what to delete"); - } - - int removeNum = Integer.parseInt(inputs[1]); - - if (removeNum > tasks.size()) { - throw new DukeException(INDENT + "☹ OOPS!!! I don't see your task"); - } - - Task removedTask = tasks.remove(removeNum - 1); - System.out.println(INDENT + "Noted. I've removed this task:"); - System.out.println(INDENT + SPACE + removedTask); - System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); - } - - public static void save(ArrayList tasks) { - String working = System.getProperty("user.dir"); - Path path = Paths.get(working, "data"); - path.toFile().mkdirs(); - File file = new File(path + "/duke.txt"); - - try { - file.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - - try { - BufferedWriter writer = new BufferedWriter(new FileWriter(file)); - StringBuilder output = new StringBuilder(); - - for (int i = 0; i < tasks.size(); i++) { - output.append(tasks.get(0).toRecord()).append("\n"); } - - writer.write(output.toString()); - writer.close(); - } catch (IOException e) { - e.printStackTrace(); + } catch (DukeException err) { + ui.showError(err.getMessage()); + } catch (NumberFormatException err) { + ui.showError(UI.getIndent() + "☹ OOPS!!! Task number given is not suitable"); } } } \ No newline at end of file diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java new file mode 100644 index 0000000000..64aa52ac21 --- /dev/null +++ b/src/main/java/Parser.java @@ -0,0 +1,113 @@ +import java.util.ArrayList; + +public class Parser { + public static Command parseCommand(String[] inputs) { + Command cmd = Command.valueOf(inputs[0].toUpperCase()); + return cmd; + } + + public static int parseMarkIndex(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(UI.getIndent() + "☹ OOPS!!! I don't know what to mark"); + } + + int markIndex = Integer.parseInt(inputs[1]); + + if (markIndex > tasks.size()) { + throw new DukeException(UI.getIndent() + "☹ OOPS!!! I don't see your task"); + } + + return markIndex; + } + + public static int parseUnmarkIndex(ArrayList tasks, String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(UI.getIndent() + "☹ OOPS!!! I don't know what to mark"); + } + + int unmarkIndex = Integer.parseInt(inputs[1]); + + if (unmarkIndex > tasks.size()) { + throw new DukeException(UI.getIndent() + "☹ OOPS!!! I don't see your task"); + } + + return unmarkIndex; + } + + public static void checkToDoDescription(String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(UI.getIndent() + "☹ OOPS!!! The description of a todo cannot be empty"); + } + } + + public static void checkDeadlineDescription(String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(UI.getIndent() + "☹ OOPS!!! The description of a deadline cannot be empty"); + } + } + + public static String[] splitDeadlineDate(String[] inputs) throws DukeException { + String[] splitByDate = inputs[1].split(" /by "); + + if (splitByDate.length < 2) { + throw new DukeException(UI.getIndent() + "☹ OOPS!!! I don't know when your task is due"); + } + + return splitByDate; + } + + public static void checkEventDescription(String[] inputs) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(UI.getIndent() + "☹ OOPS!!! The description of an event cannot be empty"); + } + } + + public static String[] splitEventDate(String[] inputs) throws DukeException { + String[] splitAtDate = inputs[1].split(" /at "); + + if (splitAtDate.length < 2) { + throw new DukeException(UI.getIndent() + "☹ OOPS!!! I don't know when your event happens"); + } + + return splitAtDate; + } + + public static int parseDeleteIndex(String[] inputs, ArrayList tasks) throws DukeException { + if (inputs.length < 2) { + throw new DukeException(UI.getIndent() + "☹ OOPS!!! I don't know what to delete"); + } + + int removeNum = Integer.parseInt(inputs[1]); + + if (removeNum > tasks.size()) { + throw new DukeException(UI.getIndent() + "☹ OOPS!!! I don't see your task"); + } + + return removeNum; + } + + public static String[] parseInput(UI ui) { + String in = ui.read(); + String[] inputs = in.split(" ", 2); + return inputs; + } + + public static Task parseEntry(String entry) { + String[] display = entry.split(" \\| "); + Task task = new Task(); + + if (display[0].equals("T")) { + task = new ToDo(display[2]); + } else if (display[0].equals("D")) { + task = new Deadline(display[2], display[3]); + } else if (display[0].equals("E")) { + task = new Event(display[2], display[3]); + } + + if (display[1].equals("1")) { + task.setDone(); + } + + return task; + } +} diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java new file mode 100644 index 0000000000..a32f26da1c --- /dev/null +++ b/src/main/java/Storage.java @@ -0,0 +1,70 @@ +import java.io.*; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; + +public class Storage { + public Storage() { + } + + public void save(ArrayList tasks) { + String working = System.getProperty("user.dir"); + Path path = Paths.get(working, "data"); + path.toFile().mkdirs(); + File file = new File(path + "/duke.txt"); + + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(file)); + StringBuilder output = new StringBuilder(); + + for (int i = 0; i < tasks.size(); i++) { + output.append(tasks.get(0)).append("\n"); + } + + writer.write(output.toString()); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public ArrayList load() throws DukeException { + String working = System.getProperty("user.dir"); + Path path = Paths.get(working, "data"); + path.toFile().mkdirs(); + File file = new File(path + "/duke.txt"); + + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + throw new DukeException("Saved task list could not be loaded"); + } + + ArrayList input = new ArrayList<>(); + + try { + BufferedReader reader = new BufferedReader(new FileReader(file)); + String entry = reader.readLine(); + + while (entry != null) { + input.add(entry); + entry = reader.readLine(); + } + + } catch (FileNotFoundException e) { + e.printStackTrace(); + throw new DukeException("Saved task list could not be loaded"); + } catch (IOException e) { + e.printStackTrace(); + } + + return input; + } +} diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java new file mode 100644 index 0000000000..e3f5bc94a1 --- /dev/null +++ b/src/main/java/TaskList.java @@ -0,0 +1,75 @@ +import java.util.ArrayList; + +public class TaskList { + private ArrayList tasks; + private ArrayList display; + public TaskList(ArrayList tasks) { + this.tasks = tasks; + this.display = new ArrayList<>(); + + for (int i = 0; i < tasks.size(); i++) { + display.add(Parser.parseEntry(tasks.get(i))); + } + } + + public TaskList() { + this.tasks = new ArrayList(); + } + + public ArrayList getTasks() { + return tasks; + } + + public void list() { + UI.listDisplay(display); + } + + public void mark(String[] inputs) throws DukeException { + int markIndex = Parser.parseMarkIndex(display, inputs); + display.get(markIndex - 1).setDone(); + tasks.set(markIndex - 1, display.get(markIndex - 1).toRecord()); + UI.markDisplay(display.get(markIndex - 1)); + } + + public void unmark(String[] inputs) throws DukeException { + int unmarkIndex = Parser.parseUnmarkIndex(display, inputs); + display.get(unmarkIndex - 1).setUndone(); + tasks.set(unmarkIndex - 1, display.get(unmarkIndex - 1).toRecord()); + UI.unmarkDisplay(display.get(unmarkIndex - 1)); + } + + public void addToDo(String[] inputs) throws DukeException { + Parser.checkToDoDescription(inputs); + Task toDo = new ToDo(inputs[1]); + display.add(toDo); + tasks.add(toDo.toRecord()); + UI.toDoDisplay(toDo, display); + } + + public void addDeadline(String[] inputs) throws DukeException { + Parser.checkDeadlineDescription(inputs); + String[] splitByDate = Parser.splitDeadlineDate(inputs); + String byDate = splitByDate[1]; + Task deadline = new Deadline(splitByDate[0], byDate); + display.add(deadline); + tasks.add(deadline.toRecord()); + UI.deadlineDisplay(deadline, display); + } + + public void addEvent(String[] inputs) throws DukeException { + Parser.checkEventDescription(inputs); + String[] splitAtDate = Parser.splitEventDate(inputs); + String atDate = splitAtDate[1]; + Task event = new Event(splitAtDate[0], atDate); + display.add(event); + tasks.add(event.toRecord()); + UI.eventDisplay(event, display); + } + + public void delete(String[] inputs) throws DukeException { + int deleteIndex = Parser.parseDeleteIndex(inputs, display); + Task deletedTask = display.remove(deleteIndex - 1); + tasks.remove(deleteIndex - 1); + UI.deleteDisplay(deletedTask, display); + } +} diff --git a/src/main/java/UI.java b/src/main/java/UI.java new file mode 100644 index 0000000000..55b8f4464c --- /dev/null +++ b/src/main/java/UI.java @@ -0,0 +1,112 @@ +import java.lang.reflect.Array; +import java.util.ArrayList; + +public class UI { + private FastReader fr; + private static final String INDENT = " "; + private static final String SPACE = " "; + private static final String[] dateFormat = {"d/MM/yyyy", "yyyy-MM-dd", "HHmm", "d/MM/yyyy HHmm", "yyyy-MM-dd HHmm"}; + + public UI() { + fr = new FastReader(); + } + + public void startScreen() { + String logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + System.out.println("Hello from\n" + logo); + System.out.println(INDENT + "____________________________________________________________"); + System.out.println(INDENT + "Hello! I'm Duke"); + System.out.println(INDENT + "What can I do for you?"); + System.out.println(INDENT + "____________________________________________________________"); + } + + public String read() { + return fr.nextLine(); + } + + public static void startLine() { + System.out.println(INDENT + "____________________________________________________________"); + } + + public static void endLine() { + System.out.println(INDENT + "____________________________________________________________"); + } + + public void byeDisplay() { + System.out.println(INDENT + "Bye. Hope to see you again soon!"); + } + + public static void listDisplay(ArrayList tasks) { + for (int i = 0; i < tasks.size(); i++) { + if (i == 0) { + System.out.println(INDENT + "Here are the tasks in your list:"); + } + + System.out.printf(INDENT + SPACE + "%d. %s\n", i + 1, tasks.get(i)); + } + + if (tasks.size() == 0) { + System.out.println(INDENT + "There's nothing in your list"); + } + } + + public static void markDisplay(Task task) { + System.out.println(INDENT + "Nice! I've marked this task as done:"); + System.out.println(INDENT + SPACE + task); + } + + public static void unmarkDisplay(Task task) { + System.out.println(INDENT + "OK, I've marked this task as not done yet:"); + System.out.println(INDENT + SPACE + task); + } + + public static String getIndent() { + return INDENT; + } + + public static String getSpace() { + return SPACE; + } + + public static void toDoDisplay(Task toDo, ArrayList tasks) { + System.out.println(INDENT + "Got it. I've added this task:"); + System.out.println(INDENT + SPACE + toDo); + System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); + } + + public static void deadlineDisplay(Task deadline, ArrayList tasks) { + System.out.println(INDENT + "Got it. I've added this task:"); + System.out.println(INDENT + SPACE + deadline); + System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); + } + + public static void eventDisplay(Task event, ArrayList tasks) { + System.out.println(INDENT + "Got it. I've added this task:"); + System.out.println(INDENT + SPACE + event); + System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); + } + + public static void deleteDisplay(Task deletedTask, ArrayList tasks) { + System.out.println(INDENT + "Noted. I've removed this task:"); + System.out.println(INDENT + SPACE + deletedTask); + System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); + } + + public void showLoadingError(String e) { + System.out.println(e); + } + + public void showError(String e) { + System.out.println(e); + } + + public void showDateFormats() { + for (String s : dateFormat) { + System.out.println(s); + } + } +} From 80b2d812405e1d9dce03f1679719a23802020caf Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Thu, 24 Feb 2022 23:03:42 +0800 Subject: [PATCH 26/47] Fix how date is parsed in event Fix how date is parsed in event so that it can read the two dates regardless of whitespace between the seperator '-' and the date --- src/main/java/Event.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/Event.java b/src/main/java/Event.java index a2c544848c..d24a31bcd1 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -11,7 +11,7 @@ public class Event extends Task { public Event(String task, String atString) { super(task); this.atString = atString; - String arr[] = atString.split("\\s+-\\s+", 2); + String arr[] = atString.split("\\s*-\\s*", 2); try { DateTimeParser[] dateParsers = { From 4967046e1ef369b25be6fd20b4f0ce106168762b Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Thu, 24 Feb 2022 23:25:23 +0800 Subject: [PATCH 27/47] Package all classes Package all classes into one package --- src/main/java/{ => duke}/Command.java | 2 ++ src/main/java/{ => duke}/Deadline.java | 2 ++ src/main/java/{ => duke}/Duke.java | 10 +--------- src/main/java/{ => duke}/DukeException.java | 2 ++ src/main/java/{ => duke}/Event.java | 2 ++ src/main/java/{ => duke}/FastReader.java | 2 ++ src/main/java/{ => duke}/Parser.java | 2 ++ src/main/java/{ => duke}/Storage.java | 12 +++++++++++- src/main/java/{ => duke}/Task.java | 2 ++ src/main/java/{ => duke}/TaskList.java | 2 ++ src/main/java/{ => duke}/ToDo.java | 2 ++ src/main/java/{ => duke}/UI.java | 3 ++- 12 files changed, 32 insertions(+), 11 deletions(-) rename src/main/java/{ => duke}/Command.java (98%) rename src/main/java/{ => duke}/Deadline.java (99%) rename src/main/java/{ => duke}/Duke.java (92%) rename src/main/java/{ => duke}/DukeException.java (89%) rename src/main/java/{ => duke}/Event.java (99%) rename src/main/java/{ => duke}/FastReader.java (98%) rename src/main/java/{ => duke}/Parser.java (99%) rename src/main/java/{ => duke}/Storage.java (89%) rename src/main/java/{ => duke}/Task.java (98%) rename src/main/java/{ => duke}/TaskList.java (99%) rename src/main/java/{ => duke}/ToDo.java (94%) rename src/main/java/{ => duke}/UI.java (99%) diff --git a/src/main/java/Command.java b/src/main/java/duke/Command.java similarity index 98% rename from src/main/java/Command.java rename to src/main/java/duke/Command.java index 18016ccd5f..a83ec8ae08 100644 --- a/src/main/java/Command.java +++ b/src/main/java/duke/Command.java @@ -1,3 +1,5 @@ +package duke; + public enum Command { BYE { public boolean isRunning() { diff --git a/src/main/java/Deadline.java b/src/main/java/duke/Deadline.java similarity index 99% rename from src/main/java/Deadline.java rename to src/main/java/duke/Deadline.java index e65dd07c4b..59fd47c491 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/duke/Deadline.java @@ -1,3 +1,5 @@ +package duke; + import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; diff --git a/src/main/java/Duke.java b/src/main/java/duke/Duke.java similarity index 92% rename from src/main/java/Duke.java rename to src/main/java/duke/Duke.java index 6cce9797ed..c340f6a494 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/duke/Duke.java @@ -1,12 +1,4 @@ -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import java.util.ArrayList; +package duke; public class Duke { private UI ui; diff --git a/src/main/java/DukeException.java b/src/main/java/duke/DukeException.java similarity index 89% rename from src/main/java/DukeException.java rename to src/main/java/duke/DukeException.java index fe8837e3a3..3fcd0f5ea8 100644 --- a/src/main/java/DukeException.java +++ b/src/main/java/duke/DukeException.java @@ -1,3 +1,5 @@ +package duke; + public class DukeException extends Exception { public DukeException(String message) { super(message); diff --git a/src/main/java/Event.java b/src/main/java/duke/Event.java similarity index 99% rename from src/main/java/Event.java rename to src/main/java/duke/Event.java index d24a31bcd1..52a0113bd0 100644 --- a/src/main/java/Event.java +++ b/src/main/java/duke/Event.java @@ -1,3 +1,5 @@ +package duke; + import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; diff --git a/src/main/java/FastReader.java b/src/main/java/duke/FastReader.java similarity index 98% rename from src/main/java/FastReader.java rename to src/main/java/duke/FastReader.java index 480de882c9..bf33639bc3 100644 --- a/src/main/java/FastReader.java +++ b/src/main/java/duke/FastReader.java @@ -1,3 +1,5 @@ +package duke; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; diff --git a/src/main/java/Parser.java b/src/main/java/duke/Parser.java similarity index 99% rename from src/main/java/Parser.java rename to src/main/java/duke/Parser.java index 64aa52ac21..fdf9e3715e 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/duke/Parser.java @@ -1,3 +1,5 @@ +package duke; + import java.util.ArrayList; public class Parser { diff --git a/src/main/java/Storage.java b/src/main/java/duke/Storage.java similarity index 89% rename from src/main/java/Storage.java rename to src/main/java/duke/Storage.java index a32f26da1c..6532152a23 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/duke/Storage.java @@ -1,6 +1,16 @@ -import java.io.*; +package duke; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + import java.nio.file.Path; import java.nio.file.Paths; + import java.util.ArrayList; public class Storage { diff --git a/src/main/java/Task.java b/src/main/java/duke/Task.java similarity index 98% rename from src/main/java/Task.java rename to src/main/java/duke/Task.java index 88deef6912..53c47c5fd7 100644 --- a/src/main/java/Task.java +++ b/src/main/java/duke/Task.java @@ -1,3 +1,5 @@ +package duke; + public class Task { private String task; private boolean isDone; diff --git a/src/main/java/TaskList.java b/src/main/java/duke/TaskList.java similarity index 99% rename from src/main/java/TaskList.java rename to src/main/java/duke/TaskList.java index e3f5bc94a1..cdf02056a5 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/duke/TaskList.java @@ -1,3 +1,5 @@ +package duke; + import java.util.ArrayList; public class TaskList { diff --git a/src/main/java/ToDo.java b/src/main/java/duke/ToDo.java similarity index 94% rename from src/main/java/ToDo.java rename to src/main/java/duke/ToDo.java index 52984cf282..dde24cacfb 100644 --- a/src/main/java/ToDo.java +++ b/src/main/java/duke/ToDo.java @@ -1,3 +1,5 @@ +package duke; + public class ToDo extends Task { public ToDo(String task) { super(task); diff --git a/src/main/java/UI.java b/src/main/java/duke/UI.java similarity index 99% rename from src/main/java/UI.java rename to src/main/java/duke/UI.java index 55b8f4464c..a62a4a6509 100644 --- a/src/main/java/UI.java +++ b/src/main/java/duke/UI.java @@ -1,4 +1,5 @@ -import java.lang.reflect.Array; +package duke; + import java.util.ArrayList; public class UI { From 5c836484033fef64b1cf43717b8a1d1b93d7503c Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Fri, 25 Feb 2022 10:05:40 +0800 Subject: [PATCH 28/47] Add exception handler for invalid commands Add exception handler for invalid commands by telling user command is invalid and then waits to receive next user input --- src/main/java/duke/Duke.java | 90 ++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index c340f6a494..a91b25696a 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -20,10 +20,16 @@ public void run() { boolean isRunning = true; while (isRunning) { String[] inputs = Parser.parseInput(ui); - Command cmd = Parser.parseCommand(inputs); UI.startLine(); - this.execute(cmd, inputs); - isRunning = cmd.isRunning(); + + try { + Command cmd = Parser.parseCommand(inputs); + this.execute(cmd, inputs); + isRunning = cmd.isRunning(); + } catch (IllegalArgumentException e) { + ui.showError(UI.getIndent() + "Invalid Command: " + inputs[0]); + } + UI.endLine(); } } @@ -35,44 +41,48 @@ public static void main(String[] args) { public void execute(Command cmd, String[] inputs) { try { switch (cmd) { - case BYE: - ui.byeDisplay(); - break; - case LIST: - tasks.list(); - break; - case MARK: - tasks.mark(inputs); - storage.save(tasks.getTasks()); - break; - case UNMARK: - tasks.unmark(inputs); - storage.save(tasks.getTasks()); - break; - case TODO: - tasks.addToDo(inputs); - storage.save(tasks.getTasks()); - break; - case DEADLINE: - tasks.addDeadline(inputs); - storage.save(tasks.getTasks()); - break; - case EVENT: - tasks.addEvent(inputs); - storage.save(tasks.getTasks()); - break; - case DELETE: - tasks.delete(inputs); - storage.save(tasks.getTasks()); - break; - case DATEFORMAT: - ui.showDateFormats(); - break; + case BYE: + ui.byeDisplay(); + break; + case LIST: + tasks.list(); + break; + case MARK: + tasks.mark(inputs); + storage.save(tasks.getTasks()); + break; + case UNMARK: + tasks.unmark(inputs); + storage.save(tasks.getTasks()); + break; + case TODO: + tasks.addToDo(inputs); + storage.save(tasks.getTasks()); + break; + case DEADLINE: + tasks.addDeadline(inputs); + storage.save(tasks.getTasks()); + break; + case EVENT: + tasks.addEvent(inputs); + storage.save(tasks.getTasks()); + break; + case DELETE: + tasks.delete(inputs); + storage.save(tasks.getTasks()); + break; + case DATEFORMAT: + ui.showDateFormats(); + break; + default: + throw new IllegalStateException(UI.getIndent() + "Unexpected value: " + cmd); } - } catch (DukeException err) { - ui.showError(err.getMessage()); - } catch (NumberFormatException err) { + } catch (NumberFormatException e) { ui.showError(UI.getIndent() + "☹ OOPS!!! Task number given is not suitable"); + } catch (DukeException | IllegalStateException e) { + ui.showError(e.getMessage()); + } catch (IllegalArgumentException e) { + ui.showError(UI.getIndent() + "Invalid command: " + cmd); } } -} \ No newline at end of file +} From 1536f419fdcefb9cbdfeee57450da7bb9e4bf46a Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Fri, 25 Feb 2022 10:06:39 +0800 Subject: [PATCH 29/47] Let parseCommand throw IllegalArgumentException Let parseCommand throw IllegalArgumentException when receive invalid user input --- src/main/java/duke/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/duke/Parser.java b/src/main/java/duke/Parser.java index fdf9e3715e..cc6d3bb058 100644 --- a/src/main/java/duke/Parser.java +++ b/src/main/java/duke/Parser.java @@ -3,7 +3,7 @@ import java.util.ArrayList; public class Parser { - public static Command parseCommand(String[] inputs) { + public static Command parseCommand(String[] inputs) throws IllegalArgumentException { Command cmd = Command.valueOf(inputs[0].toUpperCase()); return cmd; } From 1ac0efb2f91f87f39c2dce25072d01f2c5e417bb Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Fri, 25 Feb 2022 10:08:18 +0800 Subject: [PATCH 30/47] Add JUnit tests Add JUnit tests for invalid commands and check if bye command quits program --- src/test/java/duke/CommandTest.java | 15 +++++++++++++++ src/test/java/duke/ParserTest.java | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/test/java/duke/CommandTest.java create mode 100644 src/test/java/duke/ParserTest.java diff --git a/src/test/java/duke/CommandTest.java b/src/test/java/duke/CommandTest.java new file mode 100644 index 0000000000..9487653bb5 --- /dev/null +++ b/src/test/java/duke/CommandTest.java @@ -0,0 +1,15 @@ +package duke; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +class CommandTest { + @Test + @DisplayName("BYE command quits program") + void byeQuit() { + Command cmd = Parser.parseCommand(new String[] {"bye"}); + assertFalse(cmd.isRunning()); + } +} diff --git a/src/test/java/duke/ParserTest.java b/src/test/java/duke/ParserTest.java new file mode 100644 index 0000000000..7ec5119ac3 --- /dev/null +++ b/src/test/java/duke/ParserTest.java @@ -0,0 +1,16 @@ +package duke; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +class ParserTest { + @Test + @DisplayName("Invalid Command") + void executeInvalidCommand() { + assertThrows(IllegalArgumentException.class, () -> { + Parser.parseCommand(new String[] {"chicken", "little"}); + }); + } +} From 9ced63385007c716b3e277f7aa217c1b671f76d5 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Fri, 25 Feb 2022 10:08:55 +0800 Subject: [PATCH 31/47] Add checkstyle tests Add checkstyle tests to ensure coding standard --- config/checkstyle/checkstyle.xml | 398 +++++++++++++++++++++++++++++ config/checkstyle/suppressions.xml | 10 + 2 files changed, 408 insertions(+) create mode 100644 config/checkstyle/checkstyle.xml create mode 100644 config/checkstyle/suppressions.xml diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000000..7951f3a6c4 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,398 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml new file mode 100644 index 0000000000..135ea49ee0 --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file From d83efcc69dcc8883ea2bb347262f664ca64c800a Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Fri, 25 Feb 2022 19:18:17 +0800 Subject: [PATCH 32/47] Add JavaDoc Add JavaDoc to allow people to use program easily --- src/main/java/duke/Deadline.java | 7 +++ src/main/java/duke/Duke.java | 21 ++++++- src/main/java/duke/Event.java | 13 ++++- src/main/java/duke/FastReader.java | 91 +++++++++++++++--------------- src/main/java/duke/Parser.java | 83 +++++++++++++++++++++++++++ src/main/java/duke/Storage.java | 13 ++++- src/main/java/duke/Task.java | 17 +++++- src/main/java/duke/TaskList.java | 46 +++++++++++++++ src/main/java/duke/UI.java | 47 ++++++++++++++- 9 files changed, 283 insertions(+), 55 deletions(-) diff --git a/src/main/java/duke/Deadline.java b/src/main/java/duke/Deadline.java index 59fd47c491..7b630458e9 100644 --- a/src/main/java/duke/Deadline.java +++ b/src/main/java/duke/Deadline.java @@ -10,6 +10,13 @@ public class Deadline extends Task { private String byString; private DateTime byDate; + /** + * Constructs a {@link Task} that has a date associated to it. The date + * associated is normally the date that the {@link Task} should be finished. + * + * @param task the description of the task. + * @param byString the due date of the task. + */ public Deadline(String task, String byString) { super(task); this.byString = byString; diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index a91b25696a..bb52fe6a47 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -4,7 +4,13 @@ public class Duke { private UI ui; private Storage storage; private TaskList tasks; - public Duke(String filePath) { + + /** + * Constructs an instance of the main program, by setting up the + * {@link UI}, {@link Storage}, and loads the existing {@link TaskList} + * or creates a new {@link TaskList} if one does not already exist. + */ + public Duke() { ui = new UI(); storage = new Storage(); try { @@ -15,6 +21,10 @@ public Duke(String filePath) { } } + /** + * Displays start screen. Runs program and keeps the program running until + * user exits. Accepts user input while running. + */ public void run() { ui.startScreen(); boolean isRunning = true; @@ -35,9 +45,16 @@ public void run() { } public static void main(String[] args) { - new Duke("data/tasks.txt").run(); + new Duke().run(); } + /** + * Executes {@link Command} and inputs given. + * + * @param cmd one of the commands given in the enum {@link Command} + * @param inputs the user input the has been split into command and description + * @see Command + */ public void execute(Command cmd, String[] inputs) { try { switch (cmd) { diff --git a/src/main/java/duke/Event.java b/src/main/java/duke/Event.java index 52a0113bd0..428db28a19 100644 --- a/src/main/java/duke/Event.java +++ b/src/main/java/duke/Event.java @@ -10,10 +10,17 @@ public class Event extends Task { private String atString; private DateTime[] atDate; + /** + * Constructs a {@link Task} that have dates associated to it. The date + * associated is normally the date that the {@link Task} occurs and ends. + * + * @param task the description of the task. + * @param atString the start date and end date of the task. + */ public Event(String task, String atString) { super(task); this.atString = atString; - String arr[] = atString.split("\\s*-\\s*", 2); + String[] arr = atString.split("\\s*-\\s*", 2); try { DateTimeParser[] dateParsers = { @@ -53,11 +60,11 @@ public String toString() { + " - " + DateTimeFormat.forPattern("h:mm a").print(atDate[1]); } } else if (this.atDate[0] == null) { - String arr[] = atString.split("\\s+-\\s+", 2); + String[] arr = atString.split("\\s+-\\s+", 2); formattedDate = arr[0] + "-" + DateTimeFormat.forPattern("MMM dd yyyy h:mm a").print(atDate[1]); } else { - String arr[] = atString.split("\\s+-\\s+", 2); + String[] arr = atString.split("\\s+-\\s+", 2); formattedDate = DateTimeFormat.forPattern("MMM dd yyyy h:mm a").print(atDate[0]) + "-" + arr[1]; } diff --git a/src/main/java/duke/FastReader.java b/src/main/java/duke/FastReader.java index bf33639bc3..62ea51ad42 100644 --- a/src/main/java/duke/FastReader.java +++ b/src/main/java/duke/FastReader.java @@ -3,53 +3,54 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.util.Scanner; import java.util.StringTokenizer; public class FastReader { - BufferedReader br; - StringTokenizer st; - - public FastReader() { - br = new BufferedReader( - new InputStreamReader(System.in)); - } - - String next() { - while (st == null || !st.hasMoreElements()) { - try { - st = new StringTokenizer(br.readLine()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - return st.nextToken(); - } - - int nextInt() { - return Integer.parseInt(next()); - } - - long nextLong() { - return Long.parseLong(next()); - } - - double nextDouble() { - return Double.parseDouble(next()); - } - - String nextLine() { - String str = ""; - - try { - str = br.readLine(); - } - catch (IOException e) { - e.printStackTrace(); - } - - return str; - } + private BufferedReader br; + private StringTokenizer st; + + /** + * Constructs a reader that uses {@link BufferedReader}. + * @author Rishabh Mahrsee, GeeksforGeeks + */ + public FastReader() { + br = new BufferedReader(new InputStreamReader(System.in)); + } + + String next() { + while (st == null || !st.hasMoreElements()) { + try { + st = new StringTokenizer(br.readLine()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return st.nextToken(); + } + + int nextInt() { + return Integer.parseInt(next()); + } + + long nextLong() { + return Long.parseLong(next()); + } + + double nextDouble() { + return Double.parseDouble(next()); + } + + String nextLine() { + String str = ""; + + try { + str = br.readLine(); + } catch (IOException e) { + e.printStackTrace(); + } + + return str; + } } diff --git a/src/main/java/duke/Parser.java b/src/main/java/duke/Parser.java index cc6d3bb058..23ccf23595 100644 --- a/src/main/java/duke/Parser.java +++ b/src/main/java/duke/Parser.java @@ -3,11 +3,29 @@ import java.util.ArrayList; public class Parser { + /** + * Returns a command by detecting the command in the user input. + * + * @param inputs user input that has been seperated into command and description. + * @return the command from user input. + * @throws IllegalArgumentException If command is not found in enum {@link Command}. + * @see Command + */ public static Command parseCommand(String[] inputs) throws IllegalArgumentException { Command cmd = Command.valueOf(inputs[0].toUpperCase()); return cmd; } + /** + * Returns index of list that should be marked. + * + * @param tasks the list of tasks saved. + * @param inputs user input that has been seperated into command and description. + * @return the index of the task in the list that should be marked. + * @throws DukeException If index is not given, index given is not an integer or index > size + * of task list. + * @see Task + */ public static int parseMarkIndex(ArrayList tasks, String[] inputs) throws DukeException { if (inputs.length < 2) { throw new DukeException(UI.getIndent() + "☹ OOPS!!! I don't know what to mark"); @@ -22,6 +40,16 @@ public static int parseMarkIndex(ArrayList tasks, String[] inputs) throws return markIndex; } + /** + * Returns index of list that should be unmarked. + * + * @param tasks the list of tasks saved. + * @param inputs user input that has been seperated into command and description. + * @return the index of the task in the list that should be unmarked. + * @throws DukeException If index is not given, index given is not an integer or index > size + * of task list. + * @see Task + */ public static int parseUnmarkIndex(ArrayList tasks, String[] inputs) throws DukeException { if (inputs.length < 2) { throw new DukeException(UI.getIndent() + "☹ OOPS!!! I don't know what to mark"); @@ -36,18 +64,38 @@ public static int parseUnmarkIndex(ArrayList tasks, String[] inputs) throw return unmarkIndex; } + /** + * Check if description of {@link ToDo} is empty. + * + * @param inputs user input that has been seperated into command and description. + * @throws DukeException If input length < 2. + */ public static void checkToDoDescription(String[] inputs) throws DukeException { if (inputs.length < 2) { throw new DukeException(UI.getIndent() + "☹ OOPS!!! The description of a todo cannot be empty"); } } + /** + * Check if description of {@link Deadline} is empty. + * + * @param inputs user input that has been seperated into command and description. + * @throws DukeException If input length < 2. + */ public static void checkDeadlineDescription(String[] inputs) throws DukeException { if (inputs.length < 2) { throw new DukeException(UI.getIndent() + "☹ OOPS!!! The description of a deadline cannot be empty"); } } + /** + * Returns a {@link String} array that separates the task description and the date. + * + * @param inputs user input that has been seperated into command and description. + * @return the description of the task seperated into task description and the date. + * @throws DukeException If date is not given or {@link Deadline} input is not in the right + * format. + */ public static String[] splitDeadlineDate(String[] inputs) throws DukeException { String[] splitByDate = inputs[1].split(" /by "); @@ -58,12 +106,26 @@ public static String[] splitDeadlineDate(String[] inputs) throws DukeException { return splitByDate; } + /** + * Check if description of {@link Event} is empty. + * + * @param inputs user input that has been seperated into command and description. + * @throws DukeException If input length < 2. + */ public static void checkEventDescription(String[] inputs) throws DukeException { if (inputs.length < 2) { throw new DukeException(UI.getIndent() + "☹ OOPS!!! The description of an event cannot be empty"); } } + /** + * Returns a {@link String} array that separates the task description and the dates. + * + * @param inputs user input that has been seperated into command and description. + * @return the description of the task seperated into task description and the dates. + * @throws DukeException If dates are not given or {@link Event} input is not in the + * right format. + */ public static String[] splitEventDate(String[] inputs) throws DukeException { String[] splitAtDate = inputs[1].split(" /at "); @@ -74,6 +136,15 @@ public static String[] splitEventDate(String[] inputs) throws DukeException { return splitAtDate; } + /** + * Returns the index of the list to delete. + * + * @param inputs user input that has been seperated into command and description. + * @param tasks list of tasks saved. + * @return the index of the task in the list to delete. + * @throws DukeException If index is not given, index given is not an integer or index > size + * of task list. + */ public static int parseDeleteIndex(String[] inputs, ArrayList tasks) throws DukeException { if (inputs.length < 2) { throw new DukeException(UI.getIndent() + "☹ OOPS!!! I don't know what to delete"); @@ -88,12 +159,24 @@ public static int parseDeleteIndex(String[] inputs, ArrayList tasks) throw return removeNum; } + /** + * Returns a {@link String} array with the command and description seperated. + * + * @param ui the user interface that accepts user input. + * @return the array with command and description seperated. + */ public static String[] parseInput(UI ui) { String in = ui.read(); String[] inputs = in.split(" ", 2); return inputs; } + /** + * Returns a Task object by parsing entry from saved file. + * + * @param entry a task string in the form saved in file. + * @return the Task object that represents the entry inserted. + */ public static Task parseEntry(String entry) { String[] display = entry.split(" \\| "); Task task = new Task(); diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java index 6532152a23..6873d677f8 100644 --- a/src/main/java/duke/Storage.java +++ b/src/main/java/duke/Storage.java @@ -7,16 +7,19 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; - import java.nio.file.Path; import java.nio.file.Paths; - import java.util.ArrayList; public class Storage { public Storage() { } + /** + * Saves the task list in a file. + * + * @param tasks list of tasks saved. + */ public void save(ArrayList tasks) { String working = System.getProperty("user.dir"); Path path = Paths.get(working, "data"); @@ -44,6 +47,12 @@ public void save(ArrayList tasks) { } } + /** + * Returns data out of saved file as an {@link ArrayList}. + * + * @return the list of entries retrieved from save file. + * @throws DukeException If file could not be used or created. + */ public ArrayList load() throws DukeException { String working = System.getProperty("user.dir"); Path path = Paths.get(working, "data"); diff --git a/src/main/java/duke/Task.java b/src/main/java/duke/Task.java index 53c47c5fd7..522062d9dc 100644 --- a/src/main/java/duke/Task.java +++ b/src/main/java/duke/Task.java @@ -8,6 +8,16 @@ public Task() { this.isDone = false; } + /** + * Constructs a task. + *

+ * Task is initialized as not done. + * + * @param task the task description. + * @return the index of the task in the list to delete. + * @throws DukeException If index is not given, index given is not an integer or index > size + * of task list. + */ public Task(String task) { this.task = task; this.isDone = false; @@ -32,7 +42,7 @@ public int hashCode() { @Override public boolean equals(Object other) { - if (other instanceof Task){ + if (other instanceof Task) { Task toCompare = (Task) other; return this.task.equals(toCompare.getTask()); } @@ -50,6 +60,11 @@ public String toString() { return "[" + temp + "] " + this.task; } + /** + * Returns a {@link String} representing a {@link Task} in the form convenient for record. + * + * @return the text representing the task to record in save file. + */ public String toRecord() { int temp; diff --git a/src/main/java/duke/TaskList.java b/src/main/java/duke/TaskList.java index cdf02056a5..2b78a86506 100644 --- a/src/main/java/duke/TaskList.java +++ b/src/main/java/duke/TaskList.java @@ -5,6 +5,13 @@ public class TaskList { private ArrayList tasks; private ArrayList display; + + /** + * Constructs a list of tasks and converts entries in save file to a list of {@link Task} + * objects. + * + * @param tasks list of tasks saved. + */ public TaskList(ArrayList tasks) { this.tasks = tasks; this.display = new ArrayList<>(); @@ -26,6 +33,13 @@ public void list() { UI.listDisplay(display); } + /** + * Mark a task. + * + * @param inputs user input that has been seperated into command and index. + * @throws DukeException If index is not given, index given is not an integer or + * index > size of task list. + */ public void mark(String[] inputs) throws DukeException { int markIndex = Parser.parseMarkIndex(display, inputs); display.get(markIndex - 1).setDone(); @@ -33,6 +47,13 @@ public void mark(String[] inputs) throws DukeException { UI.markDisplay(display.get(markIndex - 1)); } + /** + * Unmark a task. + * + * @param inputs user input that has been seperated into command and index. + * @throws DukeException If index is not given, index given is not an integer or + * index > size of task list. + */ public void unmark(String[] inputs) throws DukeException { int unmarkIndex = Parser.parseUnmarkIndex(display, inputs); display.get(unmarkIndex - 1).setUndone(); @@ -40,6 +61,12 @@ public void unmark(String[] inputs) throws DukeException { UI.unmarkDisplay(display.get(unmarkIndex - 1)); } + /** + * Add a {@link ToDo} {@link Task}. + * + * @param inputs user input that has been seperated into command and description. + * @throws DukeException If input length < 2. + */ public void addToDo(String[] inputs) throws DukeException { Parser.checkToDoDescription(inputs); Task toDo = new ToDo(inputs[1]); @@ -48,6 +75,12 @@ public void addToDo(String[] inputs) throws DukeException { UI.toDoDisplay(toDo, display); } + /** + * Add a {@link Deadline} {@link Task}. + * + * @param inputs user input that has been seperated into command and description. + * @throws DukeException If input length < 2. + */ public void addDeadline(String[] inputs) throws DukeException { Parser.checkDeadlineDescription(inputs); String[] splitByDate = Parser.splitDeadlineDate(inputs); @@ -58,6 +91,12 @@ public void addDeadline(String[] inputs) throws DukeException { UI.deadlineDisplay(deadline, display); } + /** + * Add a {@link Event} {@link Task}. + * + * @param inputs user input that has been seperated into command and description. + * @throws DukeException If input length < 2. + */ public void addEvent(String[] inputs) throws DukeException { Parser.checkEventDescription(inputs); String[] splitAtDate = Parser.splitEventDate(inputs); @@ -68,6 +107,13 @@ public void addEvent(String[] inputs) throws DukeException { UI.eventDisplay(event, display); } + /** + * Delete a task from the task list. + * + * @param inputs user input that has been seperated into command and index. + * @throws DukeException If index is not given, index given is not an integer or + * index > size of task list. + */ public void delete(String[] inputs) throws DukeException { int deleteIndex = Parser.parseDeleteIndex(inputs, display); Task deletedTask = display.remove(deleteIndex - 1); diff --git a/src/main/java/duke/UI.java b/src/main/java/duke/UI.java index a62a4a6509..56bef56750 100644 --- a/src/main/java/duke/UI.java +++ b/src/main/java/duke/UI.java @@ -3,15 +3,18 @@ import java.util.ArrayList; public class UI { - private FastReader fr; + private static final String[] dateFormat = {"d/MM/yyyy", "yyyy-MM-dd", "HHmm", "d/MM/yyyy HHmm", "yyyy-MM-dd HHmm"}; private static final String INDENT = " "; private static final String SPACE = " "; - private static final String[] dateFormat = {"d/MM/yyyy", "yyyy-MM-dd", "HHmm", "d/MM/yyyy HHmm", "yyyy-MM-dd HHmm"}; + private FastReader fr; public UI() { fr = new FastReader(); } + /** + * Prints the starting screen + */ public void startScreen() { String logo = " ____ _ \n" + "| _ \\ _ _| | _____ \n" @@ -41,6 +44,9 @@ public void byeDisplay() { System.out.println(INDENT + "Bye. Hope to see you again soon!"); } + /** + * Prints the list of tasks. + */ public static void listDisplay(ArrayList tasks) { for (int i = 0; i < tasks.size(); i++) { if (i == 0) { @@ -55,11 +61,21 @@ public static void listDisplay(ArrayList tasks) { } } + /** + * Print marked task. + * + * @param task the marked task. + */ public static void markDisplay(Task task) { System.out.println(INDENT + "Nice! I've marked this task as done:"); System.out.println(INDENT + SPACE + task); } + /** + * Print unmarked task. + * + * @param task the unmarked task. + */ public static void unmarkDisplay(Task task) { System.out.println(INDENT + "OK, I've marked this task as not done yet:"); System.out.println(INDENT + SPACE + task); @@ -73,24 +89,48 @@ public static String getSpace() { return SPACE; } + /** + * Print added {@link ToDo} {@link Task}. + * + * @param toDo the added toDo task. + * @param tasks the list of tasks. + */ public static void toDoDisplay(Task toDo, ArrayList tasks) { System.out.println(INDENT + "Got it. I've added this task:"); System.out.println(INDENT + SPACE + toDo); System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); } + /** + * Print added {@link Deadline} {@link Task}. + * + * @param deadline the added deadline task. + * @param tasks the list of tasks. + */ public static void deadlineDisplay(Task deadline, ArrayList tasks) { System.out.println(INDENT + "Got it. I've added this task:"); System.out.println(INDENT + SPACE + deadline); System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); } + /** + * Print added {@link Event} {@link Task}. + * + * @param event the added event task. + * @param tasks the list of tasks. + */ public static void eventDisplay(Task event, ArrayList tasks) { System.out.println(INDENT + "Got it. I've added this task:"); System.out.println(INDENT + SPACE + event); System.out.printf(INDENT + "Now you have %d task(s) in the list\n", tasks.size()); } + /** + * Print deleted task. + * + * @param deletedTask the deleted task. + * @param tasks the list of tasks. + */ public static void deleteDisplay(Task deletedTask, ArrayList tasks) { System.out.println(INDENT + "Noted. I've removed this task:"); System.out.println(INDENT + SPACE + deletedTask); @@ -105,6 +145,9 @@ public void showError(String e) { System.out.println(e); } + /** + * Print a list of viable date formats for user input. + */ public void showDateFormats() { for (String s : dateFormat) { System.out.println(s); From 1642bc70157819e4eb2aff02ac2734630a9cdfcc Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Fri, 25 Feb 2022 19:29:25 +0800 Subject: [PATCH 33/47] Fix coding standards Fix coding standards for improved readability --- src/main/java/duke/Event.java | 6 +- src/main/java/duke/FastReader.java | 88 ++++++++++++++---------------- src/main/java/duke/Storage.java | 2 - src/main/java/duke/Task.java | 2 +- src/main/java/duke/UI.java | 2 +- 5 files changed, 47 insertions(+), 53 deletions(-) diff --git a/src/main/java/duke/Event.java b/src/main/java/duke/Event.java index 52a0113bd0..3a0e2943d8 100644 --- a/src/main/java/duke/Event.java +++ b/src/main/java/duke/Event.java @@ -13,7 +13,7 @@ public class Event extends Task { public Event(String task, String atString) { super(task); this.atString = atString; - String arr[] = atString.split("\\s*-\\s*", 2); + String[] arr = atString.split("\\s*-\\s*", 2); try { DateTimeParser[] dateParsers = { @@ -53,11 +53,11 @@ public String toString() { + " - " + DateTimeFormat.forPattern("h:mm a").print(atDate[1]); } } else if (this.atDate[0] == null) { - String arr[] = atString.split("\\s+-\\s+", 2); + String[] arr = atString.split("\\s+-\\s+", 2); formattedDate = arr[0] + "-" + DateTimeFormat.forPattern("MMM dd yyyy h:mm a").print(atDate[1]); } else { - String arr[] = atString.split("\\s+-\\s+", 2); + String[] arr = atString.split("\\s+-\\s+", 2); formattedDate = DateTimeFormat.forPattern("MMM dd yyyy h:mm a").print(atDate[0]) + "-" + arr[1]; } diff --git a/src/main/java/duke/FastReader.java b/src/main/java/duke/FastReader.java index bf33639bc3..9ec97f283b 100644 --- a/src/main/java/duke/FastReader.java +++ b/src/main/java/duke/FastReader.java @@ -3,53 +3,49 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.util.Scanner; import java.util.StringTokenizer; public class FastReader { - BufferedReader br; - StringTokenizer st; - - public FastReader() { - br = new BufferedReader( - new InputStreamReader(System.in)); - } - - String next() { - while (st == null || !st.hasMoreElements()) { - try { - st = new StringTokenizer(br.readLine()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - return st.nextToken(); - } - - int nextInt() { - return Integer.parseInt(next()); - } - - long nextLong() { - return Long.parseLong(next()); - } - - double nextDouble() { - return Double.parseDouble(next()); - } - - String nextLine() { - String str = ""; - - try { - str = br.readLine(); - } - catch (IOException e) { - e.printStackTrace(); - } - - return str; - } + private BufferedReader br; + private StringTokenizer st; + + public FastReader() { + br = new BufferedReader(new InputStreamReader(System.in)); + } + + String next() { + while (st == null || !st.hasMoreElements()) { + try { + st = new StringTokenizer(br.readLine()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return st.nextToken(); + } + + int nextInt() { + return Integer.parseInt(next()); + } + + long nextLong() { + return Long.parseLong(next()); + } + + double nextDouble() { + return Double.parseDouble(next()); + } + + String nextLine() { + String str = ""; + + try { + str = br.readLine(); + } catch (IOException e) { + e.printStackTrace(); + } + + return str; + } } - diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java index 6532152a23..6e111078eb 100644 --- a/src/main/java/duke/Storage.java +++ b/src/main/java/duke/Storage.java @@ -7,10 +7,8 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; - import java.nio.file.Path; import java.nio.file.Paths; - import java.util.ArrayList; public class Storage { diff --git a/src/main/java/duke/Task.java b/src/main/java/duke/Task.java index 53c47c5fd7..d8a985be03 100644 --- a/src/main/java/duke/Task.java +++ b/src/main/java/duke/Task.java @@ -32,7 +32,7 @@ public int hashCode() { @Override public boolean equals(Object other) { - if (other instanceof Task){ + if (other instanceof Task) { Task toCompare = (Task) other; return this.task.equals(toCompare.getTask()); } diff --git a/src/main/java/duke/UI.java b/src/main/java/duke/UI.java index a62a4a6509..733dcd6f0e 100644 --- a/src/main/java/duke/UI.java +++ b/src/main/java/duke/UI.java @@ -3,10 +3,10 @@ import java.util.ArrayList; public class UI { - private FastReader fr; private static final String INDENT = " "; private static final String SPACE = " "; private static final String[] dateFormat = {"d/MM/yyyy", "yyyy-MM-dd", "HHmm", "d/MM/yyyy HHmm", "yyyy-MM-dd HHmm"}; + private FastReader fr; public UI() { fr = new FastReader(); From dc491b33374f7f0577548e266b6ffe3127f319b5 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Fri, 25 Feb 2022 20:25:46 +0800 Subject: [PATCH 34/47] Add find function Add find function so users can easily filter tasks with specific keywords --- src/main/java/duke/Command.java | 5 +++++ src/main/java/duke/Duke.java | 3 +++ src/main/java/duke/Parser.java | 8 ++++++++ src/main/java/duke/TaskList.java | 11 +++++++++++ src/main/java/duke/UI.java | 15 +++++++++++++++ 5 files changed, 42 insertions(+) diff --git a/src/main/java/duke/Command.java b/src/main/java/duke/Command.java index a83ec8ae08..226b907397 100644 --- a/src/main/java/duke/Command.java +++ b/src/main/java/duke/Command.java @@ -45,6 +45,11 @@ public boolean isRunning() { public boolean isRunning() { return true; } + }, + FIND { + public boolean isRunning() { + return true; + } }; public abstract boolean isRunning(); diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index a91b25696a..938bee5979 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -74,6 +74,9 @@ public void execute(Command cmd, String[] inputs) { case DATEFORMAT: ui.showDateFormats(); break; + case FIND: + tasks.find(inputs); + break; default: throw new IllegalStateException(UI.getIndent() + "Unexpected value: " + cmd); } diff --git a/src/main/java/duke/Parser.java b/src/main/java/duke/Parser.java index cc6d3bb058..66735e2bef 100644 --- a/src/main/java/duke/Parser.java +++ b/src/main/java/duke/Parser.java @@ -112,4 +112,12 @@ public static Task parseEntry(String entry) { return task; } + + public static String parseKeyword(String[] input) throws DukeException { + if (input.length < 2) { + throw new DukeException(UI.getIndent() + "☹ OOPS!!! I don't know what keyword to look for"); + } + + return input[1]; + } } diff --git a/src/main/java/duke/TaskList.java b/src/main/java/duke/TaskList.java index cdf02056a5..9d0009034b 100644 --- a/src/main/java/duke/TaskList.java +++ b/src/main/java/duke/TaskList.java @@ -74,4 +74,15 @@ public void delete(String[] inputs) throws DukeException { tasks.remove(deleteIndex - 1); UI.deleteDisplay(deletedTask, display); } + + public void find(String[] inputs) throws DukeException { + String keyword = Parser.parseKeyword(inputs); + ArrayList foundTasks = new ArrayList<>(); + for (int i = 0; i < tasks.size(); i++) { + if (tasks.get(i).contains(keyword)) { + foundTasks.add(display.get(i)); + } + } + UI.findDisplay(foundTasks); + } } diff --git a/src/main/java/duke/UI.java b/src/main/java/duke/UI.java index a62a4a6509..0bd5201d44 100644 --- a/src/main/java/duke/UI.java +++ b/src/main/java/duke/UI.java @@ -110,4 +110,19 @@ public void showDateFormats() { System.out.println(s); } } + + public static void findDisplay(ArrayList found) { + if (found.size() < 1) { + System.out.println(INDENT + "I couldn't find any task with that keyword"); + return; + } + + System.out.println(INDENT + "Here are the matching tasks in your list:"); + int i = 1; + + for (Task task : found) { + System.out.printf(INDENT + SPACE + "%d. %s\n", i, task); + i += 1; + } + } } From 5bee49082af9850eb0dada0caf87956215a763f4 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Fri, 25 Feb 2022 20:37:08 +0800 Subject: [PATCH 35/47] Add JavaDoc for find methods Add JavaDoc for find methods for developer clarity --- src/main/java/duke/FastReader.java | 2 +- src/main/java/duke/TaskList.java | 6 ++++++ src/main/java/duke/UI.java | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/duke/FastReader.java b/src/main/java/duke/FastReader.java index c196e54ec7..6fe42a1ebb 100644 --- a/src/main/java/duke/FastReader.java +++ b/src/main/java/duke/FastReader.java @@ -8,7 +8,7 @@ public class FastReader { private BufferedReader br; private StringTokenizer st; - + /** * Constructs a reader that uses {@link BufferedReader}. * @author Rishabh Mahrsee, GeeksforGeeks diff --git a/src/main/java/duke/TaskList.java b/src/main/java/duke/TaskList.java index 330877550c..3bf80a8d43 100644 --- a/src/main/java/duke/TaskList.java +++ b/src/main/java/duke/TaskList.java @@ -121,6 +121,12 @@ public void delete(String[] inputs) throws DukeException { UI.deleteDisplay(deletedTask, display); } + /** + * Find a task from the task list. + * + * @param inputs user input that has been seperated into command and keyword. + * @throws DukeException If keyword is not given. + */ public void find(String[] inputs) throws DukeException { String keyword = Parser.parseKeyword(inputs); ArrayList foundTasks = new ArrayList<>(); diff --git a/src/main/java/duke/UI.java b/src/main/java/duke/UI.java index 2ea7824b90..2dbe14753f 100644 --- a/src/main/java/duke/UI.java +++ b/src/main/java/duke/UI.java @@ -154,6 +154,11 @@ public void showDateFormats() { } } + /** + * Print found tasks. + * + * @param found the list of found tasks containing keyword. + */ public static void findDisplay(ArrayList found) { if (found.size() < 1) { System.out.println(INDENT + "I couldn't find any task with that keyword"); From 2b64ac0a3ad0af341edfd0b3bcff91858b30389a Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Fri, 25 Feb 2022 21:03:27 +0800 Subject: [PATCH 36/47] Make a GFMD --- docs/README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/README.md b/docs/README.md index 8077118ebe..aff8a26993 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,3 +1,33 @@ +# Duke + +> “Your mind is for having ideas, not holding them.” – David Allen [(source)](https://dansilvestre.com/productivity-quotes) + +Duke eases your mind of having to keep track of things. It's, + +- text-based +- simple +- ~~FAST~~ *SUPER* FAST to use + +All you need to do is, +1. download it from here. +2. double-click it. +3. add your tasks. +4. let it manage your tasks for you 😉 + +And it is **FREE**! + +Features: +- [x] Managing tasks +- [ ] Managing deadlines (coming soon) +- [ ] Reminders (coming soon) + +If you are a Java programmer, you can use it to practice Java too. Here's the `main` method: +```aidl +public static void main(String[] args) { + new Duke().run(); +} +``` + # User Guide ## Features From 7145edb136f70b67969e5e7c601bf7578a3a02ec Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Fri, 25 Feb 2022 21:09:35 +0800 Subject: [PATCH 37/47] no message --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index aff8a26993..01c15b15f2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -22,7 +22,7 @@ Features: - [ ] Reminders (coming soon) If you are a Java programmer, you can use it to practice Java too. Here's the `main` method: -```aidl +``` public static void main(String[] args) { new Duke().run(); } From 8d43a6c8adb8c05827a543411e7ec39e5b69e9f6 Mon Sep 17 00:00:00 2001 From: tehkokhoe Date: Sat, 26 Feb 2022 15:14:55 +0800 Subject: [PATCH 38/47] Add GUI Add GUI so it is more accessible --- src/main/java/duke/DialogBox.java | 58 +++++++++++++++++++++++ src/main/java/duke/Duke.java | 59 +++++++++++++++++------- src/main/java/duke/Launcher.java | 12 +++++ src/main/java/duke/Main.java | 27 +++++++++++ src/main/java/duke/MainWindow.java | 46 ++++++++++++++++++ src/main/resources/images/DaDuke.png | Bin 0 -> 32657 bytes src/main/resources/images/DaUser.png | Bin 0 -> 37794 bytes src/main/resources/view/DialogBox.fxml | 16 +++++++ src/main/resources/view/MainWindow.fxml | 19 ++++++++ 9 files changed, 221 insertions(+), 16 deletions(-) create mode 100644 src/main/java/duke/DialogBox.java create mode 100644 src/main/java/duke/Launcher.java create mode 100644 src/main/java/duke/Main.java create mode 100644 src/main/java/duke/MainWindow.java create mode 100644 src/main/resources/images/DaDuke.png create mode 100644 src/main/resources/images/DaUser.png create mode 100644 src/main/resources/view/DialogBox.fxml create mode 100644 src/main/resources/view/MainWindow.fxml diff --git a/src/main/java/duke/DialogBox.java b/src/main/java/duke/DialogBox.java new file mode 100644 index 0000000000..78506ecb07 --- /dev/null +++ b/src/main/java/duke/DialogBox.java @@ -0,0 +1,58 @@ +package duke; + +import java.io.IOException; +import java.util.Collections; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; + +public class DialogBox extends HBox { + @FXML + private Label dialog; + @FXML + private ImageView displayPicture; + + /** + * An example of a custom control using FXML. + * This control represents a dialog box consisting of an ImageView to represent the speaker's face and a label + * containing text from the speaker. + */ + public DialogBox(String text, Image img) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml")); + fxmlLoader.setController(this); + fxmlLoader.setRoot(this); + fxmlLoader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + dialog.setText(text); + displayPicture.setImage(img); + } + + private void flip() { + ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); + Collections.reverse(tmp); + getChildren().setAll(tmp); + setAlignment(Pos.TOP_LEFT); + } + + public static DialogBox getUserDialog(String text, Image img) { + return new DialogBox(text, img); + } + + public static DialogBox getDukeDialog(String text, Image img) { + var db = new DialogBox(text, img); + db.flip(); + return db; + } +} diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index 7ea87b6247..468c266f45 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -1,6 +1,20 @@ package duke; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.layout.VBox; + public class Duke { + private ScrollPane scrollPane; + private VBox dialogContainer; + private TextField userInput; + private Button sendButton; + private Scene scene; + private Image user = new Image(this.getClass().getResourceAsStream("/images/DaUser.png")); + private Image duke = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png")); private UI ui; private Storage storage; private TaskList tasks; @@ -49,60 +63,73 @@ public static void main(String[] args) { } /** - * Executes {@link Command} and inputs given. + * Executes {@link Command} with inputs given and returns a response. * - * @param cmd one of the commands given in the enum {@link Command} - * @param inputs the user input the has been split into command and description + * @param cmd one of the commands given in the enum {@link Command}. + * @param inputs the user input the has been split into command and description. + * @return the response after executing command. * @see Command */ - public void execute(Command cmd, String[] inputs) { + public String execute(Command cmd, String[] inputs) { try { + String result = ""; switch (cmd) { case BYE: ui.byeDisplay(); break; case LIST: - tasks.list(); + result = tasks.list(); break; case MARK: - tasks.mark(inputs); + result = tasks.mark(inputs); storage.save(tasks.getTasks()); break; case UNMARK: - tasks.unmark(inputs); + result = tasks.unmark(inputs); storage.save(tasks.getTasks()); break; case TODO: - tasks.addToDo(inputs); + result = tasks.addToDo(inputs); storage.save(tasks.getTasks()); break; case DEADLINE: - tasks.addDeadline(inputs); + result = tasks.addDeadline(inputs); storage.save(tasks.getTasks()); break; case EVENT: - tasks.addEvent(inputs); + result = tasks.addEvent(inputs); storage.save(tasks.getTasks()); break; case DELETE: - tasks.delete(inputs); + result = tasks.delete(inputs); storage.save(tasks.getTasks()); break; case DATEFORMAT: - ui.showDateFormats(); + result = ui.showDateFormats(); break; case FIND: - tasks.find(inputs); + result = tasks.find(inputs); break; default: throw new IllegalStateException(UI.getIndent() + "Unexpected value: " + cmd); } + return result; } catch (NumberFormatException e) { - ui.showError(UI.getIndent() + "☹ OOPS!!! Task number given is not suitable"); + return ui.showError(UI.getIndent() + "☹ OOPS!!! Task number given is not suitable"); } catch (DukeException | IllegalStateException e) { - ui.showError(e.getMessage()); + return ui.showError(e.getMessage()); + } catch (IllegalArgumentException e) { + return ui.showError(UI.getIndent() + "Invalid command: " + cmd); + } + } + + public String getResponse(String input) { + String[] inputs = Parser.parseGuiInput(input); + try { + Command cmd = Parser.parseCommand(inputs); + return this.execute(cmd, inputs); } catch (IllegalArgumentException e) { - ui.showError(UI.getIndent() + "Invalid command: " + cmd); + return ui.showError(UI.getIndent() + "Invalid Command: " + inputs[0]); } } } diff --git a/src/main/java/duke/Launcher.java b/src/main/java/duke/Launcher.java new file mode 100644 index 0000000000..e4ef6b4628 --- /dev/null +++ b/src/main/java/duke/Launcher.java @@ -0,0 +1,12 @@ +package duke; + +import javafx.application.Application; + +/** + * A launcher class to workaround classpath issues. + */ +public class Launcher { + public static void main(String[] args) { + Application.launch(Main.class, args); + } +} diff --git a/src/main/java/duke/Main.java b/src/main/java/duke/Main.java new file mode 100644 index 0000000000..355e04241d --- /dev/null +++ b/src/main/java/duke/Main.java @@ -0,0 +1,27 @@ +package duke; + +import java.io.IOException; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +public class Main extends Application { + private Duke duke = new Duke(); + + @Override + public void start(Stage stage) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml")); + AnchorPane ap = fxmlLoader.load(); + Scene scene = new Scene(ap); + stage.setScene(scene); + fxmlLoader.getController().setDuke(duke); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/duke/MainWindow.java b/src/main/java/duke/MainWindow.java new file mode 100644 index 0000000000..fc98ddc63e --- /dev/null +++ b/src/main/java/duke/MainWindow.java @@ -0,0 +1,46 @@ +package duke; + +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; + +public class MainWindow extends AnchorPane { + @FXML + private ScrollPane scrollPane; + @FXML + private VBox dialogContainer; + @FXML + private TextField userInput; + @FXML + private Button sendButton; + + private Duke duke; + + private Image userImage = new Image(this.getClass().getResourceAsStream("/images/DaUser.png")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png")); + + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + } + + public void setDuke(Duke d) { + duke = d; + } + + @FXML + private void handleUserInput() { + String input = userInput.getText(); + String response = duke.getResponse(input); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage), + DialogBox.getDukeDialog(response, dukeImage) + ); + userInput.clear(); + } +} + diff --git a/src/main/resources/images/DaDuke.png b/src/main/resources/images/DaDuke.png new file mode 100644 index 0000000000000000000000000000000000000000..d893658717e29b50b4ceedada235d9f75835a118 GIT binary patch literal 32657 zcmV)5K*_&}P)Zf>Tg`t%N;{PE9Y2%-o7`CdniQG#9szpJX+ zcrVK`zK7pG^!n{1T{qo3os(XNkTqW0IXIfGihv^wpO4cHg6YjrzS7VYZJZVWev% z!fK9RcD(ntf zCxMCmC7oRZecuOe6J~p&f5@?I;U2zE;O2T?Vy_8UZ1S4i=b>l%q0jeYAI+i(>K zsIun-!&|SPodfa+5u7Z5yhtd`XRjxuF+Ww~1H4xg)(?GXYLo{q2VPiixh(3*`0iyFHj`V{p-_+Q4jWKC=o6ohr zW)q;Nb7IxtY}qD4RyBpAMv`rR!YytU)sgEIa8F)wX(O<=RwjYY+sLdy0`5o0>=a zd(GePvuhIc}2|`2Y7OgN%4u~XV>q*3e$65qJ;}bK0Li-Wh%~iQn_FhV_*O6|& z$E%S!>BN;~l+|jbw5B2<783HDuPYE-T1Z)&EZTFhZGIj)nseO*|7}7`Vz4%vMl&l& z!c*D(>n3|ulhD`D1FzG)Tik79GgrpJ#HE0O)y{gM#c%L1=xtu`A<9oj(;}Q6cpHb= zIjOSNTO3vD|G|N_tH+^nj?I`D4+@;j;9`s4!*lY>X2_|khHWO LGkWs%c&z?S( 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 z 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 KMNUMnLSTa5`eeEQ literal 0 HcmV?d00001 diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..e433809947 --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 0000000000..01f3c76c70 --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,19 @@ + + + + + + + + + + + +