From 3639bc29fa019eb1788ac1ce277ffa56a8699c02 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Sat, 13 Aug 2022 09:56:34 +0800 Subject: [PATCH 01/67] Add basic bot requirements --- src/main/java/CommandMatcher.java | 25 +++++++++++ src/main/java/Duke.java | 74 ++++++++++++++++++++++++++++--- 2 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 src/main/java/CommandMatcher.java diff --git a/src/main/java/CommandMatcher.java b/src/main/java/CommandMatcher.java new file mode 100644 index 0000000000..3ec27964b2 --- /dev/null +++ b/src/main/java/CommandMatcher.java @@ -0,0 +1,25 @@ +import java.util.function.Consumer; +import java.util.function.Predicate; + +public class CommandMatcher { + private Predicate check; + private Consumer action; + CommandMatcher(Predicate check, Consumer action) { + this.check = check; + this.action = action; + } + + /** + * Checks if the string matches + * If it does, it would execute the action + * @param input The string to check if it is for this command + * @return if the string matches + */ + public boolean run(String input) { + if (check.test(input)) { + action.accept(input); + return true; + } + return false; + } +} diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 5d313334cc..530a6859a4 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,10 +1,72 @@ +import java.util.ArrayList; +import java.util.Scanner; + public class Duke { + /** List of commands */ + private static ArrayList commands; + + /** + * Style a single line + * @param line what is to be printed + */ + public static void messagePrint(String line) { + messagePrint(new String[]{line}); + } + + /** + * Style some lines, given as an array of lines + * @param lines the lines of what is to be printed + */ + public static void messagePrint(String[] lines) { + System.out.println(",----------------------------------------------------------------"); + for (String str : lines) { + System.out.print("| "); + System.out.println(str); + } + System.out.println("'----------------------------------------------------------------"); + } + + private static void greet() { + Duke.messagePrint(new String[]{ + "...where is this again?", + "Oh, hello, I didn't see you there - I'm Anthea, a chatbot...", + "...or at least that's what they told me." }); + } + + private static void leave() { + Duke.messagePrint("It was nice to have you around, I'm going back to sleep..."); + } + + private static void initializeCommands() { + commands = new ArrayList<>(); + + // default command matcher - echo + commands.add(new CommandMatcher( + (str) -> true, + (str) -> Duke.messagePrint(str))); + } + + private static void handleCommand(String command) { + for (CommandMatcher matcher : commands) { + if (matcher.run(command)) { + break; + } + } + } + public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); + greet(); + initializeCommands(); + Scanner input = new Scanner(System.in); + boolean keepRunning = true; + while (keepRunning) { + String command = input.nextLine(); + if (command.equals("bye")) { + keepRunning = false; + } else { + handleCommand(command); + } + } + leave(); } } From c2e2fc30db50e6f486b014220a619b51a8407a0c Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Tue, 16 Aug 2022 13:37:18 +0800 Subject: [PATCH 02/67] Add listing feature Text entered by the user is stored and can be displayed. --- src/main/java/Duke.java | 45 ++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 530a6859a4..9f028e7eaa 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,21 +1,26 @@ import java.util.ArrayList; import java.util.Scanner; +/** + * The main method of the chatbot, as well as its startup and teardown. + */ public class Duke { /** List of commands */ private static ArrayList commands; + /** List of strings to remember */ + private static ArrayList list; /** - * Style a single line - * @param line what is to be printed + * Style and print a single line with a border. + * @param line line to be printed */ public static void messagePrint(String line) { messagePrint(new String[]{line}); } /** - * Style some lines, given as an array of lines - * @param lines the lines of what is to be printed + * Style and print lines with a border. + * @param lines lines to be printed */ public static void messagePrint(String[] lines) { System.out.println(",----------------------------------------------------------------"); @@ -27,10 +32,12 @@ public static void messagePrint(String[] lines) { } private static void greet() { - Duke.messagePrint(new String[]{ - "...where is this again?", - "Oh, hello, I didn't see you there - I'm Anthea, a chatbot...", - "...or at least that's what they told me." }); + String[] greeting = { + "...where is this again?", + "Oh, hello, I didn't see you there - I'm Anthea, a chatbot...", + "...or at least that's what they told me." + }; + Duke.messagePrint(greeting); } private static void leave() { @@ -39,11 +46,21 @@ private static void leave() { private static void initializeCommands() { commands = new ArrayList<>(); + list = new ArrayList<>(); - // default command matcher - echo - commands.add(new CommandMatcher( - (str) -> true, - (str) -> Duke.messagePrint(str))); + commands.add(new CommandMatcher((str) -> str.equals("list"), (str) -> { + String[] output = new String[list.size()]; + for (int i = 0; i < list.size(); i++) { + output[i] = (i + 1) + ". " + list.get(i); + } + Duke.messagePrint(output); + })); + + // default command matcher - add to list + commands.add(new CommandMatcher((str) -> true, (str) -> { + list.add(str); + Duke.messagePrint("added: " + str); + })); } private static void handleCommand(String command) { @@ -54,6 +71,10 @@ private static void handleCommand(String command) { } } + /** + * The main structure of the chatbot execution. + * @param args command line args which are not used + */ public static void main(String[] args) { greet(); initializeCommands(); From 1f5f99b8da0d3c13e6416a92f8ac581c631add4c Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Tue, 16 Aug 2022 14:10:52 +0800 Subject: [PATCH 03/67] Add ability to mark and unmark tasks as done --- src/main/java/CommandMatcher.java | 9 ++++++ src/main/java/Duke.java | 48 ++++++++++++++++++++++++++++--- src/main/java/Task.java | 29 +++++++++++++++++++ 3 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/main/java/Task.java diff --git a/src/main/java/CommandMatcher.java b/src/main/java/CommandMatcher.java index 3ec27964b2..ccb37f488a 100644 --- a/src/main/java/CommandMatcher.java +++ b/src/main/java/CommandMatcher.java @@ -1,6 +1,10 @@ import java.util.function.Consumer; import java.util.function.Predicate; +/** + * This class serves as a way to abstract the idea of making a command + * as a matching process and an action. + */ public class CommandMatcher { private Predicate check; private Consumer action; @@ -9,6 +13,11 @@ public class CommandMatcher { this.action = action; } + CommandMatcher(String prefix, Consumer action) { + this.check = (cmd) -> cmd.startsWith(prefix); + this.action = action; + } + /** * Checks if the string matches * If it does, it would execute the action diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 9f028e7eaa..1c4d88aa96 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,4 +1,5 @@ import java.util.ArrayList; +import java.util.Optional; import java.util.Scanner; /** @@ -8,7 +9,7 @@ public class Duke { /** List of commands */ private static ArrayList commands; /** List of strings to remember */ - private static ArrayList list; + private static ArrayList list; /** * Style and print a single line with a border. @@ -44,21 +45,60 @@ private static void leave() { Duke.messagePrint("It was nice to have you around, I'm going back to sleep..."); } + private static Optional getTask(String index) { + try { + int idx = Integer.parseInt(index); + Task task = list.get(idx - 1); + return Optional.of(task); + } catch (NumberFormatException ex) { + messagePrint("Sorry, I didn't understand " + index + ", please give me a number."); + return Optional.empty(); + } catch (IndexOutOfBoundsException ex) { + messagePrint("Sorry, the number " + index + ", wasn't in the range."); + return Optional.empty(); + } + } + private static void initializeCommands() { commands = new ArrayList<>(); list = new ArrayList<>(); commands.add(new CommandMatcher((str) -> str.equals("list"), (str) -> { - String[] output = new String[list.size()]; + String[] output = new String[list.size() + 1]; + output[0] = "Here, your tasks:"; for (int i = 0; i < list.size(); i++) { - output[i] = (i + 1) + ". " + list.get(i); + output[i + 1] = (i + 1) + "." + list.get(i).toString(); } Duke.messagePrint(output); })); + commands.add(new CommandMatcher("mark ", (str) -> { + String[] parts = str.split(" ", 2); + getTask(parts[1]).ifPresent((task) -> { + task.markAsDone(); + String[] output = { + "Marked your task as done:", + task.toString() + }; + Duke.messagePrint(output); + }); + })); + + commands.add(new CommandMatcher("unmark ", (str) -> { + String[] parts = str.split(" ", 2); + getTask(parts[1]).ifPresent((task) -> { + task.markAsNotDone(); + String[] output = { + "Aw... it's not done yet:", + task.toString() + }; + Duke.messagePrint(output); + }); + })); + // default command matcher - add to list commands.add(new CommandMatcher((str) -> true, (str) -> { - list.add(str); + list.add(new Task(str)); Duke.messagePrint("added: " + str); })); } diff --git a/src/main/java/Task.java b/src/main/java/Task.java new file mode 100644 index 0000000000..cb593b4f8e --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,29 @@ +/** + * A task stores the state and description of a task. + */ +public class Task { + private static final char MARKER_DONE = 'X'; + private static final char MARKER_NOT_DONE = ' '; + private String name; + private boolean done; + Task(String name) { + this.name = name; + this.done = false; + } + + public void markAsDone() { + this.done = true; + } + public void markAsNotDone() { + this.done = false; + } + + private char getDoneMarker() { + return this.done ? MARKER_DONE : MARKER_NOT_DONE; + } + + @Override + public String toString() { + return String.format("[%c] %s", getDoneMarker(), name); + } +} From ea599f713a9583b2620b44828d7f776a06746df7 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Wed, 17 Aug 2022 23:33:36 +0800 Subject: [PATCH 04/67] Add ToDo, Deadline, Event --- src/main/java/Deadline.java | 16 ++++++++++ src/main/java/Duke.java | 40 +++++++++++++++++++++---- src/main/java/Event.java | 16 ++++++++++ src/main/java/PrefixCommandMatcher.java | 27 +++++++++++++++++ src/main/java/ToDo.java | 14 +++++++++ 5 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 src/main/java/Deadline.java create mode 100644 src/main/java/Event.java create mode 100644 src/main/java/PrefixCommandMatcher.java create mode 100644 src/main/java/ToDo.java diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java new file mode 100644 index 0000000000..765e09c462 --- /dev/null +++ b/src/main/java/Deadline.java @@ -0,0 +1,16 @@ +/** + * Handles a task with a deadline + */ +public class Deadline extends Task { + protected String datetime; + + public Deadline(String description, String by) { + super(description); + datetime = by; + } + + @Override + public String toString() { + return String.format("[D]%s (by: %s)", super.toString(), datetime); + } +} \ No newline at end of file diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 1c4d88aa96..a1a5a3feb1 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -72,9 +72,8 @@ private static void initializeCommands() { Duke.messagePrint(output); })); - commands.add(new CommandMatcher("mark ", (str) -> { - String[] parts = str.split(" ", 2); - getTask(parts[1]).ifPresent((task) -> { + commands.add(new PrefixCommandMatcher("mark", (str, map) -> { + getTask(str).ifPresent((task) -> { task.markAsDone(); String[] output = { "Marked your task as done:", @@ -84,9 +83,8 @@ private static void initializeCommands() { }); })); - commands.add(new CommandMatcher("unmark ", (str) -> { - String[] parts = str.split(" ", 2); - getTask(parts[1]).ifPresent((task) -> { + commands.add(new PrefixCommandMatcher("unmark", (str, map) -> { + getTask(str).ifPresent((task) -> { task.markAsNotDone(); String[] output = { "Aw... it's not done yet:", @@ -96,6 +94,36 @@ private static void initializeCommands() { }); })); + commands.add(new PrefixCommandMatcher("deadline", (str, map) -> { + Task task = new Deadline(str, map.getOrDefault("by", "[unknown]")); + list.add(task); + String[] output = { + "Good luck with the deadline, here's the task:", + task.toString() + }; + Duke.messagePrint(output); + })); + + commands.add(new PrefixCommandMatcher("todo", (str, map) -> { + Task task = new ToDo(str); + list.add(task); + String[] output = { + "I've recorded this thing you need to do:", + task.toString() + }; + Duke.messagePrint(output); + })); + + commands.add(new PrefixCommandMatcher("event", (str, map) -> { + Task task = new Event(str, map.getOrDefault("at", "[unknown]")); + list.add(task); + String[] output = { + "That's going to happen at some time later:", + task.toString() + }; + Duke.messagePrint(output); + })); + // default command matcher - add to list commands.add(new CommandMatcher((str) -> true, (str) -> { list.add(new Task(str)); diff --git a/src/main/java/Event.java b/src/main/java/Event.java new file mode 100644 index 0000000000..3a20f99a5c --- /dev/null +++ b/src/main/java/Event.java @@ -0,0 +1,16 @@ +/** + * Handles an event + */ +public class Event extends Task { + protected String datetime; + + public Event(String description, String at) { + super(description); + datetime = at; + } + + @Override + public String toString() { + return String.format("[E]%s (at: %s)", super.toString(), datetime); + } +} diff --git a/src/main/java/PrefixCommandMatcher.java b/src/main/java/PrefixCommandMatcher.java new file mode 100644 index 0000000000..ea1fc3ba79 --- /dev/null +++ b/src/main/java/PrefixCommandMatcher.java @@ -0,0 +1,27 @@ +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; + +/** + * Makes a command matcher based on prefix + * It splits the slash options "/by /at" and other parts as a Map<String, String> + * and trims the string involved. The action takes the String and String + */ +public class PrefixCommandMatcher extends CommandMatcher { + PrefixCommandMatcher(String prefix, BiConsumer> action) { + super((cmd) -> cmd.startsWith(prefix + " "), (cmd) -> { + String withoutPrefix = cmd.split(" ", 2)[1]; + String[] commandParts = withoutPrefix.split(" \\\\"); + Map map = new HashMap<>(); + for (int i = 1; i < commandParts.length; i++) { + String[] keyAndValue = commandParts[i].split(" ", 2); + if (keyAndValue.length == 2) { + map.put(keyAndValue[0].strip(), keyAndValue[1].strip()); + } else { + map.put(keyAndValue[0].strip(), ""); + } + } + action.accept(commandParts[0].strip(), map); + }); + } +} diff --git a/src/main/java/ToDo.java b/src/main/java/ToDo.java new file mode 100644 index 0000000000..4c65967190 --- /dev/null +++ b/src/main/java/ToDo.java @@ -0,0 +1,14 @@ +/** + * A class that stores something to do + */ +public class ToDo extends Task { + + public ToDo(String description) { + super(description); + } + + @Override + public String toString() { + return String.format("[T]%s", super.toString()); + } +} From b285814e52f45381bf67574d7f1948b39c9834b4 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Wed, 17 Aug 2022 23:48:25 +0800 Subject: [PATCH 05/67] Fix bug that backslash is used for commands instead of forward slash Add EXPECTED output for testing --- src/main/java/PrefixCommandMatcher.java | 2 +- text-ui-test/EXPECTED.TXT | 157 ++++++++++++++++++++++-- text-ui-test/input.txt | 31 +++++ 3 files changed, 182 insertions(+), 8 deletions(-) diff --git a/src/main/java/PrefixCommandMatcher.java b/src/main/java/PrefixCommandMatcher.java index ea1fc3ba79..8baa9c7ff6 100644 --- a/src/main/java/PrefixCommandMatcher.java +++ b/src/main/java/PrefixCommandMatcher.java @@ -11,7 +11,7 @@ public class PrefixCommandMatcher extends CommandMatcher { PrefixCommandMatcher(String prefix, BiConsumer> action) { super((cmd) -> cmd.startsWith(prefix + " "), (cmd) -> { String withoutPrefix = cmd.split(" ", 2)[1]; - String[] commandParts = withoutPrefix.split(" \\\\"); + String[] commandParts = withoutPrefix.split(" /"); Map map = new HashMap<>(); for (int i = 1; i < commandParts.length; i++) { String[] keyAndValue = commandParts[i].split(" ", 2); diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e7..0bf63cb0a1 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,150 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - +,---------------------------------------------------------------- +| ...where is this again? +| Oh, hello, I didn't see you there - I'm Anthea, a chatbot... +| ...or at least that's what they told me. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| added: random +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[ ] random +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [X] random +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number 2, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number 0, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number -1, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, I didn't understand not a number, please give me a number. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, I didn't understand not a number, please give me a number. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| I've recorded this thing you need to do: +| [T][ ] homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| I've recorded this thing you need to do: +| [T][ ] other homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| I've recorded this thing you need to do: +| [T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Aw... it's not done yet: +| [ ] random +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Aw... it's not done yet: +| [T][ ] homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[ ] random +| 2.[T][ ] homework +| 3.[T][ ] other homework +| 4.[T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [T][X] other homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[ ] random +| 2.[T][ ] homework +| 3.[T][X] other homework +| 4.[T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [T][X] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[ ] random +| 2.[T][ ] homework +| 3.[T][X] other homework +| 4.[T][X] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number 5, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[ ] random +| 2.[T][ ] homework +| 3.[T][X] other homework +| 4.[T][X] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number 6, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[ ] random +| 2.[T][ ] homework +| 3.[T][X] other homework +| 4.[T][X] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| That's going to happen at some time later: +| [E][ ] meet friends (at: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| That's going to happen at some time later: +| [E][ ] meet people at dinner (at: 6:25 pm) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| That's going to happen at some time later: +| [E][ ] meet people at function x/y/z whatever (at: 7:12am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[ ] random +| 2.[T][ ] homework +| 3.[T][X] other homework +| 4.[T][X] project 12 +| 5.[E][ ] meet friends (at: [unknown]) +| 6.[E][ ] meet people at dinner (at: 6:25 pm) +| 7.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Good luck with the deadline, here's the task: +| [D][ ] /at mysterious (by: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Good luck with the deadline, here's the task: +| [D][ ] /by mysterious number 2 (by: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Good luck with the deadline, here's the task: +| [D][ ] rush this please (by: when?) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[ ] random +| 2.[T][ ] homework +| 3.[T][X] other homework +| 4.[T][X] project 12 +| 5.[E][ ] meet friends (at: [unknown]) +| 6.[E][ ] meet people at dinner (at: 6:25 pm) +| 7.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 8.[D][ ] /at mysterious (by: [unknown]) +| 9.[D][ ] /by mysterious number 2 (by: [unknown]) +| 10.[D][ ] rush this please (by: when?) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| It was nice to have you around, I'm going back to sleep... +'---------------------------------------------------------------- diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb2..611ae7a02d 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,31 @@ +random +list +mark 1 +mark 2 +mark 0 +mark -1 +mark not a number +unmark not a number +todo homework /at /by 1:23 +todo other homework /options ignored +todo project 12 +unmark 1 +unmark 2 +list +mark 3 +list +mark 4 +list +mark 5 +list +mark 6 +list +event meet friends +event meet people at dinner /at 6:25 pm +event meet people at function x/y/z whatever /at oh no not this /by /at 7:12am +list +deadline /at mysterious +deadline /by mysterious number 2 +deadline rush this please /by when? /option is ignored +list +bye From 7be191c7fb2d45a0336c1d8a5d9d2de1cf900f9b Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Thu, 18 Aug 2022 00:00:38 +0800 Subject: [PATCH 06/67] Handle errors --- src/main/java/Duke.java | 3 +- src/main/java/PrefixCommandMatcher.java | 11 ++- text-ui-test/EXPECTED.TXT | 120 ++++++++++++++---------- text-ui-test/input.txt | 13 ++- 4 files changed, 89 insertions(+), 58 deletions(-) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index a1a5a3feb1..aace1da813 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -126,8 +126,7 @@ private static void initializeCommands() { // default command matcher - add to list commands.add(new CommandMatcher((str) -> true, (str) -> { - list.add(new Task(str)); - Duke.messagePrint("added: " + str); + Duke.messagePrint("(>.<') I'm sorry, I don't really know what that means."); })); } diff --git a/src/main/java/PrefixCommandMatcher.java b/src/main/java/PrefixCommandMatcher.java index 8baa9c7ff6..b9711813ec 100644 --- a/src/main/java/PrefixCommandMatcher.java +++ b/src/main/java/PrefixCommandMatcher.java @@ -9,7 +9,11 @@ */ public class PrefixCommandMatcher extends CommandMatcher { PrefixCommandMatcher(String prefix, BiConsumer> action) { - super((cmd) -> cmd.startsWith(prefix + " "), (cmd) -> { + super((cmd) -> cmd.startsWith(prefix + " ") || cmd.equals(prefix), (cmd) -> { + if (cmd.equals(prefix)) { + Duke.messagePrint("(>.<') Add a description to your " + prefix + "."); + return; + } String withoutPrefix = cmd.split(" ", 2)[1]; String[] commandParts = withoutPrefix.split(" /"); Map map = new HashMap<>(); @@ -21,6 +25,11 @@ public class PrefixCommandMatcher extends CommandMatcher { map.put(keyAndValue[0].strip(), ""); } } + commandParts[0] = commandParts[0].strip(); + if (commandParts[0].equals("")) { + Duke.messagePrint("(>.<') The description for " + prefix + " shouldn't be empty."); + return; + } action.accept(commandParts[0].strip(), map); }); } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 0bf63cb0a1..84ab68c473 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -4,30 +4,28 @@ | ...or at least that's what they told me. '---------------------------------------------------------------- ,---------------------------------------------------------------- -| added: random +| (>.<') I'm sorry, I don't really know what that means. '---------------------------------------------------------------- ,---------------------------------------------------------------- -| Here, your tasks: -| 1.[ ] random +| (>.<') I'm sorry, I don't really know what that means. '---------------------------------------------------------------- ,---------------------------------------------------------------- -| Marked your task as done: -| [X] random +| (>.<') Add a description to your todo. '---------------------------------------------------------------- ,---------------------------------------------------------------- -| Sorry, the number 2, wasn't in the range. +| (>.<') Add a description to your deadline. '---------------------------------------------------------------- ,---------------------------------------------------------------- -| Sorry, the number 0, wasn't in the range. +| (>.<') Add a description to your event. '---------------------------------------------------------------- ,---------------------------------------------------------------- -| Sorry, the number -1, wasn't in the range. +| (>.<') Add a description to your mark. '---------------------------------------------------------------- ,---------------------------------------------------------------- -| Sorry, I didn't understand not a number, please give me a number. +| (>.<') Add a description to your unmark. '---------------------------------------------------------------- ,---------------------------------------------------------------- -| Sorry, I didn't understand not a number, please give me a number. +| Here, your tasks: '---------------------------------------------------------------- ,---------------------------------------------------------------- | I've recorded this thing you need to do: @@ -42,61 +40,81 @@ | [T][ ] project 12 '---------------------------------------------------------------- ,---------------------------------------------------------------- -| Aw... it's not done yet: -| [ ] random +| Marked your task as done: +| [T][X] homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [T][X] other homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number 0, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number -1, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, I didn't understand not a number, please give me a number. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, I didn't understand not a number, please give me a number. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][X] homework +| 2.[T][X] other homework +| 3.[T][ ] project 12 '---------------------------------------------------------------- ,---------------------------------------------------------------- | Aw... it's not done yet: | [T][ ] homework '---------------------------------------------------------------- ,---------------------------------------------------------------- +| Aw... it's not done yet: +| [T][ ] other homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- | Here, your tasks: -| 1.[ ] random -| 2.[T][ ] homework -| 3.[T][ ] other homework -| 4.[T][ ] project 12 +| 1.[T][ ] homework +| 2.[T][ ] other homework +| 3.[T][ ] project 12 '---------------------------------------------------------------- ,---------------------------------------------------------------- | Marked your task as done: -| [T][X] other homework +| [T][X] project 12 '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here, your tasks: -| 1.[ ] random -| 2.[T][ ] homework -| 3.[T][X] other homework -| 4.[T][ ] project 12 +| 1.[T][ ] homework +| 2.[T][ ] other homework +| 3.[T][X] project 12 '---------------------------------------------------------------- ,---------------------------------------------------------------- -| Marked your task as done: -| [T][X] project 12 +| Sorry, the number 4, wasn't in the range. '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here, your tasks: -| 1.[ ] random -| 2.[T][ ] homework -| 3.[T][X] other homework -| 4.[T][X] project 12 +| 1.[T][ ] homework +| 2.[T][ ] other homework +| 3.[T][X] project 12 '---------------------------------------------------------------- ,---------------------------------------------------------------- | Sorry, the number 5, wasn't in the range. '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here, your tasks: -| 1.[ ] random -| 2.[T][ ] homework -| 3.[T][X] other homework -| 4.[T][X] project 12 +| 1.[T][ ] homework +| 2.[T][ ] other homework +| 3.[T][X] project 12 '---------------------------------------------------------------- ,---------------------------------------------------------------- | Sorry, the number 6, wasn't in the range. '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here, your tasks: -| 1.[ ] random -| 2.[T][ ] homework -| 3.[T][X] other homework -| 4.[T][X] project 12 +| 1.[T][ ] homework +| 2.[T][ ] other homework +| 3.[T][X] project 12 '---------------------------------------------------------------- ,---------------------------------------------------------------- | That's going to happen at some time later: @@ -112,13 +130,12 @@ '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here, your tasks: -| 1.[ ] random -| 2.[T][ ] homework -| 3.[T][X] other homework -| 4.[T][X] project 12 -| 5.[E][ ] meet friends (at: [unknown]) -| 6.[E][ ] meet people at dinner (at: 6:25 pm) -| 7.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 1.[T][ ] homework +| 2.[T][ ] other homework +| 3.[T][X] project 12 +| 4.[E][ ] meet friends (at: [unknown]) +| 5.[E][ ] meet people at dinner (at: 6:25 pm) +| 6.[E][ ] meet people at function x/y/z whatever (at: 7:12am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | Good luck with the deadline, here's the task: @@ -134,16 +151,15 @@ '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here, your tasks: -| 1.[ ] random -| 2.[T][ ] homework -| 3.[T][X] other homework -| 4.[T][X] project 12 -| 5.[E][ ] meet friends (at: [unknown]) -| 6.[E][ ] meet people at dinner (at: 6:25 pm) -| 7.[E][ ] meet people at function x/y/z whatever (at: 7:12am) -| 8.[D][ ] /at mysterious (by: [unknown]) -| 9.[D][ ] /by mysterious number 2 (by: [unknown]) -| 10.[D][ ] rush this please (by: when?) +| 1.[T][ ] homework +| 2.[T][ ] other homework +| 3.[T][X] project 12 +| 4.[E][ ] meet friends (at: [unknown]) +| 5.[E][ ] meet people at dinner (at: 6:25 pm) +| 6.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 7.[D][ ] /at mysterious (by: [unknown]) +| 8.[D][ ] /by mysterious number 2 (by: [unknown]) +| 9.[D][ ] rush this please (by: when?) '---------------------------------------------------------------- ,---------------------------------------------------------------- | It was nice to have you around, I'm going back to sleep... diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 611ae7a02d..a6267309c9 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,14 +1,21 @@ random + +todo +deadline +event +mark +unmark list +todo homework /at /by 1:23 +todo other homework /options ignored +todo project 12 mark 1 mark 2 mark 0 mark -1 mark not a number unmark not a number -todo homework /at /by 1:23 -todo other homework /options ignored -todo project 12 +list unmark 1 unmark 2 list From 4320c7fa6674a5a3abfdce72d489044841076008 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Thu, 18 Aug 2022 00:11:43 +0800 Subject: [PATCH 07/67] Add deleting of tasks --- src/main/java/Duke.java | 12 ++++++++++++ text-ui-test/EXPECTED.TXT | 32 ++++++++++++++++++++++++++++++++ text-ui-test/input.txt | 7 +++++++ 3 files changed, 51 insertions(+) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index aace1da813..144da5949e 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -124,6 +124,18 @@ private static void initializeCommands() { Duke.messagePrint(output); })); + commands.add(new PrefixCommandMatcher("delete", (str, map) -> { + getTask(str).ifPresent((task) -> { + list.remove(task); + String[] output = { + "It seems you didn't need this task anymore, so I removed it:", + task.toString(), + String.format("You have %d tasks left.", list.size()) + }; + Duke.messagePrint(output); + }); + })); + // default command matcher - add to list commands.add(new CommandMatcher((str) -> true, (str) -> { Duke.messagePrint("(>.<') I'm sorry, I don't really know what that means."); diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 84ab68c473..16cb93c8bd 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -162,5 +162,37 @@ | 9.[D][ ] rush this please (by: when?) '---------------------------------------------------------------- ,---------------------------------------------------------------- +| It seems you didn't need this task anymore, so I removed it: +| [T][ ] other homework +| You have 8 tasks left. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| It seems you didn't need this task anymore, so I removed it: +| [T][X] project 12 +| You have 7 tasks left. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][ ] meet people at dinner (at: 6:25 pm) +| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][ ] /at mysterious (by: [unknown]) +| 6.[D][ ] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number 0, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your delete. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, I didn't understand nan, please give me a number. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number 24, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- | It was nice to have you around, I'm going back to sleep... '---------------------------------------------------------------- diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index a6267309c9..7ef1d45254 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -35,4 +35,11 @@ deadline /at mysterious deadline /by mysterious number 2 deadline rush this please /by when? /option is ignored list +delete 2 +delete 2 +list +delete 0 +delete +delete nan +delete 24 bye From 84bac1781158b85fca6958f750eb342db2098101 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Fri, 19 Aug 2022 21:59:57 +0800 Subject: [PATCH 08/67] Improve documentation, add command robustness to extra spaces --- src/main/java/CommandMatcher.java | 25 ++++++++++++++++++------- src/main/java/Deadline.java | 9 +++++++-- src/main/java/Duke.java | 12 ++++++------ src/main/java/Event.java | 7 ++++++- src/main/java/PrefixCommandMatcher.java | 13 +++++++++---- src/main/java/ToDo.java | 6 +++++- text-ui-test/EXPECTED.TXT | 15 +++++++++++++++ text-ui-test/input.txt | 2 ++ 8 files changed, 68 insertions(+), 21 deletions(-) diff --git a/src/main/java/CommandMatcher.java b/src/main/java/CommandMatcher.java index ccb37f488a..bf3ce705bb 100644 --- a/src/main/java/CommandMatcher.java +++ b/src/main/java/CommandMatcher.java @@ -8,21 +8,32 @@ public class CommandMatcher { private Predicate check; private Consumer action; - CommandMatcher(Predicate check, Consumer action) { + + /** + * Creates an object that handles checking and executing a command. + * @param check Predicate to check if the command should be run. + * @param action Action to run. + */ + public CommandMatcher(Predicate check, Consumer action) { this.check = check; this.action = action; } - CommandMatcher(String prefix, Consumer action) { - this.check = (cmd) -> cmd.startsWith(prefix); + /** + * Creates an object that handles checking and executing a command. + * @param prefix Prefix of the command which is checked. + * @param action Action to run. + */ + public CommandMatcher(String prefix, Consumer action) { + this.check = (cmd) -> cmd.strip().startsWith(prefix); this.action = action; } /** - * Checks if the string matches - * If it does, it would execute the action - * @param input The string to check if it is for this command - * @return if the string matches + * Checks if the string matches. + * If it does, it would execute the action. + * @param input String to check if it is for this command. + * @return If the string matches. */ public boolean run(String input) { if (check.test(input)) { diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 765e09c462..406da81048 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -1,9 +1,14 @@ /** - * Handles a task with a deadline + * Handles a task with a deadline. */ public class Deadline extends Task { protected String datetime; + /** + * Creates a Deadline object. + * @param description Description of deadline. + * @param by Time of deadline. + */ public Deadline(String description, String by) { super(description); datetime = by; @@ -13,4 +18,4 @@ public Deadline(String description, String by) { public String toString() { return String.format("[D]%s (by: %s)", super.toString(), datetime); } -} \ No newline at end of file +} diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 144da5949e..8c90244db8 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -12,16 +12,16 @@ public class Duke { private static ArrayList list; /** - * Style and print a single line with a border. - * @param line line to be printed + * Styles and prints a single line with a border. + * @param line Line to be printed */ public static void messagePrint(String line) { messagePrint(new String[]{line}); } /** - * Style and print lines with a border. - * @param lines lines to be printed + * Styles and prints lines with a border. + * @param lines Lines to be printed */ public static void messagePrint(String[] lines) { System.out.println(",----------------------------------------------------------------"); @@ -151,8 +151,8 @@ private static void handleCommand(String command) { } /** - * The main structure of the chatbot execution. - * @param args command line args which are not used + * Runs the chatbot execution. + * @param args Command line args which are not used. */ public static void main(String[] args) { greet(); diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 3a20f99a5c..02e7b287c9 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -1,9 +1,14 @@ /** - * Handles an event + * Handles an event. */ public class Event extends Task { protected String datetime; + /** + * Creates an event. + * @param description Description of event. + * @param at Time of event. + */ public Event(String description, String at) { super(description); datetime = at; diff --git a/src/main/java/PrefixCommandMatcher.java b/src/main/java/PrefixCommandMatcher.java index b9711813ec..e72af6023e 100644 --- a/src/main/java/PrefixCommandMatcher.java +++ b/src/main/java/PrefixCommandMatcher.java @@ -3,13 +3,18 @@ import java.util.function.BiConsumer; /** - * Makes a command matcher based on prefix + * Makes a command matcher based on prefix. * It splits the slash options "/by /at" and other parts as a Map<String, String> - * and trims the string involved. The action takes the String and String + * and trims the string involved. + * The action takes the String and a map containing the options. */ public class PrefixCommandMatcher extends CommandMatcher { - PrefixCommandMatcher(String prefix, BiConsumer> action) { - super((cmd) -> cmd.startsWith(prefix + " ") || cmd.equals(prefix), (cmd) -> { + /** + * Creates a command matcher that tries to match a prefix. + */ + public PrefixCommandMatcher(String prefix, BiConsumer> action) { + super((cmd) -> cmd.strip().startsWith(prefix + " ") || cmd.strip().equals(prefix), (cmd) -> { + cmd = cmd.strip(); if (cmd.equals(prefix)) { Duke.messagePrint("(>.<') Add a description to your " + prefix + "."); return; diff --git a/src/main/java/ToDo.java b/src/main/java/ToDo.java index 4c65967190..64209e18fa 100644 --- a/src/main/java/ToDo.java +++ b/src/main/java/ToDo.java @@ -1,8 +1,12 @@ /** - * A class that stores something to do + * A class that stores something to do. */ public class ToDo extends Task { + /** + * Creates a todo item. + * @param description Description of todo. + */ public ToDo(String description) { super(description); } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 16cb93c8bd..7c0ae7ecc9 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -194,5 +194,20 @@ | Sorry, the number 24, wasn't in the range. '---------------------------------------------------------------- ,---------------------------------------------------------------- +| That's going to happen at some time later: +| [E][ ] wow much space (at: 10am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][ ] meet people at dinner (at: 6:25 pm) +| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][ ] /at mysterious (by: [unknown]) +| 6.[D][ ] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- | It was nice to have you around, I'm going back to sleep... '---------------------------------------------------------------- diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 7ef1d45254..9c999d0633 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -42,4 +42,6 @@ delete 0 delete delete nan delete 24 + event wow much space / very /wow /at 10am +list bye From 2af3298df72c557c220effedcb918436ebfff07a Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Sat, 20 Aug 2022 14:54:08 +0800 Subject: [PATCH 09/67] Add file I/O for persistent state We need a common interchange format for Tasks to interact with files. We also need a way to extend the testing to deal with the case where the file is missing. Let's add common String[] method getAsStringArray() format for Tasks. To make it easier to make the tasks, let's also add TaskFactory to utilise the Factory design pattern. Let's come up with a new encoding for the file. As Base64 methods are available in Java 11, this can be done by encoding string information for each file in Base64, and separated with commas on a single line. Let's run the test twice, once after deleting the duke.txt file and once after not deleting the file. This would allow us to do regression testing in both cases. --- src/main/java/Deadline.java | 22 ++- src/main/java/Duke.java | 69 +++---- src/main/java/Event.java | 22 ++- src/main/java/FileState.java | 112 +++++++++++ src/main/java/Task.java | 14 +- src/main/java/TaskFactory.java | 46 +++++ src/main/java/TasksFileState.java | 32 ++++ src/main/java/ToDo.java | 25 ++- text-ui-test/ACTUAL1.TXT | 304 ++++++++++++++++++++++++++++++ text-ui-test/EXPECTED.TXT | 4 +- text-ui-test/EXPECTED1.TXT | 304 ++++++++++++++++++++++++++++++ text-ui-test/duke.txt | 16 ++ text-ui-test/runtest.bat | 12 ++ 13 files changed, 931 insertions(+), 51 deletions(-) create mode 100644 src/main/java/FileState.java create mode 100644 src/main/java/TaskFactory.java create mode 100644 src/main/java/TasksFileState.java create mode 100644 text-ui-test/ACTUAL1.TXT create mode 100644 text-ui-test/EXPECTED1.TXT create mode 100644 text-ui-test/duke.txt diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 406da81048..9cc80f9dcb 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -10,7 +10,17 @@ public class Deadline extends Task { * @param by Time of deadline. */ public Deadline(String description, String by) { - super(description); + this(description, by, false); + } + + /** + * Creates a Deadline object. + * @param description Description of deadline. + * @param by Time of deadline. + * @param done If the task is done. + */ + public Deadline(String description, String by, boolean done) { + super(description, done); datetime = by; } @@ -18,4 +28,14 @@ public Deadline(String description, String by) { public String toString() { return String.format("[D]%s (by: %s)", super.toString(), datetime); } + + /** + * Get a string array representation suitable for printing to files. + * @return String array representation. + */ + @Override + public String[] getAsStringArray() { + String[] data = super.getAsStringArray(); + return new String[]{ "Deadline", data[1], data[2], datetime }; + } } diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 8c90244db8..592b113a05 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -11,19 +11,11 @@ public class Duke { /** List of strings to remember */ private static ArrayList list; - /** - * Styles and prints a single line with a border. - * @param line Line to be printed - */ - public static void messagePrint(String line) { - messagePrint(new String[]{line}); - } - /** * Styles and prints lines with a border. * @param lines Lines to be printed */ - public static void messagePrint(String[] lines) { + public static void messagePrint(String... lines) { System.out.println(",----------------------------------------------------------------"); for (String str : lines) { System.out.print("| "); @@ -33,12 +25,9 @@ public static void messagePrint(String[] lines) { } private static void greet() { - String[] greeting = { - "...where is this again?", - "Oh, hello, I didn't see you there - I'm Anthea, a chatbot...", - "...or at least that's what they told me." - }; - Duke.messagePrint(greeting); + Duke.messagePrint("...where is this again?", + "Oh, hello, I didn't see you there - I'm Anthea, a chatbot...", + "...or at least that's what they told me."); } private static void leave() { @@ -59,9 +48,16 @@ private static Optional getTask(String index) { } } + private static void initializeTaskList() { + list = TasksFileState.getTasks(); + } + + private static void finalizeTaskList() { + TasksFileState.saveTasks(list); + } + private static void initializeCommands() { commands = new ArrayList<>(); - list = new ArrayList<>(); commands.add(new CommandMatcher((str) -> str.equals("list"), (str) -> { String[] output = new String[list.size() + 1]; @@ -86,59 +82,44 @@ private static void initializeCommands() { commands.add(new PrefixCommandMatcher("unmark", (str, map) -> { getTask(str).ifPresent((task) -> { task.markAsNotDone(); - String[] output = { - "Aw... it's not done yet:", - task.toString() - }; - Duke.messagePrint(output); + Duke.messagePrint("Aw... it's not done yet:", + task.toString()); }); })); commands.add(new PrefixCommandMatcher("deadline", (str, map) -> { Task task = new Deadline(str, map.getOrDefault("by", "[unknown]")); list.add(task); - String[] output = { - "Good luck with the deadline, here's the task:", - task.toString() - }; - Duke.messagePrint(output); + Duke.messagePrint("Good luck with the deadline, here's the task:", + task.toString()); })); commands.add(new PrefixCommandMatcher("todo", (str, map) -> { Task task = new ToDo(str); list.add(task); - String[] output = { - "I've recorded this thing you need to do:", - task.toString() - }; - Duke.messagePrint(output); + Duke.messagePrint("I've recorded this thing you need to do:", + task.toString()); })); commands.add(new PrefixCommandMatcher("event", (str, map) -> { Task task = new Event(str, map.getOrDefault("at", "[unknown]")); list.add(task); - String[] output = { - "That's going to happen at some time later:", - task.toString() - }; - Duke.messagePrint(output); + Duke.messagePrint("That's going to happen at some time later:", + task.toString()); })); commands.add(new PrefixCommandMatcher("delete", (str, map) -> { getTask(str).ifPresent((task) -> { list.remove(task); - String[] output = { - "It seems you didn't need this task anymore, so I removed it:", - task.toString(), - String.format("You have %d tasks left.", list.size()) - }; - Duke.messagePrint(output); + Duke.messagePrint("It seems you didn't need this task anymore, so I removed it:", + task.toString(), + String.format("You have %d tasks left.", list.size())); }); })); // default command matcher - add to list commands.add(new CommandMatcher((str) -> true, (str) -> { - Duke.messagePrint("(>.<') I'm sorry, I don't really know what that means."); + Duke.messagePrint("(>.<') I'm sorry, I don't really know what that means."); })); } @@ -156,6 +137,7 @@ private static void handleCommand(String command) { */ public static void main(String[] args) { greet(); + initializeTaskList(); initializeCommands(); Scanner input = new Scanner(System.in); boolean keepRunning = true; @@ -167,6 +149,7 @@ public static void main(String[] args) { handleCommand(command); } } + finalizeTaskList(); leave(); } } diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 02e7b287c9..47fc0a6ab4 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -10,7 +10,17 @@ public class Event extends Task { * @param at Time of event. */ public Event(String description, String at) { - super(description); + this(description, at, false); + } + + /** + * Creates an event. + * @param description Description of event. + * @param at Time of event. + * @param done If the task is done. + */ + public Event(String description, String at, boolean done) { + super(description, done); datetime = at; } @@ -18,4 +28,14 @@ public Event(String description, String at) { public String toString() { return String.format("[E]%s (at: %s)", super.toString(), datetime); } + + /** + * Get a string array representation suitable for printing to files. + * @return String array representation. + */ + @Override + public String[] getAsStringArray() { + String[] data = super.getAsStringArray(); + return new String[]{ "Event", data[1], data[2], datetime }; + } } diff --git a/src/main/java/FileState.java b/src/main/java/FileState.java new file mode 100644 index 0000000000..e065d58739 --- /dev/null +++ b/src/main/java/FileState.java @@ -0,0 +1,112 @@ +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Stream; + +/** + * Handles file state. + */ +public class FileState { + private static Map fileStates = new HashMap<>(); + private String contents = null; + private String fileName; + + private FileState(String fileName) { + this.fileName = fileName; + } + + public static FileState getFileState(String fileName) { + if (!fileStates.containsKey(fileName)) { + fileStates.put(fileName, new FileState(fileName)); + } + return fileStates.get(fileName); + } + + /*public String getContents() { + if (contents == null) { + try { + File f = new File(fileName); + Scanner s = new Scanner(f); + StringBuilder sb = new StringBuilder(); + boolean isFirst = true; + while (s.hasNext()) { + if (isFirst) { + isFirst = false; + } else { + sb.append('\n'); + } + sb.append(s.nextLine()); + } + contents = sb.toString(); + } catch (FileNotFoundException ex) { + // file not found or error + contents = ""; + } + } + return contents; + }*/ + + ///** + // * Saves the string to the file on disk + // * @param str string to save + // */ + /*public void saveContents(String str) { + contents = str; + // write to file TODO + }*/ + + private static String toBase64(String input) { + return Base64.getEncoder().encodeToString(input.getBytes(StandardCharsets.UTF_16)); + } + + private static String fromBase64(String input) { + return StandardCharsets.UTF_16.decode(ByteBuffer.wrap(Base64.getDecoder().decode(input))).toString(); + } + + public String[][] getLines() { + ArrayList lines = new ArrayList<>(); + try { + File f = new File(fileName); + Scanner sc = new Scanner(f); + while (sc.hasNext()) { + ArrayList curLine = new ArrayList<>(); + for (String str : sc.nextLine().split(",")) { + curLine.add(fromBase64(str)); + } + lines.add(curLine.toArray(new String[] {})); + } + return (String[][]) lines.toArray(new String[][] {}); + } catch (FileNotFoundException ex) { + // file not found or error + return new String[][] {}; + } + } + + /** + * Saves the string to the file on disk. + * @param strings Strings to save. + */ + public void saveLines(String[][] strings) { + try { + FileWriter writer = new FileWriter(fileName, false); + for (int i = 0; i < strings.length; i++) { + if (i > 0) { + writer.append('\n'); + } + StringBuilder builder = new StringBuilder(); + for (String s : strings[i]) { + builder.append(',').append(toBase64(s)); + } + writer.append(builder.substring(1)); + } + writer.close(); + } catch (IOException ex) { + Duke.messagePrint("(>.<') I was unable to record your tasks..."); + ex.printStackTrace(); + } + } +} diff --git a/src/main/java/Task.java b/src/main/java/Task.java index cb593b4f8e..6b22a6a06a 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -7,8 +7,12 @@ public class Task { private String name; private boolean done; Task(String name) { + this(name, false); + } + + Task(String name, boolean done) { this.name = name; - this.done = false; + this.done = done; } public void markAsDone() { @@ -26,4 +30,12 @@ private char getDoneMarker() { public String toString() { return String.format("[%c] %s", getDoneMarker(), name); } + + /** + * Get a string array representation suitable for printing to files. + * @return String array representation. + */ + public String[] getAsStringArray() { + return new String[]{ "Task", name, String.valueOf(done) }; + } } diff --git a/src/main/java/TaskFactory.java b/src/main/java/TaskFactory.java new file mode 100644 index 0000000000..bacbb01123 --- /dev/null +++ b/src/main/java/TaskFactory.java @@ -0,0 +1,46 @@ +import java.util.Optional; + +/** + * Constructs tasks from strings + */ +public class TaskFactory { + /** + * Constructs the task. + * @param taskData Data for the task. + * @return Task according to taskData. + * @throws IllegalArgumentException If taskData does not conform to the format. + */ + public static Task constructTask(String[] taskData) throws IllegalArgumentException { + if (taskData == null || taskData.length < 1) { + throw new IllegalArgumentException("taskData cannot be null or of length 1"); + } + if (taskData[0].equals("Task") && taskData.length >= 3) { + return new Task(taskData[1], taskData[2].equals("true")); + } + if (taskData[0].equals("ToDo") && taskData.length >= 3) { + return new ToDo(taskData[1], taskData[2].equals("true")); + } + if (taskData[0].equals("Deadline") && taskData.length >= 4) { + return new Deadline(taskData[1], taskData[3], taskData[2].equals("true")); + } + if (taskData[0].equals("Event") && taskData.length >= 4) { + return new Event(taskData[1], taskData[3], taskData[2].equals("true")); + } + throw new IllegalArgumentException("Unsupported task type or incorrect task data length"); + } + + /** + * Constructs the task. + * @param taskData Data for the task. + * @return Optional of Task according to taskData, Optional.empty() if cannot construct. + */ + public static Optional constructOptionalTask(String[] taskData) { + try { + return Optional.of(constructTask(taskData)); + } catch (IllegalArgumentException ex) { + Duke.messagePrint("(>.<') did not understand this task - dropping it", + String.join(", ", taskData)); + return Optional.empty(); + } + } +} diff --git a/src/main/java/TasksFileState.java b/src/main/java/TasksFileState.java new file mode 100644 index 0000000000..de24a99bd0 --- /dev/null +++ b/src/main/java/TasksFileState.java @@ -0,0 +1,32 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Accesses a file for tasks. + */ +public class TasksFileState { + + /** + * Gets ArrayList of previously saved tasks. + * @return ArrayList of tasks. + */ + public static ArrayList getTasks() { + FileState fileState = FileState.getFileState("./duke.txt"); + ArrayList tasks = new ArrayList<>(); + for (String[] line : fileState.getLines()) { + TaskFactory.constructOptionalTask(line).ifPresent((task) -> tasks.add(task)); + } + return tasks; + } + + /** + * Saves a list of tasks to the default file. + * @param tasks List of tasks. + */ + public static void saveTasks(List tasks) { + List lines = tasks.stream().map(Task::getAsStringArray).collect(Collectors.toList()); + FileState fileState = FileState.getFileState("./duke.txt"); + fileState.saveLines((String[][]) lines.toArray(new String[][]{})); + } +} diff --git a/src/main/java/ToDo.java b/src/main/java/ToDo.java index 64209e18fa..584351dc7d 100644 --- a/src/main/java/ToDo.java +++ b/src/main/java/ToDo.java @@ -4,15 +4,34 @@ public class ToDo extends Task { /** - * Creates a todo item. - * @param description Description of todo. + * Creates a task item. + * @param description Description of task. */ public ToDo(String description) { - super(description); + this(description, false); + } + + /** + * Creates a task item. + * @param description Description of task. + * @param done If the task is done. + */ + public ToDo(String description, boolean done) { + super(description, done); } @Override public String toString() { return String.format("[T]%s", super.toString()); } + + /** + * Get a string array representation suitable for printing to files. + * @return String array representation. + */ + @Override + public String[] getAsStringArray() { + String[] data = super.getAsStringArray(); + return new String[]{ "ToDo", data[1], data[2] }; + } } diff --git a/text-ui-test/ACTUAL1.TXT b/text-ui-test/ACTUAL1.TXT new file mode 100644 index 0000000000..daa5869735 --- /dev/null +++ b/text-ui-test/ACTUAL1.TXT @@ -0,0 +1,304 @@ +,---------------------------------------------------------------- +| ...where is this again? +| Oh, hello, I didn't see you there - I'm Anthea, a chatbot... +| ...or at least that's what they told me. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') I'm sorry, I don't really know what that means. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') I'm sorry, I don't really know what that means. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your todo. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your deadline. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your event. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your mark. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your unmark. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][ ] meet people at dinner (at: 6:25 pm) +| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][ ] /at mysterious (by: [unknown]) +| 6.[D][ ] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| I've recorded this thing you need to do: +| [T][ ] homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| I've recorded this thing you need to do: +| [T][ ] other homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| I've recorded this thing you need to do: +| [T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [T][X] homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [E][X] meet friends (at: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number 0, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number -1, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, I didn't understand not a number, please give me a number. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, I didn't understand not a number, please give me a number. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][X] homework +| 2.[E][X] meet friends (at: [unknown]) +| 3.[E][ ] meet people at dinner (at: 6:25 pm) +| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][ ] /at mysterious (by: [unknown]) +| 6.[D][ ] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Aw... it's not done yet: +| [T][ ] homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Aw... it's not done yet: +| [E][ ] meet friends (at: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][ ] meet people at dinner (at: 6:25 pm) +| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][ ] /at mysterious (by: [unknown]) +| 6.[D][ ] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [E][X] meet people at dinner (at: 6:25 pm) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][X] meet people at dinner (at: 6:25 pm) +| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][ ] /at mysterious (by: [unknown]) +| 6.[D][ ] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [E][X] meet people at function x/y/z whatever (at: 7:12am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][X] meet people at dinner (at: 6:25 pm) +| 4.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][ ] /at mysterious (by: [unknown]) +| 6.[D][ ] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [D][X] /at mysterious (by: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][X] meet people at dinner (at: 6:25 pm) +| 4.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][X] /at mysterious (by: [unknown]) +| 6.[D][ ] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [D][X] /by mysterious number 2 (by: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][X] meet people at dinner (at: 6:25 pm) +| 4.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][X] /at mysterious (by: [unknown]) +| 6.[D][X] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| That's going to happen at some time later: +| [E][ ] meet friends (at: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| That's going to happen at some time later: +| [E][ ] meet people at dinner (at: 6:25 pm) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| That's going to happen at some time later: +| [E][ ] meet people at function x/y/z whatever (at: 7:12am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][X] meet people at dinner (at: 6:25 pm) +| 4.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][X] /at mysterious (by: [unknown]) +| 6.[D][X] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +| 12.[E][ ] meet friends (at: [unknown]) +| 13.[E][ ] meet people at dinner (at: 6:25 pm) +| 14.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Good luck with the deadline, here's the task: +| [D][ ] /at mysterious (by: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Good luck with the deadline, here's the task: +| [D][ ] /by mysterious number 2 (by: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Good luck with the deadline, here's the task: +| [D][ ] rush this please (by: when?) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][X] meet people at dinner (at: 6:25 pm) +| 4.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][X] /at mysterious (by: [unknown]) +| 6.[D][X] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +| 12.[E][ ] meet friends (at: [unknown]) +| 13.[E][ ] meet people at dinner (at: 6:25 pm) +| 14.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 15.[D][ ] /at mysterious (by: [unknown]) +| 16.[D][ ] /by mysterious number 2 (by: [unknown]) +| 17.[D][ ] rush this please (by: when?) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| It seems you didn't need this task anymore, so I removed it: +| [E][ ] meet friends (at: [unknown]) +| You have 16 tasks left. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| It seems you didn't need this task anymore, so I removed it: +| [E][X] meet people at dinner (at: 6:25 pm) +| You have 15 tasks left. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 3.[D][X] /at mysterious (by: [unknown]) +| 4.[D][X] /by mysterious number 2 (by: [unknown]) +| 5.[D][ ] rush this please (by: when?) +| 6.[E][ ] wow much space (at: 10am) +| 7.[T][ ] homework +| 8.[T][ ] other homework +| 9.[T][ ] project 12 +| 10.[E][ ] meet friends (at: [unknown]) +| 11.[E][ ] meet people at dinner (at: 6:25 pm) +| 12.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 13.[D][ ] /at mysterious (by: [unknown]) +| 14.[D][ ] /by mysterious number 2 (by: [unknown]) +| 15.[D][ ] rush this please (by: when?) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number 0, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your delete. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, I didn't understand nan, please give me a number. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number 24, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| That's going to happen at some time later: +| [E][ ] wow much space (at: 10am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 3.[D][X] /at mysterious (by: [unknown]) +| 4.[D][X] /by mysterious number 2 (by: [unknown]) +| 5.[D][ ] rush this please (by: when?) +| 6.[E][ ] wow much space (at: 10am) +| 7.[T][ ] homework +| 8.[T][ ] other homework +| 9.[T][ ] project 12 +| 10.[E][ ] meet friends (at: [unknown]) +| 11.[E][ ] meet people at dinner (at: 6:25 pm) +| 12.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 13.[D][ ] /at mysterious (by: [unknown]) +| 14.[D][ ] /by mysterious number 2 (by: [unknown]) +| 15.[D][ ] rush this please (by: when?) +| 16.[E][ ] wow much space (at: 10am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| It was nice to have you around, I'm going back to sleep... +'---------------------------------------------------------------- diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 7c0ae7ecc9..f8387f0ae7 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -4,10 +4,10 @@ | ...or at least that's what they told me. '---------------------------------------------------------------- ,---------------------------------------------------------------- -| (>.<') I'm sorry, I don't really know what that means. +| (>.<') I'm sorry, I don't really know what that means. '---------------------------------------------------------------- ,---------------------------------------------------------------- -| (>.<') I'm sorry, I don't really know what that means. +| (>.<') I'm sorry, I don't really know what that means. '---------------------------------------------------------------- ,---------------------------------------------------------------- | (>.<') Add a description to your todo. diff --git a/text-ui-test/EXPECTED1.TXT b/text-ui-test/EXPECTED1.TXT new file mode 100644 index 0000000000..daa5869735 --- /dev/null +++ b/text-ui-test/EXPECTED1.TXT @@ -0,0 +1,304 @@ +,---------------------------------------------------------------- +| ...where is this again? +| Oh, hello, I didn't see you there - I'm Anthea, a chatbot... +| ...or at least that's what they told me. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') I'm sorry, I don't really know what that means. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') I'm sorry, I don't really know what that means. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your todo. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your deadline. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your event. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your mark. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your unmark. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][ ] meet people at dinner (at: 6:25 pm) +| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][ ] /at mysterious (by: [unknown]) +| 6.[D][ ] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| I've recorded this thing you need to do: +| [T][ ] homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| I've recorded this thing you need to do: +| [T][ ] other homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| I've recorded this thing you need to do: +| [T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [T][X] homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [E][X] meet friends (at: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number 0, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number -1, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, I didn't understand not a number, please give me a number. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, I didn't understand not a number, please give me a number. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][X] homework +| 2.[E][X] meet friends (at: [unknown]) +| 3.[E][ ] meet people at dinner (at: 6:25 pm) +| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][ ] /at mysterious (by: [unknown]) +| 6.[D][ ] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Aw... it's not done yet: +| [T][ ] homework +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Aw... it's not done yet: +| [E][ ] meet friends (at: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][ ] meet people at dinner (at: 6:25 pm) +| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][ ] /at mysterious (by: [unknown]) +| 6.[D][ ] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [E][X] meet people at dinner (at: 6:25 pm) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][X] meet people at dinner (at: 6:25 pm) +| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][ ] /at mysterious (by: [unknown]) +| 6.[D][ ] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [E][X] meet people at function x/y/z whatever (at: 7:12am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][X] meet people at dinner (at: 6:25 pm) +| 4.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][ ] /at mysterious (by: [unknown]) +| 6.[D][ ] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [D][X] /at mysterious (by: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][X] meet people at dinner (at: 6:25 pm) +| 4.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][X] /at mysterious (by: [unknown]) +| 6.[D][ ] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Marked your task as done: +| [D][X] /by mysterious number 2 (by: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][X] meet people at dinner (at: 6:25 pm) +| 4.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][X] /at mysterious (by: [unknown]) +| 6.[D][X] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| That's going to happen at some time later: +| [E][ ] meet friends (at: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| That's going to happen at some time later: +| [E][ ] meet people at dinner (at: 6:25 pm) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| That's going to happen at some time later: +| [E][ ] meet people at function x/y/z whatever (at: 7:12am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][X] meet people at dinner (at: 6:25 pm) +| 4.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][X] /at mysterious (by: [unknown]) +| 6.[D][X] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +| 12.[E][ ] meet friends (at: [unknown]) +| 13.[E][ ] meet people at dinner (at: 6:25 pm) +| 14.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Good luck with the deadline, here's the task: +| [D][ ] /at mysterious (by: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Good luck with the deadline, here's the task: +| [D][ ] /by mysterious number 2 (by: [unknown]) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Good luck with the deadline, here's the task: +| [D][ ] rush this please (by: when?) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][X] meet people at dinner (at: 6:25 pm) +| 4.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 5.[D][X] /at mysterious (by: [unknown]) +| 6.[D][X] /by mysterious number 2 (by: [unknown]) +| 7.[D][ ] rush this please (by: when?) +| 8.[E][ ] wow much space (at: 10am) +| 9.[T][ ] homework +| 10.[T][ ] other homework +| 11.[T][ ] project 12 +| 12.[E][ ] meet friends (at: [unknown]) +| 13.[E][ ] meet people at dinner (at: 6:25 pm) +| 14.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 15.[D][ ] /at mysterious (by: [unknown]) +| 16.[D][ ] /by mysterious number 2 (by: [unknown]) +| 17.[D][ ] rush this please (by: when?) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| It seems you didn't need this task anymore, so I removed it: +| [E][ ] meet friends (at: [unknown]) +| You have 16 tasks left. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| It seems you didn't need this task anymore, so I removed it: +| [E][X] meet people at dinner (at: 6:25 pm) +| You have 15 tasks left. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 3.[D][X] /at mysterious (by: [unknown]) +| 4.[D][X] /by mysterious number 2 (by: [unknown]) +| 5.[D][ ] rush this please (by: when?) +| 6.[E][ ] wow much space (at: 10am) +| 7.[T][ ] homework +| 8.[T][ ] other homework +| 9.[T][ ] project 12 +| 10.[E][ ] meet friends (at: [unknown]) +| 11.[E][ ] meet people at dinner (at: 6:25 pm) +| 12.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 13.[D][ ] /at mysterious (by: [unknown]) +| 14.[D][ ] /by mysterious number 2 (by: [unknown]) +| 15.[D][ ] rush this please (by: when?) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number 0, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your delete. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, I didn't understand nan, please give me a number. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Sorry, the number 24, wasn't in the range. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| That's going to happen at some time later: +| [E][ ] wow much space (at: 10am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here, your tasks: +| 1.[T][ ] homework +| 2.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 3.[D][X] /at mysterious (by: [unknown]) +| 4.[D][X] /by mysterious number 2 (by: [unknown]) +| 5.[D][ ] rush this please (by: when?) +| 6.[E][ ] wow much space (at: 10am) +| 7.[T][ ] homework +| 8.[T][ ] other homework +| 9.[T][ ] project 12 +| 10.[E][ ] meet friends (at: [unknown]) +| 11.[E][ ] meet people at dinner (at: 6:25 pm) +| 12.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +| 13.[D][ ] /at mysterious (by: [unknown]) +| 14.[D][ ] /by mysterious number 2 (by: [unknown]) +| 15.[D][ ] rush this please (by: when?) +| 16.[E][ ] wow much space (at: 10am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| It was nice to have you around, I'm going back to sleep... +'---------------------------------------------------------------- diff --git a/text-ui-test/duke.txt b/text-ui-test/duke.txt new file mode 100644 index 0000000000..78ee9ae569 --- /dev/null +++ b/text-ui-test/duke.txt @@ -0,0 +1,16 @@ +/v8AVABvAEQAbw==,/v8AaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl +/v8ARQB2AGUAbgB0,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZgB1AG4AYwB0AGkAbwBuACAAeAAvAHkALwB6ACAAdwBoAGEAdABlAHYAZQBy,/v8AdAByAHUAZQ==,/v8ANwA6ADEAMgBhAG0= +/v8ARABlAGEAZABsAGkAbgBl,/v8ALwBhAHQAIABtAHkAcwB0AGUAcgBpAG8AdQBz,/v8AdAByAHUAZQ==,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8ARABlAGEAZABsAGkAbgBl,/v8ALwBiAHkAIABtAHkAcwB0AGUAcgBpAG8AdQBzACAAbgB1AG0AYgBlAHIAIAAy,/v8AdAByAHUAZQ==,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8ARABlAGEAZABsAGkAbgBl,/v8AcgB1AHMAaAAgAHQAaABpAHMAIABwAGwAZQBhAHMAZQ==,/v8AZgBhAGwAcwBl,/v8AdwBoAGUAbgA/ +/v8ARQB2AGUAbgB0,/v8AdwBvAHcAIAAgAG0AdQBjAGgAIAAgAHMAcABhAGMAZQ==,/v8AZgBhAGwAcwBl,/v8AMQAwAGEAbQ== +/v8AVABvAEQAbw==,/v8AaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl +/v8AVABvAEQAbw==,/v8AbwB0AGgAZQByACAAaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl +/v8AVABvAEQAbw==,/v8AcAByAG8AagBlAGMAdAAgADEAMg==,/v8AZgBhAGwAcwBl +/v8ARQB2AGUAbgB0,/v8AbQBlAGUAdAAgAGYAcgBpAGUAbgBkAHM=,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8ARQB2AGUAbgB0,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZABpAG4AbgBlAHI=,/v8AZgBhAGwAcwBl,/v8ANgA6ADIANQAgAHAAbQ== +/v8ARQB2AGUAbgB0,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZgB1AG4AYwB0AGkAbwBuACAAeAAvAHkALwB6ACAAdwBoAGEAdABlAHYAZQBy,/v8AZgBhAGwAcwBl,/v8ANwA6ADEAMgBhAG0= +/v8ARABlAGEAZABsAGkAbgBl,/v8ALwBhAHQAIABtAHkAcwB0AGUAcgBpAG8AdQBz,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8ARABlAGEAZABsAGkAbgBl,/v8ALwBiAHkAIABtAHkAcwB0AGUAcgBpAG8AdQBzACAAbgB1AG0AYgBlAHIAIAAy,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8ARABlAGEAZABsAGkAbgBl,/v8AcgB1AHMAaAAgAHQAaABpAHMAIABwAGwAZQBhAHMAZQ==,/v8AZgBhAGwAcwBl,/v8AdwBoAGUAbgA/ +/v8ARQB2AGUAbgB0,/v8AdwBvAHcAIAAgAG0AdQBjAGgAIAAgAHMAcABhAGMAZQ==,/v8AZgBhAGwAcwBl,/v8AMQAwAGEAbQ== \ No newline at end of file diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 0873744649..74c2478cc0 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -14,8 +14,20 @@ IF ERRORLEVEL 1 ( ) REM no error here, errorlevel == 0 +REM delete data +if exist duke.txt del duke.txt + REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT java -classpath ..\bin Duke < input.txt > ACTUAL.TXT REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT + +REM delete output from previous run +if exist ACTUAL1.TXT del ACTUAL1.TXT + +REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL1.TXT +java -classpath ..\bin Duke < input.txt > ACTUAL1.TXT + +REM compare the output to the expected output +FC ACTUAL1.TXT EXPECTED1.TXT From edc2f9217143e75913c455c401f441a22fc717ec Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Sat, 20 Aug 2022 21:41:28 +0800 Subject: [PATCH 10/67] Add capability to recognise some date/time formats --- src/main/java/Deadline.java | 6 ++--- src/main/java/Event.java | 6 ++--- src/main/java/ParsedDateTime.java | 39 +++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 src/main/java/ParsedDateTime.java diff --git a/src/main/java/Deadline.java b/src/main/java/Deadline.java index 406da81048..c1fb2d7546 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/Deadline.java @@ -2,7 +2,7 @@ * Handles a task with a deadline. */ public class Deadline extends Task { - protected String datetime; + protected ParsedDateTime datetime; /** * Creates a Deadline object. @@ -11,11 +11,11 @@ public class Deadline extends Task { */ public Deadline(String description, String by) { super(description); - datetime = by; + datetime = new ParsedDateTime(by); } @Override public String toString() { - return String.format("[D]%s (by: %s)", super.toString(), datetime); + return String.format("[D]%s (by: %s)", super.toString(), datetime.toString()); } } diff --git a/src/main/java/Event.java b/src/main/java/Event.java index 02e7b287c9..5955ad5b44 100644 --- a/src/main/java/Event.java +++ b/src/main/java/Event.java @@ -2,7 +2,7 @@ * Handles an event. */ public class Event extends Task { - protected String datetime; + protected ParsedDateTime datetime; /** * Creates an event. @@ -11,11 +11,11 @@ public class Event extends Task { */ public Event(String description, String at) { super(description); - datetime = at; + datetime = new ParsedDateTime(at); } @Override public String toString() { - return String.format("[E]%s (at: %s)", super.toString(), datetime); + return String.format("[E]%s (at: %s)", super.toString(), datetime.toString()); } } diff --git a/src/main/java/ParsedDateTime.java b/src/main/java/ParsedDateTime.java new file mode 100644 index 0000000000..131088e10d --- /dev/null +++ b/src/main/java/ParsedDateTime.java @@ -0,0 +1,39 @@ +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.Optional; + +/** + * Helper class to parse date/time strings + */ +public class ParsedDateTime { + private static final DateTimeFormatter[] formatters = { + DateTimeFormatter.ofPattern("yyyy-MM-dd"), DateTimeFormatter.ofPattern("d/M/yyyy HHmm"), + DateTimeFormatter.BASIC_ISO_DATE, DateTimeFormatter.ISO_LOCAL_DATE, DateTimeFormatter.ISO_OFFSET_DATE, + DateTimeFormatter.ISO_DATE, DateTimeFormatter.ISO_LOCAL_TIME, DateTimeFormatter.ISO_OFFSET_TIME, + DateTimeFormatter.ISO_TIME, DateTimeFormatter.ISO_LOCAL_DATE_TIME, DateTimeFormatter.ISO_OFFSET_DATE_TIME, + DateTimeFormatter.ISO_ZONED_DATE_TIME, DateTimeFormatter.ISO_DATE_TIME, DateTimeFormatter.ISO_ORDINAL_DATE, + DateTimeFormatter.ISO_WEEK_DATE, DateTimeFormatter.ISO_INSTANT, DateTimeFormatter.RFC_1123_DATE_TIME }; + + private Optional parsedDateTime; + private String input; + ParsedDateTime(String input) { + this.input = input; + parsedDateTime = Optional.empty(); + for (DateTimeFormatter formatter : formatters) { + try { + parsedDateTime = Optional.of(LocalDateTime.parse(input, formatter)); + break; + } catch (DateTimeParseException ex) { + // Just try another one + } + } + } + + @Override + public String toString() { + return parsedDateTime.map((dateTime) -> { + return dateTime.format(DateTimeFormatter.ofPattern("dd MMM yyyy HHmm")); + }).orElse(input); + } +} From 1ed84f9eb227a522e013b2af819f8d2cd79a5a01 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Sat, 20 Aug 2022 22:00:06 +0800 Subject: [PATCH 11/67] Add Ui class to handle user interface outputs --- .gitignore | 1 + src/main/java/Duke.java | 47 +++++++------------------ src/main/java/FileState.java | 2 +- src/main/java/PrefixCommandMatcher.java | 4 +-- src/main/java/TaskFactory.java | 2 +- src/main/java/Ui.java | 39 ++++++++++++++++++++ 6 files changed, 56 insertions(+), 39 deletions(-) create mode 100644 src/main/java/Ui.java diff --git a/.gitignore b/.gitignore index f69985ef1f..7f4ac88c9b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ bin/ /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT +duke.txt \ No newline at end of file diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 592b113a05..7bf6ab446b 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -11,39 +11,16 @@ public class Duke { /** List of strings to remember */ private static ArrayList list; - /** - * Styles and prints lines with a border. - * @param lines Lines to be printed - */ - public static void messagePrint(String... lines) { - System.out.println(",----------------------------------------------------------------"); - for (String str : lines) { - System.out.print("| "); - System.out.println(str); - } - System.out.println("'----------------------------------------------------------------"); - } - - private static void greet() { - Duke.messagePrint("...where is this again?", - "Oh, hello, I didn't see you there - I'm Anthea, a chatbot...", - "...or at least that's what they told me."); - } - - private static void leave() { - Duke.messagePrint("It was nice to have you around, I'm going back to sleep..."); - } - private static Optional getTask(String index) { try { int idx = Integer.parseInt(index); Task task = list.get(idx - 1); return Optional.of(task); } catch (NumberFormatException ex) { - messagePrint("Sorry, I didn't understand " + index + ", please give me a number."); + Ui.messagePrint("Sorry, I didn't understand " + index + ", please give me a number."); return Optional.empty(); } catch (IndexOutOfBoundsException ex) { - messagePrint("Sorry, the number " + index + ", wasn't in the range."); + Ui.messagePrint("Sorry, the number " + index + ", wasn't in the range."); return Optional.empty(); } } @@ -65,7 +42,7 @@ private static void initializeCommands() { for (int i = 0; i < list.size(); i++) { output[i + 1] = (i + 1) + "." + list.get(i).toString(); } - Duke.messagePrint(output); + Ui.messagePrint(output); })); commands.add(new PrefixCommandMatcher("mark", (str, map) -> { @@ -75,14 +52,14 @@ private static void initializeCommands() { "Marked your task as done:", task.toString() }; - Duke.messagePrint(output); + Ui.messagePrint(output); }); })); commands.add(new PrefixCommandMatcher("unmark", (str, map) -> { getTask(str).ifPresent((task) -> { task.markAsNotDone(); - Duke.messagePrint("Aw... it's not done yet:", + Ui.messagePrint("Aw... it's not done yet:", task.toString()); }); })); @@ -90,28 +67,28 @@ private static void initializeCommands() { commands.add(new PrefixCommandMatcher("deadline", (str, map) -> { Task task = new Deadline(str, map.getOrDefault("by", "[unknown]")); list.add(task); - Duke.messagePrint("Good luck with the deadline, here's the task:", + Ui.messagePrint("Good luck with the deadline, here's the task:", task.toString()); })); commands.add(new PrefixCommandMatcher("todo", (str, map) -> { Task task = new ToDo(str); list.add(task); - Duke.messagePrint("I've recorded this thing you need to do:", + Ui.messagePrint("I've recorded this thing you need to do:", task.toString()); })); commands.add(new PrefixCommandMatcher("event", (str, map) -> { Task task = new Event(str, map.getOrDefault("at", "[unknown]")); list.add(task); - Duke.messagePrint("That's going to happen at some time later:", + Ui.messagePrint("That's going to happen at some time later:", task.toString()); })); commands.add(new PrefixCommandMatcher("delete", (str, map) -> { getTask(str).ifPresent((task) -> { list.remove(task); - Duke.messagePrint("It seems you didn't need this task anymore, so I removed it:", + Ui.messagePrint("It seems you didn't need this task anymore, so I removed it:", task.toString(), String.format("You have %d tasks left.", list.size())); }); @@ -119,7 +96,7 @@ private static void initializeCommands() { // default command matcher - add to list commands.add(new CommandMatcher((str) -> true, (str) -> { - Duke.messagePrint("(>.<') I'm sorry, I don't really know what that means."); + Ui.messagePrint("(>.<') I'm sorry, I don't really know what that means."); })); } @@ -136,7 +113,7 @@ private static void handleCommand(String command) { * @param args Command line args which are not used. */ public static void main(String[] args) { - greet(); + Ui.greet(); initializeTaskList(); initializeCommands(); Scanner input = new Scanner(System.in); @@ -150,6 +127,6 @@ public static void main(String[] args) { } } finalizeTaskList(); - leave(); + Ui.leave(); } } diff --git a/src/main/java/FileState.java b/src/main/java/FileState.java index e065d58739..01a06cf84b 100644 --- a/src/main/java/FileState.java +++ b/src/main/java/FileState.java @@ -105,7 +105,7 @@ public void saveLines(String[][] strings) { } writer.close(); } catch (IOException ex) { - Duke.messagePrint("(>.<') I was unable to record your tasks..."); + Ui.messagePrint("(>.<') I was unable to record your tasks..."); ex.printStackTrace(); } } diff --git a/src/main/java/PrefixCommandMatcher.java b/src/main/java/PrefixCommandMatcher.java index e72af6023e..2e42a0171a 100644 --- a/src/main/java/PrefixCommandMatcher.java +++ b/src/main/java/PrefixCommandMatcher.java @@ -16,7 +16,7 @@ public PrefixCommandMatcher(String prefix, BiConsumer cmd.strip().startsWith(prefix + " ") || cmd.strip().equals(prefix), (cmd) -> { cmd = cmd.strip(); if (cmd.equals(prefix)) { - Duke.messagePrint("(>.<') Add a description to your " + prefix + "."); + Ui.messagePrint("(>.<') Add a description to your " + prefix + "."); return; } String withoutPrefix = cmd.split(" ", 2)[1]; @@ -32,7 +32,7 @@ public PrefixCommandMatcher(String prefix, BiConsumer.<') The description for " + prefix + " shouldn't be empty."); + Ui.messagePrint("(>.<') The description for " + prefix + " shouldn't be empty."); return; } action.accept(commandParts[0].strip(), map); diff --git a/src/main/java/TaskFactory.java b/src/main/java/TaskFactory.java index bacbb01123..3b283b692e 100644 --- a/src/main/java/TaskFactory.java +++ b/src/main/java/TaskFactory.java @@ -38,7 +38,7 @@ public static Optional constructOptionalTask(String[] taskData) { try { return Optional.of(constructTask(taskData)); } catch (IllegalArgumentException ex) { - Duke.messagePrint("(>.<') did not understand this task - dropping it", + Ui.messagePrint("(>.<') did not understand this task - dropping it", String.join(", ", taskData)); return Optional.empty(); } diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java new file mode 100644 index 0000000000..e5163fbb29 --- /dev/null +++ b/src/main/java/Ui.java @@ -0,0 +1,39 @@ +import java.util.ArrayList; +import java.util.Optional; +import java.util.Scanner; + +public class Ui { + /** List of commands */ + private static ArrayList commands; + /** List of strings to remember */ + private static ArrayList list; + + /** + * Styles and prints lines with a border. + * @param lines Lines to be printed + */ + public static void messagePrint(String... lines) { + System.out.println(",----------------------------------------------------------------"); + for (String str : lines) { + System.out.print("| "); + System.out.println(str); + } + System.out.println("'----------------------------------------------------------------"); + } + + /** + * Greets user. + */ + public static void greet() { + Ui.messagePrint("...where is this again?", + "Oh, hello, I didn't see you there - I'm Anthea, a chatbot...", + "...or at least that's what they told me."); + } + + /** + * Leaves the user. + */ + public static void leave() { + Ui.messagePrint("It was nice to have you around, I'm going back to sleep..."); + } +} From bb7ed9ce9f866344816b51ac67778c81b3cc498f Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Sat, 20 Aug 2022 22:20:54 +0800 Subject: [PATCH 12/67] Extract Storage, Parser, TaskList classes --- src/main/java/Duke.java | 85 +------------------ src/main/java/Parser.java | 75 ++++++++++++++++ .../java/{FileState.java => Storage.java} | 11 ++- src/main/java/TaskList.java | 39 +++++++++ .../{TasksFileState.java => TaskStorage.java} | 10 +-- 5 files changed, 128 insertions(+), 92 deletions(-) create mode 100644 src/main/java/Parser.java rename src/main/java/{FileState.java => Storage.java} (88%) create mode 100644 src/main/java/TaskList.java rename src/main/java/{TasksFileState.java => TaskStorage.java} (68%) diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index 7bf6ab446b..7dd662bb87 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -8,13 +8,11 @@ public class Duke { /** List of commands */ private static ArrayList commands; - /** List of strings to remember */ - private static ArrayList list; private static Optional getTask(String index) { try { int idx = Integer.parseInt(index); - Task task = list.get(idx - 1); + Task task = TaskList.getTaskList().get(idx - 1); return Optional.of(task); } catch (NumberFormatException ex) { Ui.messagePrint("Sorry, I didn't understand " + index + ", please give me a number."); @@ -25,81 +23,6 @@ private static Optional getTask(String index) { } } - private static void initializeTaskList() { - list = TasksFileState.getTasks(); - } - - private static void finalizeTaskList() { - TasksFileState.saveTasks(list); - } - - private static void initializeCommands() { - commands = new ArrayList<>(); - - commands.add(new CommandMatcher((str) -> str.equals("list"), (str) -> { - String[] output = new String[list.size() + 1]; - output[0] = "Here, your tasks:"; - for (int i = 0; i < list.size(); i++) { - output[i + 1] = (i + 1) + "." + list.get(i).toString(); - } - Ui.messagePrint(output); - })); - - commands.add(new PrefixCommandMatcher("mark", (str, map) -> { - getTask(str).ifPresent((task) -> { - task.markAsDone(); - String[] output = { - "Marked your task as done:", - task.toString() - }; - Ui.messagePrint(output); - }); - })); - - commands.add(new PrefixCommandMatcher("unmark", (str, map) -> { - getTask(str).ifPresent((task) -> { - task.markAsNotDone(); - Ui.messagePrint("Aw... it's not done yet:", - task.toString()); - }); - })); - - commands.add(new PrefixCommandMatcher("deadline", (str, map) -> { - Task task = new Deadline(str, map.getOrDefault("by", "[unknown]")); - list.add(task); - Ui.messagePrint("Good luck with the deadline, here's the task:", - task.toString()); - })); - - commands.add(new PrefixCommandMatcher("todo", (str, map) -> { - Task task = new ToDo(str); - list.add(task); - Ui.messagePrint("I've recorded this thing you need to do:", - task.toString()); - })); - - commands.add(new PrefixCommandMatcher("event", (str, map) -> { - Task task = new Event(str, map.getOrDefault("at", "[unknown]")); - list.add(task); - Ui.messagePrint("That's going to happen at some time later:", - task.toString()); - })); - - commands.add(new PrefixCommandMatcher("delete", (str, map) -> { - getTask(str).ifPresent((task) -> { - list.remove(task); - Ui.messagePrint("It seems you didn't need this task anymore, so I removed it:", - task.toString(), - String.format("You have %d tasks left.", list.size())); - }); - })); - - // default command matcher - add to list - commands.add(new CommandMatcher((str) -> true, (str) -> { - Ui.messagePrint("(>.<') I'm sorry, I don't really know what that means."); - })); - } - private static void handleCommand(String command) { for (CommandMatcher matcher : commands) { if (matcher.run(command)) { @@ -114,8 +37,8 @@ private static void handleCommand(String command) { */ public static void main(String[] args) { Ui.greet(); - initializeTaskList(); - initializeCommands(); + TaskList.initializeTaskList(); + commands = Parser.getCommands(); Scanner input = new Scanner(System.in); boolean keepRunning = true; while (keepRunning) { @@ -126,7 +49,7 @@ public static void main(String[] args) { handleCommand(command); } } - finalizeTaskList(); + TaskList.finalizeTaskList(); Ui.leave(); } } diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java new file mode 100644 index 0000000000..17d81c3d0a --- /dev/null +++ b/src/main/java/Parser.java @@ -0,0 +1,75 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * Handles creating commands through CommandMatcher/PrefixCommandMatcher. + */ +public class Parser { + + public static ArrayList getCommands() { + ArrayList commands = new ArrayList<>(); + + commands.add(new CommandMatcher((str) -> str.equals("list"), (str) -> { + List list = TaskList.getTaskList(); + String[] output = new String[list.size() + 1]; + output[0] = "Here, your tasks:"; + for (int i = 0; i < list.size(); i++) { + output[i + 1] = (i + 1) + "." + list.get(i).toString(); + } + Ui.messagePrint(output); + })); + + commands.add(new PrefixCommandMatcher("mark", (str, map) -> { + TaskList.getTask(str).ifPresent((task) -> { + task.markAsDone(); + Ui.messagePrint("Marked your task as done:", + task.toString()); + }); + })); + + commands.add(new PrefixCommandMatcher("unmark", (str, map) -> { + TaskList.getTask(str).ifPresent((task) -> { + task.markAsNotDone(); + Ui.messagePrint("Aw... it's not done yet:", + task.toString()); + }); + })); + + commands.add(new PrefixCommandMatcher("deadline", (str, map) -> { + Task task = new Deadline(str, map.getOrDefault("by", "[unknown]")); + TaskList.getTaskList().add(task); + Ui.messagePrint("Good luck with the deadline, here's the task:", + task.toString()); + })); + + commands.add(new PrefixCommandMatcher("todo", (str, map) -> { + Task task = new ToDo(str); + TaskList.getTaskList().add(task); + Ui.messagePrint("I've recorded this thing you need to do:", + task.toString()); + })); + + commands.add(new PrefixCommandMatcher("event", (str, map) -> { + Task task = new Event(str, map.getOrDefault("at", "[unknown]")); + TaskList.getTaskList().add(task); + Ui.messagePrint("That's going to happen at some time later:", + task.toString()); + })); + + commands.add(new PrefixCommandMatcher("delete", (str, map) -> { + TaskList.getTask(str).ifPresent((task) -> { + TaskList.getTaskList().remove(task); + Ui.messagePrint("It seems you didn't need this task anymore, so I removed it:", + task.toString(), + String.format("You have %d tasks left.", TaskList.getTaskList().size())); + }); + })); + + // default command matcher - add to list + commands.add(new CommandMatcher((str) -> true, (str) -> { + Ui.messagePrint("(>.<') I'm sorry, I don't really know what that means."); + })); + + return commands; + } +} diff --git a/src/main/java/FileState.java b/src/main/java/Storage.java similarity index 88% rename from src/main/java/FileState.java rename to src/main/java/Storage.java index 01a06cf84b..e90157bee6 100644 --- a/src/main/java/FileState.java +++ b/src/main/java/Storage.java @@ -5,23 +5,22 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.*; -import java.util.stream.Stream; /** * Handles file state. */ -public class FileState { - private static Map fileStates = new HashMap<>(); +public class Storage { + private static Map fileStates = new HashMap<>(); private String contents = null; private String fileName; - private FileState(String fileName) { + private Storage(String fileName) { this.fileName = fileName; } - public static FileState getFileState(String fileName) { + public static Storage getFileState(String fileName) { if (!fileStates.containsKey(fileName)) { - fileStates.put(fileName, new FileState(fileName)); + fileStates.put(fileName, new Storage(fileName)); } return fileStates.get(fileName); } diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java new file mode 100644 index 0000000000..1304f25630 --- /dev/null +++ b/src/main/java/TaskList.java @@ -0,0 +1,39 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class TaskList { + /** List of tasks to remember */ + private static ArrayList taskList = new ArrayList<>(); + + public static void initializeTaskList() { + taskList = TaskStorage.getTasks(); + } + + public static void finalizeTaskList() { + TaskStorage.saveTasks(taskList); + } + + /** + * Get task from index as string + * @param index the index as a string + * @return Optional.of(task) if successful, else Optional.empty() + */ + public static Optional getTask(String index) { + try { + int idx = Integer.parseInt(index); + Task task = taskList.get(idx - 1); + return Optional.of(task); + } catch (NumberFormatException ex) { + Ui.messagePrint("Sorry, I didn't understand " + index + ", please give me a number."); + return Optional.empty(); + } catch (IndexOutOfBoundsException ex) { + Ui.messagePrint("Sorry, the number " + index + ", wasn't in the range."); + return Optional.empty(); + } + } + + public static List getTaskList() { + return taskList; + } +} diff --git a/src/main/java/TasksFileState.java b/src/main/java/TaskStorage.java similarity index 68% rename from src/main/java/TasksFileState.java rename to src/main/java/TaskStorage.java index de24a99bd0..d82db09e60 100644 --- a/src/main/java/TasksFileState.java +++ b/src/main/java/TaskStorage.java @@ -5,16 +5,16 @@ /** * Accesses a file for tasks. */ -public class TasksFileState { +public class TaskStorage { /** * Gets ArrayList of previously saved tasks. * @return ArrayList of tasks. */ public static ArrayList getTasks() { - FileState fileState = FileState.getFileState("./duke.txt"); + Storage storage = Storage.getFileState("./duke.txt"); ArrayList tasks = new ArrayList<>(); - for (String[] line : fileState.getLines()) { + for (String[] line : storage.getLines()) { TaskFactory.constructOptionalTask(line).ifPresent((task) -> tasks.add(task)); } return tasks; @@ -26,7 +26,7 @@ public static ArrayList getTasks() { */ public static void saveTasks(List tasks) { List lines = tasks.stream().map(Task::getAsStringArray).collect(Collectors.toList()); - FileState fileState = FileState.getFileState("./duke.txt"); - fileState.saveLines((String[][]) lines.toArray(new String[][]{})); + Storage storage = Storage.getFileState("./duke.txt"); + storage.saveLines((String[][]) lines.toArray(new String[][]{})); } } From 6e160d2d429baf493493ffb14361b918b5d5db24 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Sat, 20 Aug 2022 22:52:22 +0800 Subject: [PATCH 13/67] Move classes into packages Packaging is good for organisation. Another thing we need to do while packaging is to update runtest.bat to compile code within the packages. --- README.md | 4 +- src/main/java/{ => duke}/CommandMatcher.java | 2 + src/main/java/{ => duke}/Duke.java | 115 +++++++++--------- src/main/java/{ => duke}/ParsedDateTime.java | 9 +- src/main/java/{ => duke}/Parser.java | 6 +- .../java/{ => duke}/PrefixCommandMatcher.java | 2 + src/main/java/{ => duke}/Storage.java | 2 + src/main/java/{ => duke}/Ui.java | 6 +- src/main/java/{ => duke/task}/Deadline.java | 10 +- src/main/java/{ => duke/task}/Event.java | 6 +- src/main/java/{ => duke/task}/Task.java | 84 ++++++------- .../java/{ => duke/task}/TaskFactory.java | 16 ++- src/main/java/{ => duke/task}/TaskList.java | 13 +- .../java/{ => duke/task}/TaskStorage.java | 4 + src/main/java/{ => duke/task}/ToDo.java | 4 +- text-ui-test/duke.txt | 32 ++--- text-ui-test/runtest.bat | 6 +- 17 files changed, 186 insertions(+), 135 deletions(-) rename src/main/java/{ => duke}/CommandMatcher.java (95%) rename src/main/java/{ => duke}/Duke.java (93%) rename src/main/java/{ => duke}/ParsedDateTime.java (86%) rename src/main/java/{ => duke}/Parser.java (93%) rename src/main/java/{ => duke}/PrefixCommandMatcher.java (96%) rename src/main/java/{ => duke}/Storage.java (96%) rename src/main/java/{ => duke}/Ui.java (92%) rename src/main/java/{ => duke/task}/Deadline.java (78%) rename src/main/java/{ => duke/task}/Event.java (84%) rename src/main/java/{ => duke/task}/Task.java (86%) rename src/main/java/{ => duke/task}/TaskFactory.java (72%) rename src/main/java/{ => duke/task}/TaskList.java (84%) rename src/main/java/{ => duke/task}/TaskStorage.java (92%) rename src/main/java/{ => duke/task}/ToDo.java (86%) diff --git a/README.md b/README.md index 8715d4d915..0f2208ab64 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Duke project template +# duke.Duke project template This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. @@ -13,7 +13,7 @@ Prerequisites: JDK 11, update Intellij to the most recent version. 1. If there are any further prompts, accept the defaults. 1. Configure the project to use **JDK 11** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk).
In the same dialog, set the **Project language level** field to the `SDK default` option. -3. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: +3. After that, locate the `src/main/java/duke.Duke.java` file, right-click it, and choose `Run duke.Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: ``` Hello from ____ _ diff --git a/src/main/java/CommandMatcher.java b/src/main/java/duke/CommandMatcher.java similarity index 95% rename from src/main/java/CommandMatcher.java rename to src/main/java/duke/CommandMatcher.java index bf3ce705bb..e69d884771 100644 --- a/src/main/java/CommandMatcher.java +++ b/src/main/java/duke/CommandMatcher.java @@ -1,3 +1,5 @@ +package duke; + import java.util.function.Consumer; import java.util.function.Predicate; diff --git a/src/main/java/Duke.java b/src/main/java/duke/Duke.java similarity index 93% rename from src/main/java/Duke.java rename to src/main/java/duke/Duke.java index 7dd662bb87..b7fc66fc24 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/duke/Duke.java @@ -1,55 +1,60 @@ -import java.util.ArrayList; -import java.util.Optional; -import java.util.Scanner; - -/** - * The main method of the chatbot, as well as its startup and teardown. - */ -public class Duke { - /** List of commands */ - private static ArrayList commands; - - private static Optional getTask(String index) { - try { - int idx = Integer.parseInt(index); - Task task = TaskList.getTaskList().get(idx - 1); - return Optional.of(task); - } catch (NumberFormatException ex) { - Ui.messagePrint("Sorry, I didn't understand " + index + ", please give me a number."); - return Optional.empty(); - } catch (IndexOutOfBoundsException ex) { - Ui.messagePrint("Sorry, the number " + index + ", wasn't in the range."); - return Optional.empty(); - } - } - - private static void handleCommand(String command) { - for (CommandMatcher matcher : commands) { - if (matcher.run(command)) { - break; - } - } - } - - /** - * Runs the chatbot execution. - * @param args Command line args which are not used. - */ - public static void main(String[] args) { - Ui.greet(); - TaskList.initializeTaskList(); - commands = Parser.getCommands(); - Scanner input = new Scanner(System.in); - boolean keepRunning = true; - while (keepRunning) { - String command = input.nextLine(); - if (command.equals("bye")) { - keepRunning = false; - } else { - handleCommand(command); - } - } - TaskList.finalizeTaskList(); - Ui.leave(); - } -} +package duke; + +import duke.task.Task; +import duke.task.TaskList; + +import java.util.ArrayList; +import java.util.Optional; +import java.util.Scanner; + +/** + * The main method of the chatbot, as well as its startup and teardown. + */ +public class Duke { + /** List of commands */ + private static ArrayList commands; + + private static Optional getTask(String index) { + try { + int idx = Integer.parseInt(index); + Task task = TaskList.getTaskList().get(idx - 1); + return Optional.of(task); + } catch (NumberFormatException ex) { + Ui.messagePrint("Sorry, I didn't understand " + index + ", please give me a number."); + return Optional.empty(); + } catch (IndexOutOfBoundsException ex) { + Ui.messagePrint("Sorry, the number " + index + ", wasn't in the range."); + return Optional.empty(); + } + } + + private static void handleCommand(String command) { + for (CommandMatcher matcher : commands) { + if (matcher.run(command)) { + break; + } + } + } + + /** + * Runs the chatbot execution. + * @param args Command line args which are not used. + */ + public static void main(String[] args) { + Ui.greet(); + TaskList.initializeTaskList(); + commands = Parser.getCommands(); + Scanner input = new Scanner(System.in); + boolean keepRunning = true; + while (keepRunning) { + String command = input.nextLine(); + if (command.equals("bye")) { + keepRunning = false; + } else { + handleCommand(command); + } + } + TaskList.finalizeTaskList(); + Ui.leave(); + } +} diff --git a/src/main/java/ParsedDateTime.java b/src/main/java/duke/ParsedDateTime.java similarity index 86% rename from src/main/java/ParsedDateTime.java rename to src/main/java/duke/ParsedDateTime.java index 131088e10d..8e6c43c7f0 100644 --- a/src/main/java/ParsedDateTime.java +++ b/src/main/java/duke/ParsedDateTime.java @@ -1,3 +1,5 @@ +package duke; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -17,7 +19,12 @@ public class ParsedDateTime { private Optional parsedDateTime; private String input; - ParsedDateTime(String input) { + + /** + * Creates an object to handle if the date/time can be parsed. + * @param input String that may represent date/time. + */ + public ParsedDateTime(String input) { this.input = input; parsedDateTime = Optional.empty(); for (DateTimeFormatter formatter : formatters) { diff --git a/src/main/java/Parser.java b/src/main/java/duke/Parser.java similarity index 93% rename from src/main/java/Parser.java rename to src/main/java/duke/Parser.java index 17d81c3d0a..dd90f3a8de 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/duke/Parser.java @@ -1,8 +1,12 @@ +package duke; + +import duke.task.*; + import java.util.ArrayList; import java.util.List; /** - * Handles creating commands through CommandMatcher/PrefixCommandMatcher. + * Handles creating commands through duke.CommandMatcher/duke.PrefixCommandMatcher. */ public class Parser { diff --git a/src/main/java/PrefixCommandMatcher.java b/src/main/java/duke/PrefixCommandMatcher.java similarity index 96% rename from src/main/java/PrefixCommandMatcher.java rename to src/main/java/duke/PrefixCommandMatcher.java index 2e42a0171a..0c90fe056c 100644 --- a/src/main/java/PrefixCommandMatcher.java +++ b/src/main/java/duke/PrefixCommandMatcher.java @@ -1,3 +1,5 @@ +package duke; + import java.util.HashMap; import java.util.Map; import java.util.function.BiConsumer; diff --git a/src/main/java/Storage.java b/src/main/java/duke/Storage.java similarity index 96% rename from src/main/java/Storage.java rename to src/main/java/duke/Storage.java index e90157bee6..a16d70d212 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/duke/Storage.java @@ -1,3 +1,5 @@ +package duke; + import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; diff --git a/src/main/java/Ui.java b/src/main/java/duke/Ui.java similarity index 92% rename from src/main/java/Ui.java rename to src/main/java/duke/Ui.java index e5163fbb29..8b66775451 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/duke/Ui.java @@ -1,6 +1,8 @@ +package duke; + +import duke.task.Task; + import java.util.ArrayList; -import java.util.Optional; -import java.util.Scanner; public class Ui { /** List of commands */ diff --git a/src/main/java/Deadline.java b/src/main/java/duke/task/Deadline.java similarity index 78% rename from src/main/java/Deadline.java rename to src/main/java/duke/task/Deadline.java index 5c6639918f..804bb2e82f 100644 --- a/src/main/java/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -1,3 +1,7 @@ +package duke.task; + +import duke.ParsedDateTime; + /** * Handles a task with a deadline. */ @@ -5,7 +9,7 @@ public class Deadline extends Task { protected ParsedDateTime datetime; /** - * Creates a Deadline object. + * Creates a duke.tasks.Deadline object. * @param description Description of deadline. * @param by Time of deadline. */ @@ -14,7 +18,7 @@ public Deadline(String description, String by) { } /** - * Creates a Deadline object. + * Creates a duke.tasks.Deadline object. * @param description Description of deadline. * @param by Time of deadline. * @param done If the task is done. @@ -36,6 +40,6 @@ public String toString() { @Override public String[] getAsStringArray() { String[] data = super.getAsStringArray(); - return new String[]{ "Deadline", data[1], data[2], datetime.toString() }; + return new String[]{ "duke.tasks.Deadline", data[1], data[2], datetime.toString() }; } } diff --git a/src/main/java/Event.java b/src/main/java/duke/task/Event.java similarity index 84% rename from src/main/java/Event.java rename to src/main/java/duke/task/Event.java index d3e436593e..23191272c1 100644 --- a/src/main/java/Event.java +++ b/src/main/java/duke/task/Event.java @@ -1,3 +1,7 @@ +package duke.task; + +import duke.ParsedDateTime; + /** * Handles an event. */ @@ -36,6 +40,6 @@ public String toString() { @Override public String[] getAsStringArray() { String[] data = super.getAsStringArray(); - return new String[]{ "Event", data[1], data[2], datetime.toString() }; + return new String[]{ "duke.tasks.Event", data[1], data[2], datetime.toString() }; } } diff --git a/src/main/java/Task.java b/src/main/java/duke/task/Task.java similarity index 86% rename from src/main/java/Task.java rename to src/main/java/duke/task/Task.java index 6b22a6a06a..d0d1542af1 100644 --- a/src/main/java/Task.java +++ b/src/main/java/duke/task/Task.java @@ -1,41 +1,43 @@ -/** - * A task stores the state and description of a task. - */ -public class Task { - private static final char MARKER_DONE = 'X'; - private static final char MARKER_NOT_DONE = ' '; - private String name; - private boolean done; - Task(String name) { - this(name, false); - } - - Task(String name, boolean done) { - this.name = name; - this.done = done; - } - - public void markAsDone() { - this.done = true; - } - public void markAsNotDone() { - this.done = false; - } - - private char getDoneMarker() { - return this.done ? MARKER_DONE : MARKER_NOT_DONE; - } - - @Override - public String toString() { - return String.format("[%c] %s", getDoneMarker(), name); - } - - /** - * Get a string array representation suitable for printing to files. - * @return String array representation. - */ - public String[] getAsStringArray() { - return new String[]{ "Task", name, String.valueOf(done) }; - } -} +package duke.task; + +/** + * A task stores the state and description of a task. + */ +public class Task { + private static final char MARKER_DONE = 'X'; + private static final char MARKER_NOT_DONE = ' '; + private String name; + private boolean done; + Task(String name) { + this(name, false); + } + + Task(String name, boolean done) { + this.name = name; + this.done = done; + } + + public void markAsDone() { + this.done = true; + } + public void markAsNotDone() { + this.done = false; + } + + private char getDoneMarker() { + return this.done ? MARKER_DONE : MARKER_NOT_DONE; + } + + @Override + public String toString() { + return String.format("[%c] %s", getDoneMarker(), name); + } + + /** + * Get a string array representation suitable for printing to files. + * @return String array representation. + */ + public String[] getAsStringArray() { + return new String[]{ "duke.tasks.Task", name, String.valueOf(done) }; + } +} diff --git a/src/main/java/TaskFactory.java b/src/main/java/duke/task/TaskFactory.java similarity index 72% rename from src/main/java/TaskFactory.java rename to src/main/java/duke/task/TaskFactory.java index 3b283b692e..b43daa287b 100644 --- a/src/main/java/TaskFactory.java +++ b/src/main/java/duke/task/TaskFactory.java @@ -1,5 +1,9 @@ +package duke.task; + import java.util.Optional; +import duke.Ui; + /** * Constructs tasks from strings */ @@ -7,23 +11,23 @@ public class TaskFactory { /** * Constructs the task. * @param taskData Data for the task. - * @return Task according to taskData. + * @return duke.tasks.Task according to taskData. * @throws IllegalArgumentException If taskData does not conform to the format. */ public static Task constructTask(String[] taskData) throws IllegalArgumentException { if (taskData == null || taskData.length < 1) { throw new IllegalArgumentException("taskData cannot be null or of length 1"); } - if (taskData[0].equals("Task") && taskData.length >= 3) { + if (taskData[0].equals("duke.tasks.Task") && taskData.length >= 3) { return new Task(taskData[1], taskData[2].equals("true")); } - if (taskData[0].equals("ToDo") && taskData.length >= 3) { + if (taskData[0].equals("duke.tasks.ToDo") && taskData.length >= 3) { return new ToDo(taskData[1], taskData[2].equals("true")); } - if (taskData[0].equals("Deadline") && taskData.length >= 4) { + if (taskData[0].equals("duke.tasks.Deadline") && taskData.length >= 4) { return new Deadline(taskData[1], taskData[3], taskData[2].equals("true")); } - if (taskData[0].equals("Event") && taskData.length >= 4) { + if (taskData[0].equals("duke.tasks.Event") && taskData.length >= 4) { return new Event(taskData[1], taskData[3], taskData[2].equals("true")); } throw new IllegalArgumentException("Unsupported task type or incorrect task data length"); @@ -32,7 +36,7 @@ public static Task constructTask(String[] taskData) throws IllegalArgumentExcept /** * Constructs the task. * @param taskData Data for the task. - * @return Optional of Task according to taskData, Optional.empty() if cannot construct. + * @return Optional of duke.tasks.Task according to taskData, Optional.empty() if cannot construct. */ public static Optional constructOptionalTask(String[] taskData) { try { diff --git a/src/main/java/TaskList.java b/src/main/java/duke/task/TaskList.java similarity index 84% rename from src/main/java/TaskList.java rename to src/main/java/duke/task/TaskList.java index 1304f25630..fc2ddbe736 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/duke/task/TaskList.java @@ -1,7 +1,14 @@ +package duke.task; + import java.util.ArrayList; import java.util.List; import java.util.Optional; +import duke.Ui; + +/** + * Holds the list of tasks + */ public class TaskList { /** List of tasks to remember */ private static ArrayList taskList = new ArrayList<>(); @@ -15,9 +22,9 @@ public static void finalizeTaskList() { } /** - * Get task from index as string - * @param index the index as a string - * @return Optional.of(task) if successful, else Optional.empty() + * Get task from index as string. + * @param index Index as a string. + * @return Optional.of(task) if successful, else Optional.empty(). */ public static Optional getTask(String index) { try { diff --git a/src/main/java/TaskStorage.java b/src/main/java/duke/task/TaskStorage.java similarity index 92% rename from src/main/java/TaskStorage.java rename to src/main/java/duke/task/TaskStorage.java index d82db09e60..83e0f01fe9 100644 --- a/src/main/java/TaskStorage.java +++ b/src/main/java/duke/task/TaskStorage.java @@ -1,7 +1,11 @@ +package duke.task; + import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import duke.Storage; + /** * Accesses a file for tasks. */ diff --git a/src/main/java/ToDo.java b/src/main/java/duke/task/ToDo.java similarity index 86% rename from src/main/java/ToDo.java rename to src/main/java/duke/task/ToDo.java index 584351dc7d..421605a6bc 100644 --- a/src/main/java/ToDo.java +++ b/src/main/java/duke/task/ToDo.java @@ -1,3 +1,5 @@ +package duke.task; + /** * A class that stores something to do. */ @@ -32,6 +34,6 @@ public String toString() { @Override public String[] getAsStringArray() { String[] data = super.getAsStringArray(); - return new String[]{ "ToDo", data[1], data[2] }; + return new String[]{ "duke.tasks.ToDo", data[1], data[2] }; } } diff --git a/text-ui-test/duke.txt b/text-ui-test/duke.txt index 78ee9ae569..c443f1bbad 100644 --- a/text-ui-test/duke.txt +++ b/text-ui-test/duke.txt @@ -1,16 +1,16 @@ -/v8AVABvAEQAbw==,/v8AaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl -/v8ARQB2AGUAbgB0,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZgB1AG4AYwB0AGkAbwBuACAAeAAvAHkALwB6ACAAdwBoAGEAdABlAHYAZQBy,/v8AdAByAHUAZQ==,/v8ANwA6ADEAMgBhAG0= -/v8ARABlAGEAZABsAGkAbgBl,/v8ALwBhAHQAIABtAHkAcwB0AGUAcgBpAG8AdQBz,/v8AdAByAHUAZQ==,/v8AWwB1AG4AawBuAG8AdwBuAF0= -/v8ARABlAGEAZABsAGkAbgBl,/v8ALwBiAHkAIABtAHkAcwB0AGUAcgBpAG8AdQBzACAAbgB1AG0AYgBlAHIAIAAy,/v8AdAByAHUAZQ==,/v8AWwB1AG4AawBuAG8AdwBuAF0= -/v8ARABlAGEAZABsAGkAbgBl,/v8AcgB1AHMAaAAgAHQAaABpAHMAIABwAGwAZQBhAHMAZQ==,/v8AZgBhAGwAcwBl,/v8AdwBoAGUAbgA/ -/v8ARQB2AGUAbgB0,/v8AdwBvAHcAIAAgAG0AdQBjAGgAIAAgAHMAcABhAGMAZQ==,/v8AZgBhAGwAcwBl,/v8AMQAwAGEAbQ== -/v8AVABvAEQAbw==,/v8AaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl -/v8AVABvAEQAbw==,/v8AbwB0AGgAZQByACAAaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl -/v8AVABvAEQAbw==,/v8AcAByAG8AagBlAGMAdAAgADEAMg==,/v8AZgBhAGwAcwBl -/v8ARQB2AGUAbgB0,/v8AbQBlAGUAdAAgAGYAcgBpAGUAbgBkAHM=,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= -/v8ARQB2AGUAbgB0,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZABpAG4AbgBlAHI=,/v8AZgBhAGwAcwBl,/v8ANgA6ADIANQAgAHAAbQ== -/v8ARQB2AGUAbgB0,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZgB1AG4AYwB0AGkAbwBuACAAeAAvAHkALwB6ACAAdwBoAGEAdABlAHYAZQBy,/v8AZgBhAGwAcwBl,/v8ANwA6ADEAMgBhAG0= -/v8ARABlAGEAZABsAGkAbgBl,/v8ALwBhAHQAIABtAHkAcwB0AGUAcgBpAG8AdQBz,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= -/v8ARABlAGEAZABsAGkAbgBl,/v8ALwBiAHkAIABtAHkAcwB0AGUAcgBpAG8AdQBzACAAbgB1AG0AYgBlAHIAIAAy,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= -/v8ARABlAGEAZABsAGkAbgBl,/v8AcgB1AHMAaAAgAHQAaABpAHMAIABwAGwAZQBhAHMAZQ==,/v8AZgBhAGwAcwBl,/v8AdwBoAGUAbgA/ -/v8ARQB2AGUAbgB0,/v8AdwBvAHcAIAAgAG0AdQBjAGgAIAAgAHMAcABhAGMAZQ==,/v8AZgBhAGwAcwBl,/v8AMQAwAGEAbQ== \ No newline at end of file +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAFQAbwBEAG8=,/v8AaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEUAdgBlAG4AdA==,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZgB1AG4AYwB0AGkAbwBuACAAeAAvAHkALwB6ACAAdwBoAGEAdABlAHYAZQBy,/v8AdAByAHUAZQ==,/v8ANwA6ADEAMgBhAG0= +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEQAZQBhAGQAbABpAG4AZQ==,/v8ALwBhAHQAIABtAHkAcwB0AGUAcgBpAG8AdQBz,/v8AdAByAHUAZQ==,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEQAZQBhAGQAbABpAG4AZQ==,/v8ALwBiAHkAIABtAHkAcwB0AGUAcgBpAG8AdQBzACAAbgB1AG0AYgBlAHIAIAAy,/v8AdAByAHUAZQ==,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEQAZQBhAGQAbABpAG4AZQ==,/v8AcgB1AHMAaAAgAHQAaABpAHMAIABwAGwAZQBhAHMAZQ==,/v8AZgBhAGwAcwBl,/v8AdwBoAGUAbgA/ +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEUAdgBlAG4AdA==,/v8AdwBvAHcAIAAgAG0AdQBjAGgAIAAgAHMAcABhAGMAZQ==,/v8AZgBhAGwAcwBl,/v8AMQAwAGEAbQ== +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAFQAbwBEAG8=,/v8AaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAFQAbwBEAG8=,/v8AbwB0AGgAZQByACAAaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAFQAbwBEAG8=,/v8AcAByAG8AagBlAGMAdAAgADEAMg==,/v8AZgBhAGwAcwBl +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEUAdgBlAG4AdA==,/v8AbQBlAGUAdAAgAGYAcgBpAGUAbgBkAHM=,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEUAdgBlAG4AdA==,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZABpAG4AbgBlAHI=,/v8AZgBhAGwAcwBl,/v8ANgA6ADIANQAgAHAAbQ== +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEUAdgBlAG4AdA==,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZgB1AG4AYwB0AGkAbwBuACAAeAAvAHkALwB6ACAAdwBoAGEAdABlAHYAZQBy,/v8AZgBhAGwAcwBl,/v8ANwA6ADEAMgBhAG0= +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEQAZQBhAGQAbABpAG4AZQ==,/v8ALwBhAHQAIABtAHkAcwB0AGUAcgBpAG8AdQBz,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEQAZQBhAGQAbABpAG4AZQ==,/v8ALwBiAHkAIABtAHkAcwB0AGUAcgBpAG8AdQBzACAAbgB1AG0AYgBlAHIAIAAy,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEQAZQBhAGQAbABpAG4AZQ==,/v8AcgB1AHMAaAAgAHQAaABpAHMAIABwAGwAZQBhAHMAZQ==,/v8AZgBhAGwAcwBl,/v8AdwBoAGUAbgA/ +/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEUAdgBlAG4AdA==,/v8AdwBvAHcAIAAgAG0AdQBjAGgAIAAgAHMAcABhAGMAZQ==,/v8AZgBhAGwAcwBl,/v8AMQAwAGEAbQ== \ No newline at end of file diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 74c2478cc0..8f94108629 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -7,7 +7,7 @@ REM delete output from previous run if exist ACTUAL.TXT del ACTUAL.TXT REM compile the code into the bin folder -javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\*.java +javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\duke\*.java ..\src\main\java\duke\task\*.java IF ERRORLEVEL 1 ( echo ********** BUILD FAILURE ********** exit /b 1 @@ -18,7 +18,7 @@ REM delete data if exist duke.txt del duke.txt REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ..\bin Duke < input.txt > ACTUAL.TXT +java -classpath ..\bin duke.Duke < input.txt > ACTUAL.TXT REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT @@ -27,7 +27,7 @@ REM delete output from previous run if exist ACTUAL1.TXT del ACTUAL1.TXT REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL1.TXT -java -classpath ..\bin Duke < input.txt > ACTUAL1.TXT +java -classpath ..\bin duke.Duke < input.txt > ACTUAL1.TXT REM compare the output to the expected output FC ACTUAL1.TXT EXPECTED1.TXT From 9611cbd26b48caa21a208f6538855ea5dc86859f Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Wed, 24 Aug 2022 22:32:07 +0800 Subject: [PATCH 14/67] Add files in attempt to add Gradle --- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58695 bytes gradle/wrapper/gradle-wrapper.properties | 5 +++++ 2 files changed, 5 insertions(+) create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f3d88b1c2faf2fc91d853cd5d4242b5547257070 GIT binary patch literal 58695 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..c14115d7e0 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists \ No newline at end of file From efb5c95a91d4f08071a3b99c1c49770bd31b1e52 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Wed, 24 Aug 2022 22:37:32 +0800 Subject: [PATCH 15/67] Add remaining files to add gradle support --- build.gradle | 41 ++++++++++++ gradlew | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++ gradlew.bat | 103 +++++++++++++++++++++++++++++ 3 files changed, 327 insertions(+) create mode 100644 build.gradle create mode 100644 gradlew create mode 100644 gradlew.bat diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..8a747821a5 --- /dev/null +++ b/build.gradle @@ -0,0 +1,41 @@ +plugins { + id 'java' + id 'application' + id 'com.github.johnrengelman.shadow' version '5.1.0' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' +} + +test { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed" + + showExceptions true + exceptionFormat "full" + showCauses true + showStackTraces true + showStandardStreams = false + } +} + +application { + mainClassName = "seedu.duke.Duke" +} + +shadowJar { + archiveBaseName = "duke" + archiveClassifier = null +} + +run{ + standardInput = System.in +} \ No newline at end of file diff --git a/gradlew b/gradlew new file mode 100644 index 0000000000..645f6ca315 --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" \ No newline at end of file diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..a14a3d051d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega \ No newline at end of file From f929e81e7b460067b9ec1e77f22a843318750129 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Wed, 24 Aug 2022 23:18:50 +0800 Subject: [PATCH 16/67] Change mainClassName to duke.Duke for Gradle to run project --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8a747821a5..c4e66ae0eb 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ test { } application { - mainClassName = "seedu.duke.Duke" + mainClassName = "duke.Duke" } shadowJar { From d2333f2116e306c9a815dd131fbc1871dcabad03 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Thu, 25 Aug 2022 09:30:54 +0800 Subject: [PATCH 17/67] Add some documentation and TaskTest --- src/main/java/duke/task/Deadline.java | 2 +- src/main/java/duke/task/Event.java | 2 +- src/main/java/duke/task/Task.java | 15 ++++++- src/main/java/duke/task/TaskFactory.java | 4 +- src/test/java/duke/task/TaskTest.java | 55 ++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 src/test/java/duke/task/TaskTest.java diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java index 804bb2e82f..1f38b2e1cb 100644 --- a/src/main/java/duke/task/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -40,6 +40,6 @@ public String toString() { @Override public String[] getAsStringArray() { String[] data = super.getAsStringArray(); - return new String[]{ "duke.tasks.Deadline", data[1], data[2], datetime.toString() }; + return new String[]{ "Deadline", data[1], data[2], datetime.toString() }; } } diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java index 23191272c1..e16fb19e07 100644 --- a/src/main/java/duke/task/Event.java +++ b/src/main/java/duke/task/Event.java @@ -40,6 +40,6 @@ public String toString() { @Override public String[] getAsStringArray() { String[] data = super.getAsStringArray(); - return new String[]{ "duke.tasks.Event", data[1], data[2], datetime.toString() }; + return new String[]{ "Event", data[1], data[2], datetime.toString() }; } } diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java index d0d1542af1..a30269e77d 100644 --- a/src/main/java/duke/task/Task.java +++ b/src/main/java/duke/task/Task.java @@ -17,9 +17,16 @@ public class Task { this.done = done; } + /** + * Marks the task as done. + */ public void markAsDone() { this.done = true; } + + /** + * Marks the task as not done. + */ public void markAsNotDone() { this.done = false; } @@ -28,16 +35,20 @@ private char getDoneMarker() { return this.done ? MARKER_DONE : MARKER_NOT_DONE; } + /** + * Creates a string representation suitable for printing to screen. + * @return String representation of task. + */ @Override public String toString() { return String.format("[%c] %s", getDoneMarker(), name); } /** - * Get a string array representation suitable for printing to files. + * Creates a string array representation suitable for printing to files. * @return String array representation. */ public String[] getAsStringArray() { - return new String[]{ "duke.tasks.Task", name, String.valueOf(done) }; + return new String[]{ "Task", name, String.valueOf(done) }; } } diff --git a/src/main/java/duke/task/TaskFactory.java b/src/main/java/duke/task/TaskFactory.java index b43daa287b..feac197cfe 100644 --- a/src/main/java/duke/task/TaskFactory.java +++ b/src/main/java/duke/task/TaskFactory.java @@ -11,7 +11,7 @@ public class TaskFactory { /** * Constructs the task. * @param taskData Data for the task. - * @return duke.tasks.Task according to taskData. + * @return Task according to taskData. * @throws IllegalArgumentException If taskData does not conform to the format. */ public static Task constructTask(String[] taskData) throws IllegalArgumentException { @@ -36,7 +36,7 @@ public static Task constructTask(String[] taskData) throws IllegalArgumentExcept /** * Constructs the task. * @param taskData Data for the task. - * @return Optional of duke.tasks.Task according to taskData, Optional.empty() if cannot construct. + * @return Optional of Task according to taskData, Optional.empty() if cannot construct. */ public static Optional constructOptionalTask(String[] taskData) { try { diff --git a/src/test/java/duke/task/TaskTest.java b/src/test/java/duke/task/TaskTest.java new file mode 100644 index 0000000000..2f0acd6d6b --- /dev/null +++ b/src/test/java/duke/task/TaskTest.java @@ -0,0 +1,55 @@ +package duke.task; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TaskTest { + @Test + public void testDoneMarker() { + Task task1 = new Task("task one", false); + Task task2 = new Task("task two", true); + Task task3 = new Task("task three"); + + assertEquals("[ ] task one", task1.toString()); + assertEquals("[X] task two", task2.toString()); + assertEquals("[ ] task three", task3.toString()); + + task1.markAsDone(); + task2.markAsNotDone(); + task3.markAsNotDone(); + + assertEquals("[X] task one", task1.toString()); + assertEquals("[ ] task two", task2.toString()); + assertEquals("[ ] task three", task3.toString()); + + task1.markAsDone(); + task2.markAsNotDone(); + task3.markAsNotDone(); + + assertEquals("[X] task one", task1.toString()); + assertEquals("[ ] task two", task2.toString()); + assertEquals("[ ] task three", task3.toString()); + + task1.markAsNotDone(); + task2.markAsDone(); + task3.markAsDone(); + + assertEquals("[ ] task one", task1.toString()); + assertEquals("[X] task two", task2.toString()); + assertEquals("[X] task three", task3.toString()); + } + + @Test + public void testStringArray() { + Task task1 = new Task("task one", false); + Task task2 = new Task("task two", true); + Task task3 = new Task("task three"); + + assertArrayEquals(new String[] { "Task", "task one", "false" }, task1.getAsStringArray()); + assertArrayEquals(new String[] { "Task", "task two", "true" }, task2.getAsStringArray()); + assertArrayEquals(new String[] { "Task", "task three", "false" }, task3.getAsStringArray()); + } +} From 8f0bdb2fd53fcc1132a847786ea3ee8abb441f33 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Fri, 26 Aug 2022 16:19:21 +0800 Subject: [PATCH 18/67] Add tests for Deadline; unroll import * s --- src/test/java/duke/task/DeadlineTest.java | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/test/java/duke/task/DeadlineTest.java diff --git a/src/test/java/duke/task/DeadlineTest.java b/src/test/java/duke/task/DeadlineTest.java new file mode 100644 index 0000000000..ac93dbcf15 --- /dev/null +++ b/src/test/java/duke/task/DeadlineTest.java @@ -0,0 +1,2 @@ +package duke.task;public class DeadlineTest { +} From 28ea520b47105c07b5bf17cdeac9d8364a1c657b Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Fri, 26 Aug 2022 16:20:48 +0800 Subject: [PATCH 19/67] Actually add DeadlineTest --- src/main/java/duke/Parser.java | 6 +++++- src/main/java/duke/Storage.java | 1 + src/test/java/duke/task/DeadlineTest.java | 23 ++++++++++++++++++++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/main/java/duke/Parser.java b/src/main/java/duke/Parser.java index dd90f3a8de..5e2b811e38 100644 --- a/src/main/java/duke/Parser.java +++ b/src/main/java/duke/Parser.java @@ -1,6 +1,10 @@ package duke; -import duke.task.*; +import duke.task.Deadline; +import duke.task.Event; +import duke.task.Task; +import duke.task.TaskList; +import duke.task.ToDo; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java index a16d70d212..83bc5e1da9 100644 --- a/src/main/java/duke/Storage.java +++ b/src/main/java/duke/Storage.java @@ -8,6 +8,7 @@ import java.nio.charset.StandardCharsets; import java.util.*; + /** * Handles file state. */ diff --git a/src/test/java/duke/task/DeadlineTest.java b/src/test/java/duke/task/DeadlineTest.java index ac93dbcf15..ba3f65dfeb 100644 --- a/src/test/java/duke/task/DeadlineTest.java +++ b/src/test/java/duke/task/DeadlineTest.java @@ -1,2 +1,23 @@ -package duke.task;public class DeadlineTest { +package duke.task; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DeadlineTest { + @Test + public void testStringArray() { + Task task1 = new Deadline("task one", "1", false); + Task task2 = new Deadline("task two", "2", true); + Task task3 = new Deadline("task three", "3"); + + assertEquals("[D][ ] task one (by: 1)", task1.toString()); + assertEquals("[D][X] task two (by: 2)", task2.toString()); + assertEquals("[D][ ] task three (by: 3)", task3.toString()); + + assertArrayEquals(new String[] { "Deadline", "task one", "false", "1" }, task1.getAsStringArray()); + assertArrayEquals(new String[] { "Deadline", "task two", "true", "2" }, task2.getAsStringArray()); + assertArrayEquals(new String[] { "Deadline", "task three", "false", "3" }, task3.getAsStringArray()); + } } From cdf8c76b583bcd52bb5a2b34b07d6722ca449847 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Fri, 26 Aug 2022 20:34:47 +0800 Subject: [PATCH 20/67] Fix bugs related to package refactoring changing strings --- src/main/java/duke/task/TaskFactory.java | 8 +++--- src/main/java/duke/task/ToDo.java | 2 +- text-ui-test/duke.txt | 32 ++++++++++++------------ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/main/java/duke/task/TaskFactory.java b/src/main/java/duke/task/TaskFactory.java index feac197cfe..c594c22414 100644 --- a/src/main/java/duke/task/TaskFactory.java +++ b/src/main/java/duke/task/TaskFactory.java @@ -18,16 +18,16 @@ public static Task constructTask(String[] taskData) throws IllegalArgumentExcept if (taskData == null || taskData.length < 1) { throw new IllegalArgumentException("taskData cannot be null or of length 1"); } - if (taskData[0].equals("duke.tasks.Task") && taskData.length >= 3) { + if (taskData[0].equals("Task") && taskData.length >= 3) { return new Task(taskData[1], taskData[2].equals("true")); } - if (taskData[0].equals("duke.tasks.ToDo") && taskData.length >= 3) { + if (taskData[0].equals("ToDo") && taskData.length >= 3) { return new ToDo(taskData[1], taskData[2].equals("true")); } - if (taskData[0].equals("duke.tasks.Deadline") && taskData.length >= 4) { + if (taskData[0].equals("Deadline") && taskData.length >= 4) { return new Deadline(taskData[1], taskData[3], taskData[2].equals("true")); } - if (taskData[0].equals("duke.tasks.Event") && taskData.length >= 4) { + if (taskData[0].equals("Event") && taskData.length >= 4) { return new Event(taskData[1], taskData[3], taskData[2].equals("true")); } throw new IllegalArgumentException("Unsupported task type or incorrect task data length"); diff --git a/src/main/java/duke/task/ToDo.java b/src/main/java/duke/task/ToDo.java index 421605a6bc..bf17b053ac 100644 --- a/src/main/java/duke/task/ToDo.java +++ b/src/main/java/duke/task/ToDo.java @@ -34,6 +34,6 @@ public String toString() { @Override public String[] getAsStringArray() { String[] data = super.getAsStringArray(); - return new String[]{ "duke.tasks.ToDo", data[1], data[2] }; + return new String[]{ "ToDo", data[1], data[2] }; } } diff --git a/text-ui-test/duke.txt b/text-ui-test/duke.txt index c443f1bbad..78ee9ae569 100644 --- a/text-ui-test/duke.txt +++ b/text-ui-test/duke.txt @@ -1,16 +1,16 @@ -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAFQAbwBEAG8=,/v8AaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEUAdgBlAG4AdA==,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZgB1AG4AYwB0AGkAbwBuACAAeAAvAHkALwB6ACAAdwBoAGEAdABlAHYAZQBy,/v8AdAByAHUAZQ==,/v8ANwA6ADEAMgBhAG0= -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEQAZQBhAGQAbABpAG4AZQ==,/v8ALwBhAHQAIABtAHkAcwB0AGUAcgBpAG8AdQBz,/v8AdAByAHUAZQ==,/v8AWwB1AG4AawBuAG8AdwBuAF0= -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEQAZQBhAGQAbABpAG4AZQ==,/v8ALwBiAHkAIABtAHkAcwB0AGUAcgBpAG8AdQBzACAAbgB1AG0AYgBlAHIAIAAy,/v8AdAByAHUAZQ==,/v8AWwB1AG4AawBuAG8AdwBuAF0= -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEQAZQBhAGQAbABpAG4AZQ==,/v8AcgB1AHMAaAAgAHQAaABpAHMAIABwAGwAZQBhAHMAZQ==,/v8AZgBhAGwAcwBl,/v8AdwBoAGUAbgA/ -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEUAdgBlAG4AdA==,/v8AdwBvAHcAIAAgAG0AdQBjAGgAIAAgAHMAcABhAGMAZQ==,/v8AZgBhAGwAcwBl,/v8AMQAwAGEAbQ== -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAFQAbwBEAG8=,/v8AaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAFQAbwBEAG8=,/v8AbwB0AGgAZQByACAAaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAFQAbwBEAG8=,/v8AcAByAG8AagBlAGMAdAAgADEAMg==,/v8AZgBhAGwAcwBl -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEUAdgBlAG4AdA==,/v8AbQBlAGUAdAAgAGYAcgBpAGUAbgBkAHM=,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEUAdgBlAG4AdA==,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZABpAG4AbgBlAHI=,/v8AZgBhAGwAcwBl,/v8ANgA6ADIANQAgAHAAbQ== -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEUAdgBlAG4AdA==,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZgB1AG4AYwB0AGkAbwBuACAAeAAvAHkALwB6ACAAdwBoAGEAdABlAHYAZQBy,/v8AZgBhAGwAcwBl,/v8ANwA6ADEAMgBhAG0= -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEQAZQBhAGQAbABpAG4AZQ==,/v8ALwBhAHQAIABtAHkAcwB0AGUAcgBpAG8AdQBz,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEQAZQBhAGQAbABpAG4AZQ==,/v8ALwBiAHkAIABtAHkAcwB0AGUAcgBpAG8AdQBzACAAbgB1AG0AYgBlAHIAIAAy,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEQAZQBhAGQAbABpAG4AZQ==,/v8AcgB1AHMAaAAgAHQAaABpAHMAIABwAGwAZQBhAHMAZQ==,/v8AZgBhAGwAcwBl,/v8AdwBoAGUAbgA/ -/v8AZAB1AGsAZQAuAHQAYQBzAGsAcwAuAEUAdgBlAG4AdA==,/v8AdwBvAHcAIAAgAG0AdQBjAGgAIAAgAHMAcABhAGMAZQ==,/v8AZgBhAGwAcwBl,/v8AMQAwAGEAbQ== \ No newline at end of file +/v8AVABvAEQAbw==,/v8AaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl +/v8ARQB2AGUAbgB0,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZgB1AG4AYwB0AGkAbwBuACAAeAAvAHkALwB6ACAAdwBoAGEAdABlAHYAZQBy,/v8AdAByAHUAZQ==,/v8ANwA6ADEAMgBhAG0= +/v8ARABlAGEAZABsAGkAbgBl,/v8ALwBhAHQAIABtAHkAcwB0AGUAcgBpAG8AdQBz,/v8AdAByAHUAZQ==,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8ARABlAGEAZABsAGkAbgBl,/v8ALwBiAHkAIABtAHkAcwB0AGUAcgBpAG8AdQBzACAAbgB1AG0AYgBlAHIAIAAy,/v8AdAByAHUAZQ==,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8ARABlAGEAZABsAGkAbgBl,/v8AcgB1AHMAaAAgAHQAaABpAHMAIABwAGwAZQBhAHMAZQ==,/v8AZgBhAGwAcwBl,/v8AdwBoAGUAbgA/ +/v8ARQB2AGUAbgB0,/v8AdwBvAHcAIAAgAG0AdQBjAGgAIAAgAHMAcABhAGMAZQ==,/v8AZgBhAGwAcwBl,/v8AMQAwAGEAbQ== +/v8AVABvAEQAbw==,/v8AaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl +/v8AVABvAEQAbw==,/v8AbwB0AGgAZQByACAAaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl +/v8AVABvAEQAbw==,/v8AcAByAG8AagBlAGMAdAAgADEAMg==,/v8AZgBhAGwAcwBl +/v8ARQB2AGUAbgB0,/v8AbQBlAGUAdAAgAGYAcgBpAGUAbgBkAHM=,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8ARQB2AGUAbgB0,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZABpAG4AbgBlAHI=,/v8AZgBhAGwAcwBl,/v8ANgA6ADIANQAgAHAAbQ== +/v8ARQB2AGUAbgB0,/v8AbQBlAGUAdAAgAHAAZQBvAHAAbABlACAAYQB0ACAAZgB1AG4AYwB0AGkAbwBuACAAeAAvAHkALwB6ACAAdwBoAGEAdABlAHYAZQBy,/v8AZgBhAGwAcwBl,/v8ANwA6ADEAMgBhAG0= +/v8ARABlAGEAZABsAGkAbgBl,/v8ALwBhAHQAIABtAHkAcwB0AGUAcgBpAG8AdQBz,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8ARABlAGEAZABsAGkAbgBl,/v8ALwBiAHkAIABtAHkAcwB0AGUAcgBpAG8AdQBzACAAbgB1AG0AYgBlAHIAIAAy,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= +/v8ARABlAGEAZABsAGkAbgBl,/v8AcgB1AHMAaAAgAHQAaABpAHMAIABwAGwAZQBhAHMAZQ==,/v8AZgBhAGwAcwBl,/v8AdwBoAGUAbgA/ +/v8ARQB2AGUAbgB0,/v8AdwBvAHcAIAAgAG0AdQBjAGgAIAAgAHMAcABhAGMAZQ==,/v8AZgBhAGwAcwBl,/v8AMQAwAGEAbQ== \ No newline at end of file From 7448407b79eebaecb82b41e7ffda9026f5ecf185 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Fri, 26 Aug 2022 22:00:26 +0800 Subject: [PATCH 21/67] Follow coding standard --- src/main/java/duke/Duke.java | 15 ++++++++++----- src/main/java/duke/Parser.java | 16 ++++++++-------- src/main/java/duke/PrefixCommandMatcher.java | 13 +++++++++++-- src/main/java/duke/Storage.java | 16 ++++++++++------ src/main/java/duke/Ui.java | 10 +++------- src/main/java/duke/task/Deadline.java | 6 +++--- src/main/java/duke/task/Event.java | 6 +++--- src/main/java/duke/task/Task.java | 14 +++++++------- src/main/java/duke/task/TaskFactory.java | 2 +- src/main/java/duke/task/TaskList.java | 4 ++-- src/main/java/duke/task/ToDo.java | 6 +++--- src/test/java/duke/task/DeadlineTest.java | 2 +- src/test/java/duke/task/TaskTest.java | 4 ++-- 13 files changed, 64 insertions(+), 50 deletions(-) diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index b7fc66fc24..ff37da1c75 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -20,10 +20,10 @@ private static Optional getTask(String index) { Task task = TaskList.getTaskList().get(idx - 1); return Optional.of(task); } catch (NumberFormatException ex) { - Ui.messagePrint("Sorry, I didn't understand " + index + ", please give me a number."); + Ui.printStyledMessage("Sorry, I didn't understand " + index + ", please give me a number."); return Optional.empty(); } catch (IndexOutOfBoundsException ex) { - Ui.messagePrint("Sorry, the number " + index + ", wasn't in the range."); + Ui.printStyledMessage("Sorry, the number " + index + ", wasn't in the range."); return Optional.empty(); } } @@ -41,19 +41,24 @@ private static void handleCommand(String command) { * @param args Command line args which are not used. */ public static void main(String[] args) { + // initialization Ui.greet(); TaskList.initializeTaskList(); commands = Parser.getCommands(); Scanner input = new Scanner(System.in); - boolean keepRunning = true; - while (keepRunning) { + + // main application logic + boolean isStillRunning = true; + while (isStillRunning) { String command = input.nextLine(); if (command.equals("bye")) { - keepRunning = false; + isStillRunning = false; } else { handleCommand(command); } } + + // finalization TaskList.finalizeTaskList(); Ui.leave(); } diff --git a/src/main/java/duke/Parser.java b/src/main/java/duke/Parser.java index 5e2b811e38..ccb1b60830 100644 --- a/src/main/java/duke/Parser.java +++ b/src/main/java/duke/Parser.java @@ -24,13 +24,13 @@ public static ArrayList getCommands() { for (int i = 0; i < list.size(); i++) { output[i + 1] = (i + 1) + "." + list.get(i).toString(); } - Ui.messagePrint(output); + Ui.printStyledMessage(output); })); commands.add(new PrefixCommandMatcher("mark", (str, map) -> { TaskList.getTask(str).ifPresent((task) -> { task.markAsDone(); - Ui.messagePrint("Marked your task as done:", + Ui.printStyledMessage("Marked your task as done:", task.toString()); }); })); @@ -38,7 +38,7 @@ public static ArrayList getCommands() { commands.add(new PrefixCommandMatcher("unmark", (str, map) -> { TaskList.getTask(str).ifPresent((task) -> { task.markAsNotDone(); - Ui.messagePrint("Aw... it's not done yet:", + Ui.printStyledMessage("Aw... it's not done yet:", task.toString()); }); })); @@ -46,28 +46,28 @@ public static ArrayList getCommands() { commands.add(new PrefixCommandMatcher("deadline", (str, map) -> { Task task = new Deadline(str, map.getOrDefault("by", "[unknown]")); TaskList.getTaskList().add(task); - Ui.messagePrint("Good luck with the deadline, here's the task:", + Ui.printStyledMessage("Good luck with the deadline, here's the task:", task.toString()); })); commands.add(new PrefixCommandMatcher("todo", (str, map) -> { Task task = new ToDo(str); TaskList.getTaskList().add(task); - Ui.messagePrint("I've recorded this thing you need to do:", + Ui.printStyledMessage("I've recorded this thing you need to do:", task.toString()); })); commands.add(new PrefixCommandMatcher("event", (str, map) -> { Task task = new Event(str, map.getOrDefault("at", "[unknown]")); TaskList.getTaskList().add(task); - Ui.messagePrint("That's going to happen at some time later:", + Ui.printStyledMessage("That's going to happen at some time later:", task.toString()); })); commands.add(new PrefixCommandMatcher("delete", (str, map) -> { TaskList.getTask(str).ifPresent((task) -> { TaskList.getTaskList().remove(task); - Ui.messagePrint("It seems you didn't need this task anymore, so I removed it:", + Ui.printStyledMessage("It seems you didn't need this task anymore, so I removed it:", task.toString(), String.format("You have %d tasks left.", TaskList.getTaskList().size())); }); @@ -75,7 +75,7 @@ public static ArrayList getCommands() { // default command matcher - add to list commands.add(new CommandMatcher((str) -> true, (str) -> { - Ui.messagePrint("(>.<') I'm sorry, I don't really know what that means."); + Ui.printStyledMessage("(>.<') I'm sorry, I don't really know what that means."); })); return commands; diff --git a/src/main/java/duke/PrefixCommandMatcher.java b/src/main/java/duke/PrefixCommandMatcher.java index 0c90fe056c..7a38569427 100644 --- a/src/main/java/duke/PrefixCommandMatcher.java +++ b/src/main/java/duke/PrefixCommandMatcher.java @@ -16,11 +16,16 @@ public class PrefixCommandMatcher extends CommandMatcher { */ public PrefixCommandMatcher(String prefix, BiConsumer> action) { super((cmd) -> cmd.strip().startsWith(prefix + " ") || cmd.strip().equals(prefix), (cmd) -> { + // preprocessing cmd = cmd.strip(); + + // corner case if (cmd.equals(prefix)) { - Ui.messagePrint("(>.<') Add a description to your " + prefix + "."); + Ui.printStyledMessage("(>.<') Add a description to your " + prefix + "."); return; } + + // map processing String withoutPrefix = cmd.split(" ", 2)[1]; String[] commandParts = withoutPrefix.split(" /"); Map map = new HashMap<>(); @@ -33,10 +38,14 @@ public PrefixCommandMatcher(String prefix, BiConsumer.<') The description for " + prefix + " shouldn't be empty."); + Ui.printStyledMessage("(>.<') The description for " + prefix + " shouldn't be empty."); return; } + + // accept action.accept(commandParts[0].strip(), map); }); } diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java index 83bc5e1da9..86cd45d6de 100644 --- a/src/main/java/duke/Storage.java +++ b/src/main/java/duke/Storage.java @@ -6,7 +6,11 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; /** @@ -61,11 +65,11 @@ public static Storage getFileState(String fileName) { // write to file TODO }*/ - private static String toBase64(String input) { + private static String convertToBase64(String input) { return Base64.getEncoder().encodeToString(input.getBytes(StandardCharsets.UTF_16)); } - private static String fromBase64(String input) { + private static String convertFromBase64(String input) { return StandardCharsets.UTF_16.decode(ByteBuffer.wrap(Base64.getDecoder().decode(input))).toString(); } @@ -77,7 +81,7 @@ public String[][] getLines() { while (sc.hasNext()) { ArrayList curLine = new ArrayList<>(); for (String str : sc.nextLine().split(",")) { - curLine.add(fromBase64(str)); + curLine.add(convertFromBase64(str)); } lines.add(curLine.toArray(new String[] {})); } @@ -101,13 +105,13 @@ public void saveLines(String[][] strings) { } StringBuilder builder = new StringBuilder(); for (String s : strings[i]) { - builder.append(',').append(toBase64(s)); + builder.append(',').append(convertToBase64(s)); } writer.append(builder.substring(1)); } writer.close(); } catch (IOException ex) { - Ui.messagePrint("(>.<') I was unable to record your tasks..."); + Ui.printStyledMessage("(>.<') I was unable to record your tasks..."); ex.printStackTrace(); } } diff --git a/src/main/java/duke/Ui.java b/src/main/java/duke/Ui.java index 8b66775451..9137330b09 100644 --- a/src/main/java/duke/Ui.java +++ b/src/main/java/duke/Ui.java @@ -5,16 +5,12 @@ import java.util.ArrayList; public class Ui { - /** List of commands */ - private static ArrayList commands; - /** List of strings to remember */ - private static ArrayList list; /** * Styles and prints lines with a border. * @param lines Lines to be printed */ - public static void messagePrint(String... lines) { + public static void printStyledMessage(String... lines) { System.out.println(",----------------------------------------------------------------"); for (String str : lines) { System.out.print("| "); @@ -27,7 +23,7 @@ public static void messagePrint(String... lines) { * Greets user. */ public static void greet() { - Ui.messagePrint("...where is this again?", + Ui.printStyledMessage("...where is this again?", "Oh, hello, I didn't see you there - I'm Anthea, a chatbot...", "...or at least that's what they told me."); } @@ -36,6 +32,6 @@ public static void greet() { * Leaves the user. */ public static void leave() { - Ui.messagePrint("It was nice to have you around, I'm going back to sleep..."); + Ui.printStyledMessage("It was nice to have you around, I'm going back to sleep..."); } } diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java index 1f38b2e1cb..b7d8f7e2d1 100644 --- a/src/main/java/duke/task/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -21,10 +21,10 @@ public Deadline(String description, String by) { * Creates a duke.tasks.Deadline object. * @param description Description of deadline. * @param by Time of deadline. - * @param done If the task is done. + * @param isDone If the task is done. */ - public Deadline(String description, String by, boolean done) { - super(description, done); + public Deadline(String description, String by, boolean isDone) { + super(description, isDone); datetime = new ParsedDateTime(by); } diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java index e16fb19e07..52b476f942 100644 --- a/src/main/java/duke/task/Event.java +++ b/src/main/java/duke/task/Event.java @@ -21,10 +21,10 @@ public Event(String description, String at) { * Creates an event. * @param description Description of event. * @param at Time of event. - * @param done If the task is done. + * @param isDone If the task is done. */ - public Event(String description, String at, boolean done) { - super(description, done); + public Event(String description, String at, boolean isDone) { + super(description, isDone); datetime = new ParsedDateTime(at); } diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java index a30269e77d..bdd85b689b 100644 --- a/src/main/java/duke/task/Task.java +++ b/src/main/java/duke/task/Task.java @@ -7,32 +7,32 @@ public class Task { private static final char MARKER_DONE = 'X'; private static final char MARKER_NOT_DONE = ' '; private String name; - private boolean done; + private boolean isDone; Task(String name) { this(name, false); } - Task(String name, boolean done) { + Task(String name, boolean isDone) { this.name = name; - this.done = done; + this.isDone = isDone; } /** * Marks the task as done. */ public void markAsDone() { - this.done = true; + this.isDone = true; } /** * Marks the task as not done. */ public void markAsNotDone() { - this.done = false; + this.isDone = false; } private char getDoneMarker() { - return this.done ? MARKER_DONE : MARKER_NOT_DONE; + return this.isDone ? MARKER_DONE : MARKER_NOT_DONE; } /** @@ -49,6 +49,6 @@ public String toString() { * @return String array representation. */ public String[] getAsStringArray() { - return new String[]{ "Task", name, String.valueOf(done) }; + return new String[]{ "Task", name, String.valueOf(isDone) }; } } diff --git a/src/main/java/duke/task/TaskFactory.java b/src/main/java/duke/task/TaskFactory.java index c594c22414..a6799834f3 100644 --- a/src/main/java/duke/task/TaskFactory.java +++ b/src/main/java/duke/task/TaskFactory.java @@ -42,7 +42,7 @@ public static Optional constructOptionalTask(String[] taskData) { try { return Optional.of(constructTask(taskData)); } catch (IllegalArgumentException ex) { - Ui.messagePrint("(>.<') did not understand this task - dropping it", + Ui.printStyledMessage("(>.<') did not understand this task - dropping it", String.join(", ", taskData)); return Optional.empty(); } diff --git a/src/main/java/duke/task/TaskList.java b/src/main/java/duke/task/TaskList.java index fc2ddbe736..b20a8846f1 100644 --- a/src/main/java/duke/task/TaskList.java +++ b/src/main/java/duke/task/TaskList.java @@ -32,10 +32,10 @@ public static Optional getTask(String index) { Task task = taskList.get(idx - 1); return Optional.of(task); } catch (NumberFormatException ex) { - Ui.messagePrint("Sorry, I didn't understand " + index + ", please give me a number."); + Ui.printStyledMessage("Sorry, I didn't understand " + index + ", please give me a number."); return Optional.empty(); } catch (IndexOutOfBoundsException ex) { - Ui.messagePrint("Sorry, the number " + index + ", wasn't in the range."); + Ui.printStyledMessage("Sorry, the number " + index + ", wasn't in the range."); return Optional.empty(); } } diff --git a/src/main/java/duke/task/ToDo.java b/src/main/java/duke/task/ToDo.java index bf17b053ac..09d4e8fe25 100644 --- a/src/main/java/duke/task/ToDo.java +++ b/src/main/java/duke/task/ToDo.java @@ -16,10 +16,10 @@ public ToDo(String description) { /** * Creates a task item. * @param description Description of task. - * @param done If the task is done. + * @param isDone If the task is done. */ - public ToDo(String description, boolean done) { - super(description, done); + public ToDo(String description, boolean isDone) { + super(description, isDone); } @Override diff --git a/src/test/java/duke/task/DeadlineTest.java b/src/test/java/duke/task/DeadlineTest.java index ba3f65dfeb..cce58c8a1c 100644 --- a/src/test/java/duke/task/DeadlineTest.java +++ b/src/test/java/duke/task/DeadlineTest.java @@ -7,7 +7,7 @@ public class DeadlineTest { @Test - public void testStringArray() { + public void getAsStringArray_makingObjects_normalBehavior() { Task task1 = new Deadline("task one", "1", false); Task task2 = new Deadline("task two", "2", true); Task task3 = new Deadline("task three", "3"); diff --git a/src/test/java/duke/task/TaskTest.java b/src/test/java/duke/task/TaskTest.java index 2f0acd6d6b..d34aee8247 100644 --- a/src/test/java/duke/task/TaskTest.java +++ b/src/test/java/duke/task/TaskTest.java @@ -8,7 +8,7 @@ public class TaskTest { @Test - public void testDoneMarker() { + public void markAsDone_togglingDoneAndNotDone_normalBehavior() { Task task1 = new Task("task one", false); Task task2 = new Task("task two", true); Task task3 = new Task("task three"); @@ -43,7 +43,7 @@ public void testDoneMarker() { } @Test - public void testStringArray() { + public void getAsStringArray_makingObjects_normalBehavior() { Task task1 = new Task("task one", false); Task task2 = new Task("task two", true); Task task3 = new Task("task three"); From 366007cec020cda13722040c93d071d828381f03 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Fri, 26 Aug 2022 22:09:02 +0800 Subject: [PATCH 22/67] Add JavaDoc for all public methods --- src/main/java/duke/ParsedDateTime.java | 4 ++++ src/main/java/duke/Parser.java | 4 ++++ src/main/java/duke/Storage.java | 13 +++++++++++-- src/main/java/duke/task/Deadline.java | 6 ++++-- src/main/java/duke/task/Event.java | 6 ++++-- src/main/java/duke/task/TaskList.java | 12 +++++++++++- 6 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/main/java/duke/ParsedDateTime.java b/src/main/java/duke/ParsedDateTime.java index 8e6c43c7f0..781838d862 100644 --- a/src/main/java/duke/ParsedDateTime.java +++ b/src/main/java/duke/ParsedDateTime.java @@ -37,6 +37,10 @@ public ParsedDateTime(String input) { } } + /** + * Gets a nicely-formatted date. + * @return The date if it parses, else the original string. + */ @Override public String toString() { return parsedDateTime.map((dateTime) -> { diff --git a/src/main/java/duke/Parser.java b/src/main/java/duke/Parser.java index 5e2b811e38..3bd416cabe 100644 --- a/src/main/java/duke/Parser.java +++ b/src/main/java/duke/Parser.java @@ -14,6 +14,10 @@ */ public class Parser { + /** + * Gets the chatbot commands in an ArrayList. + * @return ArrayList of chatbot commands. + */ public static ArrayList getCommands() { ArrayList commands = new ArrayList<>(); diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java index 83bc5e1da9..4ac5b98a95 100644 --- a/src/main/java/duke/Storage.java +++ b/src/main/java/duke/Storage.java @@ -21,6 +21,11 @@ private Storage(String fileName) { this.fileName = fileName; } + /** + * Gets a singleton object that manages a particular file. + * @param fileName File name to identify file. + * @return Object managing the file. + */ public static Storage getFileState(String fileName) { if (!fileStates.containsKey(fileName)) { fileStates.put(fileName, new Storage(fileName)); @@ -69,6 +74,10 @@ private static String fromBase64(String input) { return StandardCharsets.UTF_16.decode(ByteBuffer.wrap(Base64.getDecoder().decode(input))).toString(); } + /** + * Gets lines from file as a String[][]. + * @return Array of String[], each storing comma-separated parts of a line. + */ public String[][] getLines() { ArrayList lines = new ArrayList<>(); try { @@ -79,9 +88,9 @@ public String[][] getLines() { for (String str : sc.nextLine().split(",")) { curLine.add(fromBase64(str)); } - lines.add(curLine.toArray(new String[] {})); + lines.add(curLine.toArray(new String[]{})); } - return (String[][]) lines.toArray(new String[][] {}); + return (String[][]) lines.toArray(new String[][]{}); } catch (FileNotFoundException ex) { // file not found or error return new String[][] {}; diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java index 1f38b2e1cb..25e10623f0 100644 --- a/src/main/java/duke/task/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -28,14 +28,16 @@ public Deadline(String description, String by, boolean done) { datetime = new ParsedDateTime(by); } + /** + * {@inheritDoc} + */ @Override public String toString() { return String.format("[D]%s (by: %s)", super.toString(), datetime.toString()); } /** - * Get a string array representation suitable for printing to files. - * @return String array representation. + * {@inheritDoc} */ @Override public String[] getAsStringArray() { diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java index e16fb19e07..6f65f519d2 100644 --- a/src/main/java/duke/task/Event.java +++ b/src/main/java/duke/task/Event.java @@ -28,14 +28,16 @@ public Event(String description, String at, boolean done) { datetime = new ParsedDateTime(at); } + /** + * {@inheritDoc} + */ @Override public String toString() { return String.format("[E]%s (at: %s)", super.toString(), datetime.toString()); } /** - * Get a string array representation suitable for printing to files. - * @return String array representation. + * {@inheritDoc} */ @Override public String[] getAsStringArray() { diff --git a/src/main/java/duke/task/TaskList.java b/src/main/java/duke/task/TaskList.java index fc2ddbe736..2f0cba88d2 100644 --- a/src/main/java/duke/task/TaskList.java +++ b/src/main/java/duke/task/TaskList.java @@ -13,16 +13,22 @@ public class TaskList { /** List of tasks to remember */ private static ArrayList taskList = new ArrayList<>(); + /** + * Initializes the task list. + */ public static void initializeTaskList() { taskList = TaskStorage.getTasks(); } + /** + * Finalizes the task list. + */ public static void finalizeTaskList() { TaskStorage.saveTasks(taskList); } /** - * Get task from index as string. + * Gets task from index as string. * @param index Index as a string. * @return Optional.of(task) if successful, else Optional.empty(). */ @@ -40,6 +46,10 @@ public static Optional getTask(String index) { } } + /** + * Gets the task list for other classes to work on. + * @return The task list. + */ public static List getTaskList() { return taskList; } From fe22a6b209ce492f3139e4b3dc69d76395a39f94 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Fri, 26 Aug 2022 22:26:41 +0800 Subject: [PATCH 23/67] Add find feature --- src/main/java/duke/Parser.java | 23 ++++++++++++++++------- src/main/java/duke/task/Task.java | 9 +++++++++ src/main/java/duke/task/TaskList.java | 12 ++++++++++++ text-ui-test/ACTUAL1.TXT | 23 +++++++++++++++++++++++ text-ui-test/EXPECTED.TXT | 19 +++++++++++++++++++ text-ui-test/EXPECTED1.TXT | 23 +++++++++++++++++++++++ text-ui-test/input.txt | 4 ++++ 7 files changed, 106 insertions(+), 7 deletions(-) diff --git a/src/main/java/duke/Parser.java b/src/main/java/duke/Parser.java index 5e2b811e38..61171b8446 100644 --- a/src/main/java/duke/Parser.java +++ b/src/main/java/duke/Parser.java @@ -14,17 +14,21 @@ */ public class Parser { + private static String[] listTasks(String response, List tasks) { + String[] output = new String[tasks.size() + 1]; + output[0] = response; + for (int i = 0; i < tasks.size(); i++) { + output[i + 1] = (i + 1) + "." + tasks.get(i).toString(); + } + return output; + } + public static ArrayList getCommands() { ArrayList commands = new ArrayList<>(); commands.add(new CommandMatcher((str) -> str.equals("list"), (str) -> { - List list = TaskList.getTaskList(); - String[] output = new String[list.size() + 1]; - output[0] = "Here, your tasks:"; - for (int i = 0; i < list.size(); i++) { - output[i + 1] = (i + 1) + "." + list.get(i).toString(); - } - Ui.messagePrint(output); + List tasks = TaskList.getTaskList(); + Ui.messagePrint(listTasks("Here, your tasks:", tasks)); })); commands.add(new PrefixCommandMatcher("mark", (str, map) -> { @@ -73,6 +77,11 @@ public static ArrayList getCommands() { }); })); + commands.add(new PrefixCommandMatcher("find", (str, map) -> { + List tasks = TaskList.filterTasks(str); + Ui.messagePrint(listTasks("Here are the tasks that you might be looking for:", tasks)); + })); + // default command matcher - add to list commands.add(new CommandMatcher((str) -> true, (str) -> { Ui.messagePrint("(>.<') I'm sorry, I don't really know what that means."); diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java index a30269e77d..f65c990891 100644 --- a/src/main/java/duke/task/Task.java +++ b/src/main/java/duke/task/Task.java @@ -31,6 +31,15 @@ public void markAsNotDone() { this.done = false; } + /** + * Checks if the task description matches the query. + * @param query Query for the task to check. + * @return True if the task description matches the query. + */ + public boolean isMatchingQuery(String query) { + return name.contains(query); + } + private char getDoneMarker() { return this.done ? MARKER_DONE : MARKER_NOT_DONE; } diff --git a/src/main/java/duke/task/TaskList.java b/src/main/java/duke/task/TaskList.java index fc2ddbe736..05c733c5cc 100644 --- a/src/main/java/duke/task/TaskList.java +++ b/src/main/java/duke/task/TaskList.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import duke.Ui; @@ -40,6 +41,17 @@ public static Optional getTask(String index) { } } + /** + * Get tasks that match the search term. + * @param query Search term. + * @return List of tasks. + */ + public static List filterTasks(String query) { + return taskList.stream() + .filter((task) -> task.isMatchingQuery(query)) + .collect(Collectors.toList()); + } + public static List getTaskList() { return taskList; } diff --git a/text-ui-test/ACTUAL1.TXT b/text-ui-test/ACTUAL1.TXT index daa5869735..0ca8fc0ec9 100644 --- a/text-ui-test/ACTUAL1.TXT +++ b/text-ui-test/ACTUAL1.TXT @@ -300,5 +300,28 @@ | 16.[E][ ] wow much space (at: 10am) '---------------------------------------------------------------- ,---------------------------------------------------------------- +| (>.<') Add a description to your find. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your find. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here are the tasks that you might be looking for: +| 1.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][ ] meet people at dinner (at: 6:25 pm) +| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here are the tasks that you might be looking for: +| 1.[T][ ] homework +| 2.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 3.[T][ ] homework +| 4.[T][ ] other homework +| 5.[E][ ] meet friends (at: [unknown]) +| 6.[E][ ] meet people at dinner (at: 6:25 pm) +| 7.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- | It was nice to have you around, I'm going back to sleep... '---------------------------------------------------------------- diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index f8387f0ae7..86501ca8fa 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -209,5 +209,24 @@ | 8.[E][ ] wow much space (at: 10am) '---------------------------------------------------------------- ,---------------------------------------------------------------- +| (>.<') Add a description to your find. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your find. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here are the tasks that you might be looking for: +| 1.[E][ ] meet friends (at: [unknown]) +| 2.[E][ ] meet people at dinner (at: 6:25 pm) +| 3.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here are the tasks that you might be looking for: +| 1.[T][ ] homework +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][ ] meet people at dinner (at: 6:25 pm) +| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- | It was nice to have you around, I'm going back to sleep... '---------------------------------------------------------------- diff --git a/text-ui-test/EXPECTED1.TXT b/text-ui-test/EXPECTED1.TXT index daa5869735..0ca8fc0ec9 100644 --- a/text-ui-test/EXPECTED1.TXT +++ b/text-ui-test/EXPECTED1.TXT @@ -300,5 +300,28 @@ | 16.[E][ ] wow much space (at: 10am) '---------------------------------------------------------------- ,---------------------------------------------------------------- +| (>.<') Add a description to your find. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| (>.<') Add a description to your find. +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here are the tasks that you might be looking for: +| 1.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 2.[E][ ] meet friends (at: [unknown]) +| 3.[E][ ] meet people at dinner (at: 6:25 pm) +| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- +| Here are the tasks that you might be looking for: +| 1.[T][ ] homework +| 2.[E][X] meet people at function x/y/z whatever (at: 7:12am) +| 3.[T][ ] homework +| 4.[T][ ] other homework +| 5.[E][ ] meet friends (at: [unknown]) +| 6.[E][ ] meet people at dinner (at: 6:25 pm) +| 7.[E][ ] meet people at function x/y/z whatever (at: 7:12am) +'---------------------------------------------------------------- +,---------------------------------------------------------------- | It was nice to have you around, I'm going back to sleep... '---------------------------------------------------------------- diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 9c999d0633..8ea2993b78 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -44,4 +44,8 @@ delete nan delete 24 event wow much space / very /wow /at 10am list +find +find +find meet +find me bye From ee22800f4214284642efdd8de1ad509091b9617e Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Sat, 27 Aug 2022 11:03:28 +0800 Subject: [PATCH 24/67] Add checkstyle and prepare for JavaFX --- build.gradle | 20 ++ config/checkstyle/checkstyle.xml | 429 +++++++++++++++++++++++++++++ config/checkstyle/suppressions.xml | 10 + 3 files changed, 459 insertions(+) create mode 100644 config/checkstyle/checkstyle.xml create mode 100644 config/checkstyle/suppressions.xml diff --git a/build.gradle b/build.gradle index c4e66ae0eb..9210096bab 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,11 @@ plugins { id 'java' id 'application' id 'com.github.johnrengelman.shadow' version '5.1.0' + id 'checkstyle' +} + +checkstyle { + toolVersion = '10.2' } repositories { @@ -11,6 +16,21 @@ repositories { dependencies { testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' + + String javaFxVersion = '11' + + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux' } test { diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000000..870acebdda --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,429 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml new file mode 100644 index 0000000000..dcaa1af3c3 --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file From 2ef6f93f26b723bade0280389e3452c966c38f08 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Sat, 27 Aug 2022 15:50:24 +0800 Subject: [PATCH 25/67] Refactor Predicate in CommandMatcher to shouldRunCommand --- src/main/java/duke/CommandMatcher.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/duke/CommandMatcher.java b/src/main/java/duke/CommandMatcher.java index e69d884771..c123b362db 100644 --- a/src/main/java/duke/CommandMatcher.java +++ b/src/main/java/duke/CommandMatcher.java @@ -8,16 +8,16 @@ * as a matching process and an action. */ public class CommandMatcher { - private Predicate check; + private Predicate shouldRunAction; private Consumer action; /** * Creates an object that handles checking and executing a command. - * @param check Predicate to check if the command should be run. + * @param shouldRunAction Predicate to check if the command should be run. * @param action Action to run. */ - public CommandMatcher(Predicate check, Consumer action) { - this.check = check; + public CommandMatcher(Predicate shouldRunAction, Consumer action) { + this.shouldRunAction = shouldRunAction; this.action = action; } @@ -27,7 +27,7 @@ public CommandMatcher(Predicate check, Consumer action) { * @param action Action to run. */ public CommandMatcher(String prefix, Consumer action) { - this.check = (cmd) -> cmd.strip().startsWith(prefix); + this.shouldRunAction = (cmd) -> cmd.strip().startsWith(prefix); this.action = action; } @@ -38,7 +38,7 @@ public CommandMatcher(String prefix, Consumer action) { * @return If the string matches. */ public boolean run(String input) { - if (check.test(input)) { + if (shouldRunAction.test(input)) { action.accept(input); return true; } From 7ba6ac32635fa24be7173e4f5022155c61161739 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Sat, 27 Aug 2022 15:53:38 +0800 Subject: [PATCH 26/67] Add more JUnit tests --- src/test/java/duke/task/EventTest.java | 23 +++++++++++++++++++++++ src/test/java/duke/task/ToDoTest.java | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 src/test/java/duke/task/EventTest.java create mode 100644 src/test/java/duke/task/ToDoTest.java diff --git a/src/test/java/duke/task/EventTest.java b/src/test/java/duke/task/EventTest.java new file mode 100644 index 0000000000..6f017c68c6 --- /dev/null +++ b/src/test/java/duke/task/EventTest.java @@ -0,0 +1,23 @@ +package duke.task; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class EventTest { + @Test + public void getAsStringArray_makingObjects_normalBehavior() { + Task task1 = new Event("task one", "1", false); + Task task2 = new Event("task two", "2", true); + Task task3 = new Event("task three", "3"); + + assertEquals("[E][ ] task one (at: 1)", task1.toString()); + assertEquals("[E][X] task two (at: 2)", task2.toString()); + assertEquals("[E][ ] task three (at: 3)", task3.toString()); + + assertArrayEquals(new String[] { "Event", "task one", "false", "1" }, task1.getAsStringArray()); + assertArrayEquals(new String[] { "Event", "task two", "true", "2" }, task2.getAsStringArray()); + assertArrayEquals(new String[] { "Event", "task three", "false", "3" }, task3.getAsStringArray()); + } +} diff --git a/src/test/java/duke/task/ToDoTest.java b/src/test/java/duke/task/ToDoTest.java new file mode 100644 index 0000000000..88716b1f2a --- /dev/null +++ b/src/test/java/duke/task/ToDoTest.java @@ -0,0 +1,23 @@ +package duke.task; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ToDoTest { + @Test + public void getAsStringArray_makingObjects_normalBehavior() { + Task task1 = new ToDo("task one", false); + Task task2 = new ToDo("task two", true); + Task task3 = new ToDo("task three"); + + assertEquals("[T][ ] task one", task1.toString()); + assertEquals("[T][X] task two", task2.toString()); + assertEquals("[T][ ] task three", task3.toString()); + + assertArrayEquals(new String[] { "ToDo", "task one", "false" }, task1.getAsStringArray()); + assertArrayEquals(new String[] { "ToDo", "task two", "true" }, task2.getAsStringArray()); + assertArrayEquals(new String[] { "ToDo", "task three", "false" }, task3.getAsStringArray()); + } +} From 9a79557e67a15b1b645cf1999bed714e0c91e4fe Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Sun, 28 Aug 2022 10:50:24 +0800 Subject: [PATCH 27/67] Remove unused commented out code --- src/main/java/duke/Storage.java | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java index aac5b7a5fa..092bf0c16a 100644 --- a/src/main/java/duke/Storage.java +++ b/src/main/java/duke/Storage.java @@ -37,39 +37,6 @@ public static Storage getFileState(String fileName) { return fileStates.get(fileName); } - /*public String getContents() { - if (contents == null) { - try { - File f = new File(fileName); - Scanner s = new Scanner(f); - StringBuilder sb = new StringBuilder(); - boolean isFirst = true; - while (s.hasNext()) { - if (isFirst) { - isFirst = false; - } else { - sb.append('\n'); - } - sb.append(s.nextLine()); - } - contents = sb.toString(); - } catch (FileNotFoundException ex) { - // file not found or error - contents = ""; - } - } - return contents; - }*/ - - ///** - // * Saves the string to the file on disk - // * @param str string to save - // */ - /*public void saveContents(String str) { - contents = str; - // write to file TODO - }*/ - private static String convertToBase64(String input) { return Base64.getEncoder().encodeToString(input.getBytes(StandardCharsets.UTF_16)); } From 73bb0233ca340c6f8581b3be728fd0b54f0b4d3e Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Wed, 31 Aug 2022 15:43:31 +0800 Subject: [PATCH 28/67] Fix JavaDoc by adding the missing blank line in JavaDoc comment --- src/main/java/duke/CommandMatcher.java | 3 +++ src/main/java/duke/Duke.java | 1 + src/main/java/duke/ParsedDateTime.java | 2 ++ src/main/java/duke/Parser.java | 1 + src/main/java/duke/Storage.java | 3 +++ src/main/java/duke/Ui.java | 3 ++- src/main/java/duke/task/Deadline.java | 2 ++ src/main/java/duke/task/Event.java | 2 ++ src/main/java/duke/task/Task.java | 3 +++ src/main/java/duke/task/TaskFactory.java | 2 ++ src/main/java/duke/task/TaskList.java | 3 +++ src/main/java/duke/task/TaskStorage.java | 2 ++ src/main/java/duke/task/ToDo.java | 3 +++ 13 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/main/java/duke/CommandMatcher.java b/src/main/java/duke/CommandMatcher.java index c123b362db..f8f106fb7c 100644 --- a/src/main/java/duke/CommandMatcher.java +++ b/src/main/java/duke/CommandMatcher.java @@ -13,6 +13,7 @@ public class CommandMatcher { /** * Creates an object that handles checking and executing a command. + * * @param shouldRunAction Predicate to check if the command should be run. * @param action Action to run. */ @@ -23,6 +24,7 @@ public CommandMatcher(Predicate shouldRunAction, Consumer action /** * Creates an object that handles checking and executing a command. + * * @param prefix Prefix of the command which is checked. * @param action Action to run. */ @@ -34,6 +36,7 @@ public CommandMatcher(String prefix, Consumer action) { /** * Checks if the string matches. * If it does, it would execute the action. + * * @param input String to check if it is for this command. * @return If the string matches. */ diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index ff37da1c75..84394d0883 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -38,6 +38,7 @@ private static void handleCommand(String command) { /** * Runs the chatbot execution. + * * @param args Command line args which are not used. */ public static void main(String[] args) { diff --git a/src/main/java/duke/ParsedDateTime.java b/src/main/java/duke/ParsedDateTime.java index 781838d862..b3fc5d9c61 100644 --- a/src/main/java/duke/ParsedDateTime.java +++ b/src/main/java/duke/ParsedDateTime.java @@ -22,6 +22,7 @@ public class ParsedDateTime { /** * Creates an object to handle if the date/time can be parsed. + * * @param input String that may represent date/time. */ public ParsedDateTime(String input) { @@ -39,6 +40,7 @@ public ParsedDateTime(String input) { /** * Gets a nicely-formatted date. + * * @return The date if it parses, else the original string. */ @Override diff --git a/src/main/java/duke/Parser.java b/src/main/java/duke/Parser.java index 7d372a3bc9..50f6f04477 100644 --- a/src/main/java/duke/Parser.java +++ b/src/main/java/duke/Parser.java @@ -25,6 +25,7 @@ private static String[] listTasks(String response, List tasks) { /** * Gets the chatbot commands in an ArrayList. + * * @return ArrayList of chatbot commands. */ public static ArrayList getCommands() { diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java index 092bf0c16a..5021f19b0c 100644 --- a/src/main/java/duke/Storage.java +++ b/src/main/java/duke/Storage.java @@ -27,6 +27,7 @@ private Storage(String fileName) { /** * Gets a singleton object that manages a particular file. + * * @param fileName File name to identify file. * @return Object managing the file. */ @@ -47,6 +48,7 @@ private static String convertFromBase64(String input) { /** * Gets lines from file as a String[][]. + * * @return Array of String[], each storing comma-separated parts of a line. */ public String[][] getLines() { @@ -70,6 +72,7 @@ public String[][] getLines() { /** * Saves the string to the file on disk. + * * @param strings Strings to save. */ public void saveLines(String[][] strings) { diff --git a/src/main/java/duke/Ui.java b/src/main/java/duke/Ui.java index 9137330b09..3017d778cd 100644 --- a/src/main/java/duke/Ui.java +++ b/src/main/java/duke/Ui.java @@ -8,7 +8,8 @@ public class Ui { /** * Styles and prints lines with a border. - * @param lines Lines to be printed + * + * @param lines Line to be printed */ public static void printStyledMessage(String... lines) { System.out.println(",----------------------------------------------------------------"); diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java index 5d37952b27..a29b0c5fdc 100644 --- a/src/main/java/duke/task/Deadline.java +++ b/src/main/java/duke/task/Deadline.java @@ -10,6 +10,7 @@ public class Deadline extends Task { /** * Creates a duke.tasks.Deadline object. + * * @param description Description of deadline. * @param by Time of deadline. */ @@ -19,6 +20,7 @@ public Deadline(String description, String by) { /** * Creates a duke.tasks.Deadline object. + * * @param description Description of deadline. * @param by Time of deadline. * @param isDone If the task is done. diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java index 1992399bf4..42233ee4ac 100644 --- a/src/main/java/duke/task/Event.java +++ b/src/main/java/duke/task/Event.java @@ -10,6 +10,7 @@ public class Event extends Task { /** * Creates an event. + * * @param description Description of event. * @param at Time of event. */ @@ -19,6 +20,7 @@ public Event(String description, String at) { /** * Creates an event. + * * @param description Description of event. * @param at Time of event. * @param isDone If the task is done. diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java index 7061122802..dffc895d7c 100644 --- a/src/main/java/duke/task/Task.java +++ b/src/main/java/duke/task/Task.java @@ -33,6 +33,7 @@ public void markAsNotDone() { /** * Checks if the task description matches the query. + * * @param query Query for the task to check. * @return True if the task description matches the query. */ @@ -46,6 +47,7 @@ private char getDoneMarker() { /** * Creates a string representation suitable for printing to screen. + * * @return String representation of task. */ @Override @@ -55,6 +57,7 @@ public String toString() { /** * Creates a string array representation suitable for printing to files. + * * @return String array representation. */ public String[] getAsStringArray() { diff --git a/src/main/java/duke/task/TaskFactory.java b/src/main/java/duke/task/TaskFactory.java index a6799834f3..bf8d882891 100644 --- a/src/main/java/duke/task/TaskFactory.java +++ b/src/main/java/duke/task/TaskFactory.java @@ -10,6 +10,7 @@ public class TaskFactory { /** * Constructs the task. + * * @param taskData Data for the task. * @return Task according to taskData. * @throws IllegalArgumentException If taskData does not conform to the format. @@ -35,6 +36,7 @@ public static Task constructTask(String[] taskData) throws IllegalArgumentExcept /** * Constructs the task. + * * @param taskData Data for the task. * @return Optional of Task according to taskData, Optional.empty() if cannot construct. */ diff --git a/src/main/java/duke/task/TaskList.java b/src/main/java/duke/task/TaskList.java index bc830e88e0..0c422cfb11 100644 --- a/src/main/java/duke/task/TaskList.java +++ b/src/main/java/duke/task/TaskList.java @@ -30,6 +30,7 @@ public static void finalizeTaskList() { /** * Gets task from index as string. + * * @param index Index as a string. * @return Optional.of(task) if successful, else Optional.empty(). */ @@ -49,6 +50,7 @@ public static Optional getTask(String index) { /** * Get tasks that match the search term. + * * @param query Search term. * @return List of tasks. */ @@ -60,6 +62,7 @@ public static List filterTasks(String query) { /** * Gets the task list for other classes to work on. + * * @return The task list. */ public static List getTaskList() { diff --git a/src/main/java/duke/task/TaskStorage.java b/src/main/java/duke/task/TaskStorage.java index 83e0f01fe9..b421c560a4 100644 --- a/src/main/java/duke/task/TaskStorage.java +++ b/src/main/java/duke/task/TaskStorage.java @@ -13,6 +13,7 @@ public class TaskStorage { /** * Gets ArrayList of previously saved tasks. + * * @return ArrayList of tasks. */ public static ArrayList getTasks() { @@ -26,6 +27,7 @@ public static ArrayList getTasks() { /** * Saves a list of tasks to the default file. + * * @param tasks List of tasks. */ public static void saveTasks(List tasks) { diff --git a/src/main/java/duke/task/ToDo.java b/src/main/java/duke/task/ToDo.java index 09d4e8fe25..3c3596d9d4 100644 --- a/src/main/java/duke/task/ToDo.java +++ b/src/main/java/duke/task/ToDo.java @@ -7,6 +7,7 @@ public class ToDo extends Task { /** * Creates a task item. + * * @param description Description of task. */ public ToDo(String description) { @@ -15,6 +16,7 @@ public ToDo(String description) { /** * Creates a task item. + * * @param description Description of task. * @param isDone If the task is done. */ @@ -29,6 +31,7 @@ public String toString() { /** * Get a string array representation suitable for printing to files. + * * @return String array representation. */ @Override From 86b1cde714e0f7fcb22767745d33210c40fe4a91 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Fri, 2 Sep 2022 21:49:31 +0800 Subject: [PATCH 29/67] Add backwards compatible text-ui-test regression testing as integration tests --- src/main/java/duke/Duke.java | 25 ++++++++++--- src/main/java/duke/GuiHandler.java | 32 +++++++++++++++++ src/main/java/duke/Parser.java | 27 +++++++++----- src/main/java/duke/PrefixCommandMatcher.java | 10 +++--- src/main/java/duke/Storage.java | 3 +- src/main/java/duke/Ui.java | 30 ++++++++-------- src/main/java/duke/UiInterface.java | 23 ++++++++++++ src/main/java/duke/task/TaskFactory.java | 4 ++- src/main/java/duke/task/TaskList.java | 7 ++-- text-ui-test/ACTUAL1.TXT | 37 +++++++------------- text-ui-test/EXPECTED.TXT | 11 ++---- text-ui-test/EXPECTED1.TXT | 37 +++++++------------- text-ui-test/duke.txt | 4 +-- text-ui-test/runtest.bat | 4 +-- 14 files changed, 156 insertions(+), 98 deletions(-) create mode 100644 src/main/java/duke/GuiHandler.java create mode 100644 src/main/java/duke/UiInterface.java diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index 84394d0883..7ee68a1009 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -13,6 +13,16 @@ public class Duke { /** List of commands */ private static ArrayList commands; + private static UiInterface ui; + + /** + * Gets the current UiInterface for command line purposes. + * + * @return UiInterface that helps display text to screen. + */ + public static UiInterface getCurrentUi() { + return ui; + } private static Optional getTask(String index) { try { @@ -20,10 +30,10 @@ private static Optional getTask(String index) { Task task = TaskList.getTaskList().get(idx - 1); return Optional.of(task); } catch (NumberFormatException ex) { - Ui.printStyledMessage("Sorry, I didn't understand " + index + ", please give me a number."); + ui.printStyledMessage("Sorry, I didn't understand " + index + ", please give me a number."); return Optional.empty(); } catch (IndexOutOfBoundsException ex) { - Ui.printStyledMessage("Sorry, the number " + index + ", wasn't in the range."); + ui.printStyledMessage("Sorry, the number " + index + ", wasn't in the range."); return Optional.empty(); } } @@ -42,8 +52,15 @@ private static void handleCommand(String command) { * @param args Command line args which are not used. */ public static void main(String[] args) { + // allow for console tests to run + if (args.length >= 1 && args[0].equals("console-test")) { + ui = new Ui(); + } else { + ui = new GuiHandler(); + } + // initialization - Ui.greet(); + ui.greet(); TaskList.initializeTaskList(); commands = Parser.getCommands(); Scanner input = new Scanner(System.in); @@ -61,6 +78,6 @@ public static void main(String[] args) { // finalization TaskList.finalizeTaskList(); - Ui.leave(); + ui.leave(); } } diff --git a/src/main/java/duke/GuiHandler.java b/src/main/java/duke/GuiHandler.java new file mode 100644 index 0000000000..edb652391c --- /dev/null +++ b/src/main/java/duke/GuiHandler.java @@ -0,0 +1,32 @@ +package duke; + +/** + * Used for GUI. + */ +public class GuiHandler implements UiInterface { + /** + * Prints lines, when actually implemented. + * + * @param lines Line to be printed + */ + @Override + public void printStyledMessage(String... lines) { + // stub + } + + /** + * Greets user, when actually implemented. + */ + @Override + public void greet() { + // stub + } + + /** + * Leaves the user, when actually implemented. + */ + @Override + public void leave() { + // stub + } +} diff --git a/src/main/java/duke/Parser.java b/src/main/java/duke/Parser.java index 50f6f04477..b176624af1 100644 --- a/src/main/java/duke/Parser.java +++ b/src/main/java/duke/Parser.java @@ -33,13 +33,15 @@ public static ArrayList getCommands() { commands.add(new CommandMatcher((str) -> str.equals("list"), (str) -> { List tasks = TaskList.getTaskList(); - Ui.printStyledMessage(listTasks("Here, your tasks:", tasks)); + Duke.getCurrentUi().printStyledMessage( + listTasks("Here, your tasks:", tasks)); })); commands.add(new PrefixCommandMatcher("mark", (str, map) -> { TaskList.getTask(str).ifPresent((task) -> { task.markAsDone(); - Ui.printStyledMessage("Marked your task as done:", + Duke.getCurrentUi().printStyledMessage( + "Marked your task as done:", task.toString()); }); })); @@ -47,7 +49,8 @@ public static ArrayList getCommands() { commands.add(new PrefixCommandMatcher("unmark", (str, map) -> { TaskList.getTask(str).ifPresent((task) -> { task.markAsNotDone(); - Ui.printStyledMessage("Aw... it's not done yet:", + Duke.getCurrentUi().printStyledMessage( + "Aw... it's not done yet:", task.toString()); }); })); @@ -55,28 +58,32 @@ public static ArrayList getCommands() { commands.add(new PrefixCommandMatcher("deadline", (str, map) -> { Task task = new Deadline(str, map.getOrDefault("by", "[unknown]")); TaskList.getTaskList().add(task); - Ui.printStyledMessage("Good luck with the deadline, here's the task:", + Duke.getCurrentUi().printStyledMessage( + "Good luck with the deadline, here's the task:", task.toString()); })); commands.add(new PrefixCommandMatcher("todo", (str, map) -> { Task task = new ToDo(str); TaskList.getTaskList().add(task); - Ui.printStyledMessage("I've recorded this thing you need to do:", + Duke.getCurrentUi().printStyledMessage( + "I've recorded this thing you need to do:", task.toString()); })); commands.add(new PrefixCommandMatcher("event", (str, map) -> { Task task = new Event(str, map.getOrDefault("at", "[unknown]")); TaskList.getTaskList().add(task); - Ui.printStyledMessage("That's going to happen at some time later:", + Duke.getCurrentUi().printStyledMessage( + "That's going to happen at some time later:", task.toString()); })); commands.add(new PrefixCommandMatcher("delete", (str, map) -> { TaskList.getTask(str).ifPresent((task) -> { TaskList.getTaskList().remove(task); - Ui.printStyledMessage("It seems you didn't need this task anymore, so I removed it:", + Duke.getCurrentUi().printStyledMessage( + "It seems you didn't need this task anymore, so I removed it:", task.toString(), String.format("You have %d tasks left.", TaskList.getTaskList().size())); }); @@ -84,12 +91,14 @@ public static ArrayList getCommands() { commands.add(new PrefixCommandMatcher("find", (str, map) -> { List tasks = TaskList.filterTasks(str); - Ui.printStyledMessage(listTasks("Here are the tasks that you might be looking for:", tasks)); + Duke.getCurrentUi().printStyledMessage( + listTasks("Here are the tasks that you might be looking for:", tasks)); })); // default command matcher - add to list commands.add(new CommandMatcher((str) -> true, (str) -> { - Ui.printStyledMessage("(>.<') I'm sorry, I don't really know what that means."); + Duke.getCurrentUi().printStyledMessage( + "(>.<') I'm sorry, I don't really know what that means."); })); return commands; diff --git a/src/main/java/duke/PrefixCommandMatcher.java b/src/main/java/duke/PrefixCommandMatcher.java index 7a38569427..9c3fc0c506 100644 --- a/src/main/java/duke/PrefixCommandMatcher.java +++ b/src/main/java/duke/PrefixCommandMatcher.java @@ -21,7 +21,8 @@ public PrefixCommandMatcher(String prefix, BiConsumer.<') Add a description to your " + prefix + "."); + Duke.getCurrentUi().printStyledMessage( + "(>.<') Add a description to your " + prefix + "."); return; } @@ -32,21 +33,20 @@ public PrefixCommandMatcher(String prefix, BiConsumer.<') The description for " + prefix + " shouldn't be empty."); + Duke.getCurrentUi().printStyledMessage("(>.<') The description for " + prefix + " shouldn't be empty."); return; } // accept - action.accept(commandParts[0].strip(), map); + action.accept(commandParts[0], map); }); } } diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java index 5021f19b0c..064f1e4266 100644 --- a/src/main/java/duke/Storage.java +++ b/src/main/java/duke/Storage.java @@ -90,7 +90,8 @@ public void saveLines(String[][] strings) { } writer.close(); } catch (IOException ex) { - Ui.printStyledMessage("(>.<') I was unable to record your tasks..."); + Duke.getCurrentUi().printStyledMessage( + "(>.<') I was unable to record your tasks..."); ex.printStackTrace(); } } diff --git a/src/main/java/duke/Ui.java b/src/main/java/duke/Ui.java index 3017d778cd..259851f862 100644 --- a/src/main/java/duke/Ui.java +++ b/src/main/java/duke/Ui.java @@ -1,17 +1,15 @@ package duke; -import duke.task.Task; - -import java.util.ArrayList; - -public class Ui { +/** + * Handles console-based user interface. Used for sanity checks. + */ +public class Ui implements UiInterface { /** - * Styles and prints lines with a border. - * - * @param lines Line to be printed + * {@inheritDoc} */ - public static void printStyledMessage(String... lines) { + @Override + public void printStyledMessage(String... lines) { System.out.println(",----------------------------------------------------------------"); for (String str : lines) { System.out.print("| "); @@ -21,18 +19,20 @@ public static void printStyledMessage(String... lines) { } /** - * Greets user. + * {@inheritDoc} */ - public static void greet() { - Ui.printStyledMessage("...where is this again?", + @Override + public void greet() { + printStyledMessage("...where is this again?", "Oh, hello, I didn't see you there - I'm Anthea, a chatbot...", "...or at least that's what they told me."); } /** - * Leaves the user. + * {@inheritDoc} */ - public static void leave() { - Ui.printStyledMessage("It was nice to have you around, I'm going back to sleep..."); + @Override + public void leave() { + printStyledMessage("It was nice to have you around, I'm going back to sleep..."); } } diff --git a/src/main/java/duke/UiInterface.java b/src/main/java/duke/UiInterface.java new file mode 100644 index 0000000000..8ee0e305f8 --- /dev/null +++ b/src/main/java/duke/UiInterface.java @@ -0,0 +1,23 @@ +package duke; + +/** + * Functions that a UI interface needs to implement. + */ +public interface UiInterface { + /** + * Styles and prints lines with a border. + * + * @param lines Lines to be printed + */ + public void printStyledMessage(String... lines); + + /** + * Greets user. + */ + public void greet(); + + /** + * Leaves the user. + */ + public void leave(); +} diff --git a/src/main/java/duke/task/TaskFactory.java b/src/main/java/duke/task/TaskFactory.java index bf8d882891..d6bb4c0d2e 100644 --- a/src/main/java/duke/task/TaskFactory.java +++ b/src/main/java/duke/task/TaskFactory.java @@ -2,6 +2,7 @@ import java.util.Optional; +import duke.Duke; import duke.Ui; /** @@ -44,7 +45,8 @@ public static Optional constructOptionalTask(String[] taskData) { try { return Optional.of(constructTask(taskData)); } catch (IllegalArgumentException ex) { - Ui.printStyledMessage("(>.<') did not understand this task - dropping it", + Duke.getCurrentUi().printStyledMessage( + "(>.<') did not understand this task - dropping it", String.join(", ", taskData)); return Optional.empty(); } diff --git a/src/main/java/duke/task/TaskList.java b/src/main/java/duke/task/TaskList.java index 0c422cfb11..73eee897f2 100644 --- a/src/main/java/duke/task/TaskList.java +++ b/src/main/java/duke/task/TaskList.java @@ -5,6 +5,7 @@ import java.util.Optional; import java.util.stream.Collectors; +import duke.Duke; import duke.Ui; /** @@ -40,10 +41,12 @@ public static Optional getTask(String index) { Task task = taskList.get(idx - 1); return Optional.of(task); } catch (NumberFormatException ex) { - Ui.printStyledMessage("Sorry, I didn't understand " + index + ", please give me a number."); + Duke.getCurrentUi().printStyledMessage( + "Sorry, I didn't understand " + index + ", please give me a number."); return Optional.empty(); } catch (IndexOutOfBoundsException ex) { - Ui.printStyledMessage("Sorry, the number " + index + ", wasn't in the range."); + Duke.getCurrentUi().printStyledMessage( + "Sorry, the number " + index + ", wasn't in the range."); return Optional.empty(); } } diff --git a/text-ui-test/ACTUAL1.TXT b/text-ui-test/ACTUAL1.TXT index 0ca8fc0ec9..be3fb5a5bb 100644 --- a/text-ui-test/ACTUAL1.TXT +++ b/text-ui-test/ACTUAL1.TXT @@ -33,7 +33,7 @@ | 5.[D][ ] /at mysterious (by: [unknown]) | 6.[D][ ] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | I've recorded this thing you need to do: @@ -76,7 +76,7 @@ | 5.[D][ ] /at mysterious (by: [unknown]) | 6.[D][ ] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -98,7 +98,7 @@ | 5.[D][ ] /at mysterious (by: [unknown]) | 6.[D][ ] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -116,7 +116,7 @@ | 5.[D][ ] /at mysterious (by: [unknown]) | 6.[D][ ] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -134,7 +134,7 @@ | 5.[D][ ] /at mysterious (by: [unknown]) | 6.[D][ ] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -152,7 +152,7 @@ | 5.[D][X] /at mysterious (by: [unknown]) | 6.[D][ ] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -170,7 +170,7 @@ | 5.[D][X] /at mysterious (by: [unknown]) | 6.[D][X] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -196,7 +196,7 @@ | 5.[D][X] /at mysterious (by: [unknown]) | 6.[D][X] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -225,7 +225,7 @@ | 5.[D][X] /at mysterious (by: [unknown]) | 6.[D][X] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -253,7 +253,7 @@ | 3.[D][X] /at mysterious (by: [unknown]) | 4.[D][X] /by mysterious number 2 (by: [unknown]) | 5.[D][ ] rush this please (by: when?) -| 6.[E][ ] wow much space (at: 10am) +| 6.[E][ ] wow much space (at: 10am) | 7.[T][ ] homework | 8.[T][ ] other homework | 9.[T][ ] project 12 @@ -278,7 +278,7 @@ '---------------------------------------------------------------- ,---------------------------------------------------------------- | That's going to happen at some time later: -| [E][ ] wow much space (at: 10am) +| [E][ ] wow much space (at: 10am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here, your tasks: @@ -287,7 +287,7 @@ | 3.[D][X] /at mysterious (by: [unknown]) | 4.[D][X] /by mysterious number 2 (by: [unknown]) | 5.[D][ ] rush this please (by: when?) -| 6.[E][ ] wow much space (at: 10am) +| 6.[E][ ] wow much space (at: 10am) | 7.[T][ ] homework | 8.[T][ ] other homework | 9.[T][ ] project 12 @@ -297,7 +297,7 @@ | 13.[D][ ] /at mysterious (by: [unknown]) | 14.[D][ ] /by mysterious number 2 (by: [unknown]) | 15.[D][ ] rush this please (by: when?) -| 16.[E][ ] wow much space (at: 10am) +| 16.[E][ ] wow much space (at: 10am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | (>.<') Add a description to your find. @@ -307,20 +307,9 @@ '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here are the tasks that you might be looking for: -| 1.[E][X] meet people at function x/y/z whatever (at: 7:12am) -| 2.[E][ ] meet friends (at: [unknown]) -| 3.[E][ ] meet people at dinner (at: 6:25 pm) -| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here are the tasks that you might be looking for: -| 1.[T][ ] homework -| 2.[E][X] meet people at function x/y/z whatever (at: 7:12am) -| 3.[T][ ] homework -| 4.[T][ ] other homework -| 5.[E][ ] meet friends (at: [unknown]) -| 6.[E][ ] meet people at dinner (at: 6:25 pm) -| 7.[E][ ] meet people at function x/y/z whatever (at: 7:12am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | It was nice to have you around, I'm going back to sleep... diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 86501ca8fa..3bb4a2c8d7 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -195,7 +195,7 @@ '---------------------------------------------------------------- ,---------------------------------------------------------------- | That's going to happen at some time later: -| [E][ ] wow much space (at: 10am) +| [E][ ] wow much space (at: 10am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here, your tasks: @@ -206,7 +206,7 @@ | 5.[D][ ] /at mysterious (by: [unknown]) | 6.[D][ ] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | (>.<') Add a description to your find. @@ -216,16 +216,9 @@ '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here are the tasks that you might be looking for: -| 1.[E][ ] meet friends (at: [unknown]) -| 2.[E][ ] meet people at dinner (at: 6:25 pm) -| 3.[E][ ] meet people at function x/y/z whatever (at: 7:12am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here are the tasks that you might be looking for: -| 1.[T][ ] homework -| 2.[E][ ] meet friends (at: [unknown]) -| 3.[E][ ] meet people at dinner (at: 6:25 pm) -| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | It was nice to have you around, I'm going back to sleep... diff --git a/text-ui-test/EXPECTED1.TXT b/text-ui-test/EXPECTED1.TXT index 0ca8fc0ec9..be3fb5a5bb 100644 --- a/text-ui-test/EXPECTED1.TXT +++ b/text-ui-test/EXPECTED1.TXT @@ -33,7 +33,7 @@ | 5.[D][ ] /at mysterious (by: [unknown]) | 6.[D][ ] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | I've recorded this thing you need to do: @@ -76,7 +76,7 @@ | 5.[D][ ] /at mysterious (by: [unknown]) | 6.[D][ ] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -98,7 +98,7 @@ | 5.[D][ ] /at mysterious (by: [unknown]) | 6.[D][ ] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -116,7 +116,7 @@ | 5.[D][ ] /at mysterious (by: [unknown]) | 6.[D][ ] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -134,7 +134,7 @@ | 5.[D][ ] /at mysterious (by: [unknown]) | 6.[D][ ] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -152,7 +152,7 @@ | 5.[D][X] /at mysterious (by: [unknown]) | 6.[D][ ] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -170,7 +170,7 @@ | 5.[D][X] /at mysterious (by: [unknown]) | 6.[D][X] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -196,7 +196,7 @@ | 5.[D][X] /at mysterious (by: [unknown]) | 6.[D][X] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -225,7 +225,7 @@ | 5.[D][X] /at mysterious (by: [unknown]) | 6.[D][X] /by mysterious number 2 (by: [unknown]) | 7.[D][ ] rush this please (by: when?) -| 8.[E][ ] wow much space (at: 10am) +| 8.[E][ ] wow much space (at: 10am) | 9.[T][ ] homework | 10.[T][ ] other homework | 11.[T][ ] project 12 @@ -253,7 +253,7 @@ | 3.[D][X] /at mysterious (by: [unknown]) | 4.[D][X] /by mysterious number 2 (by: [unknown]) | 5.[D][ ] rush this please (by: when?) -| 6.[E][ ] wow much space (at: 10am) +| 6.[E][ ] wow much space (at: 10am) | 7.[T][ ] homework | 8.[T][ ] other homework | 9.[T][ ] project 12 @@ -278,7 +278,7 @@ '---------------------------------------------------------------- ,---------------------------------------------------------------- | That's going to happen at some time later: -| [E][ ] wow much space (at: 10am) +| [E][ ] wow much space (at: 10am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here, your tasks: @@ -287,7 +287,7 @@ | 3.[D][X] /at mysterious (by: [unknown]) | 4.[D][X] /by mysterious number 2 (by: [unknown]) | 5.[D][ ] rush this please (by: when?) -| 6.[E][ ] wow much space (at: 10am) +| 6.[E][ ] wow much space (at: 10am) | 7.[T][ ] homework | 8.[T][ ] other homework | 9.[T][ ] project 12 @@ -297,7 +297,7 @@ | 13.[D][ ] /at mysterious (by: [unknown]) | 14.[D][ ] /by mysterious number 2 (by: [unknown]) | 15.[D][ ] rush this please (by: when?) -| 16.[E][ ] wow much space (at: 10am) +| 16.[E][ ] wow much space (at: 10am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | (>.<') Add a description to your find. @@ -307,20 +307,9 @@ '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here are the tasks that you might be looking for: -| 1.[E][X] meet people at function x/y/z whatever (at: 7:12am) -| 2.[E][ ] meet friends (at: [unknown]) -| 3.[E][ ] meet people at dinner (at: 6:25 pm) -| 4.[E][ ] meet people at function x/y/z whatever (at: 7:12am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | Here are the tasks that you might be looking for: -| 1.[T][ ] homework -| 2.[E][X] meet people at function x/y/z whatever (at: 7:12am) -| 3.[T][ ] homework -| 4.[T][ ] other homework -| 5.[E][ ] meet friends (at: [unknown]) -| 6.[E][ ] meet people at dinner (at: 6:25 pm) -| 7.[E][ ] meet people at function x/y/z whatever (at: 7:12am) '---------------------------------------------------------------- ,---------------------------------------------------------------- | It was nice to have you around, I'm going back to sleep... diff --git a/text-ui-test/duke.txt b/text-ui-test/duke.txt index 78ee9ae569..8c67ce25ab 100644 --- a/text-ui-test/duke.txt +++ b/text-ui-test/duke.txt @@ -3,7 +3,7 @@ /v8ARABlAGEAZABsAGkAbgBl,/v8ALwBhAHQAIABtAHkAcwB0AGUAcgBpAG8AdQBz,/v8AdAByAHUAZQ==,/v8AWwB1AG4AawBuAG8AdwBuAF0= /v8ARABlAGEAZABsAGkAbgBl,/v8ALwBiAHkAIABtAHkAcwB0AGUAcgBpAG8AdQBzACAAbgB1AG0AYgBlAHIAIAAy,/v8AdAByAHUAZQ==,/v8AWwB1AG4AawBuAG8AdwBuAF0= /v8ARABlAGEAZABsAGkAbgBl,/v8AcgB1AHMAaAAgAHQAaABpAHMAIABwAGwAZQBhAHMAZQ==,/v8AZgBhAGwAcwBl,/v8AdwBoAGUAbgA/ -/v8ARQB2AGUAbgB0,/v8AdwBvAHcAIAAgAG0AdQBjAGgAIAAgAHMAcABhAGMAZQ==,/v8AZgBhAGwAcwBl,/v8AMQAwAGEAbQ== +/v8ARQB2AGUAbgB0,/v8AIAB3AG8AdwAgACAAbQB1AGMAaAAgACAAcwBwAGEAYwBl,/v8AZgBhAGwAcwBl,/v8AMQAwAGEAbQ== /v8AVABvAEQAbw==,/v8AaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl /v8AVABvAEQAbw==,/v8AbwB0AGgAZQByACAAaABvAG0AZQB3AG8AcgBr,/v8AZgBhAGwAcwBl /v8AVABvAEQAbw==,/v8AcAByAG8AagBlAGMAdAAgADEAMg==,/v8AZgBhAGwAcwBl @@ -13,4 +13,4 @@ /v8ARABlAGEAZABsAGkAbgBl,/v8ALwBhAHQAIABtAHkAcwB0AGUAcgBpAG8AdQBz,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= /v8ARABlAGEAZABsAGkAbgBl,/v8ALwBiAHkAIABtAHkAcwB0AGUAcgBpAG8AdQBzACAAbgB1AG0AYgBlAHIAIAAy,/v8AZgBhAGwAcwBl,/v8AWwB1AG4AawBuAG8AdwBuAF0= /v8ARABlAGEAZABsAGkAbgBl,/v8AcgB1AHMAaAAgAHQAaABpAHMAIABwAGwAZQBhAHMAZQ==,/v8AZgBhAGwAcwBl,/v8AdwBoAGUAbgA/ -/v8ARQB2AGUAbgB0,/v8AdwBvAHcAIAAgAG0AdQBjAGgAIAAgAHMAcABhAGMAZQ==,/v8AZgBhAGwAcwBl,/v8AMQAwAGEAbQ== \ No newline at end of file +/v8ARQB2AGUAbgB0,/v8AIAB3AG8AdwAgACAAbQB1AGMAaAAgACAAcwBwAGEAYwBl,/v8AZgBhAGwAcwBl,/v8AMQAwAGEAbQ== \ No newline at end of file diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 8f94108629..e7eeab5370 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -18,7 +18,7 @@ REM delete data if exist duke.txt del duke.txt REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ..\bin duke.Duke < input.txt > ACTUAL.TXT +java -classpath ..\bin duke.Duke console-test < input.txt > ACTUAL.TXT REM compare the output to the expected output FC ACTUAL.TXT EXPECTED.TXT @@ -27,7 +27,7 @@ REM delete output from previous run if exist ACTUAL1.TXT del ACTUAL1.TXT REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL1.TXT -java -classpath ..\bin duke.Duke < input.txt > ACTUAL1.TXT +java -classpath ..\bin duke.Duke console-test < input.txt > ACTUAL1.TXT REM compare the output to the expected output FC ACTUAL1.TXT EXPECTED1.TXT From fee826fdf1ab8b9c867c23f847fe924da0dd7482 Mon Sep 17 00:00:00 2001 From: Clarence Chew <62918570+clarence-chew@users.noreply.github.com> Date: Sat, 3 Sep 2022 16:32:40 +0800 Subject: [PATCH 30/67] Add GUI, still unsuccessful --- build.gradle | 5 +- .../java/duke/{Ui.java => ConsoleUi.java} | 12 ++- src/main/java/duke/Duke.java | 55 +++++----- src/main/java/duke/GuiHandler.java | 32 ------ src/main/java/duke/Parser.java | 24 ++--- src/main/java/duke/PrefixCommandMatcher.java | 4 +- src/main/java/duke/Storage.java | 2 +- src/main/java/duke/UiInterface.java | 15 ++- src/main/java/duke/gui/DialogBox.java | 61 +++++++++++ src/main/java/duke/gui/GraphicUi.java | 80 +++++++++++++++ src/main/java/duke/gui/Launcher.java | 12 +++ src/main/java/duke/gui/Main.java | 41 ++++++++ src/main/java/duke/gui/MainWindow.java | 97 ++++++++++++++++++ src/main/java/duke/task/TaskFactory.java | 3 +- src/main/java/duke/task/TaskList.java | 5 +- src/main/resources/images/dukeImage.png | Bin 0 -> 503 bytes src/main/resources/images/userImage.png | Bin 0 -> 411 bytes src/main/resources/view/DialogBox.fxml | 16 +++ src/main/resources/view/MainWindow.fxml | 19 ++++ 19 files changed, 395 insertions(+), 88 deletions(-) rename src/main/java/duke/{Ui.java => ConsoleUi.java} (76%) delete mode 100644 src/main/java/duke/GuiHandler.java create mode 100644 src/main/java/duke/gui/DialogBox.java create mode 100644 src/main/java/duke/gui/GraphicUi.java create mode 100644 src/main/java/duke/gui/Launcher.java create mode 100644 src/main/java/duke/gui/Main.java create mode 100644 src/main/java/duke/gui/MainWindow.java create mode 100644 src/main/resources/images/dukeImage.png create mode 100644 src/main/resources/images/userImage.png create mode 100644 src/main/resources/view/DialogBox.fxml create mode 100644 src/main/resources/view/MainWindow.fxml diff --git a/build.gradle b/build.gradle index 9210096bab..b11c3ab4db 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ test { } application { - mainClassName = "duke.Duke" + mainClassName = "duke.gui.Launcher" } shadowJar { @@ -56,6 +56,7 @@ shadowJar { archiveClassifier = null } -run{ +run { standardInput = System.in + enableAssertions = true } \ No newline at end of file diff --git a/src/main/java/duke/Ui.java b/src/main/java/duke/ConsoleUi.java similarity index 76% rename from src/main/java/duke/Ui.java rename to src/main/java/duke/ConsoleUi.java index 259851f862..fd66fb1b5b 100644 --- a/src/main/java/duke/Ui.java +++ b/src/main/java/duke/ConsoleUi.java @@ -1,10 +1,20 @@ package duke; +import java.io.InputStreamReader; +import java.io.Reader; /** * Handles console-based user interface. Used for sanity checks. */ -public class Ui implements UiInterface { +public class ConsoleUi implements UiInterface { + /** + * {@inheritDoc} + */ + @Override + public Reader getReader() { + return new InputStreamReader(System.in); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java index 7ee68a1009..a95801c617 100644 --- a/src/main/java/duke/Duke.java +++ b/src/main/java/duke/Duke.java @@ -1,11 +1,12 @@ package duke; -import duke.task.Task; -import duke.task.TaskList; - +import java.io.BufferedReader; +import java.io.IOException; import java.util.ArrayList; import java.util.Optional; -import java.util.Scanner; + +import duke.task.Task; +import duke.task.TaskList; /** * The main method of the chatbot, as well as its startup and teardown. @@ -13,29 +14,24 @@ public class Duke { /** List of commands */ private static ArrayList commands; - private static UiInterface ui; + private static UiInterface ui = new ConsoleUi(); /** - * Gets the current UiInterface for command line purposes. + * Sets the current UI. * - * @return UiInterface that helps display text to screen. + * @param ui The current UI to use. */ - public static UiInterface getCurrentUi() { - return ui; + public static void setUi(UiInterface ui) { + Duke.ui = ui; } - private static Optional getTask(String index) { - try { - int idx = Integer.parseInt(index); - Task task = TaskList.getTaskList().get(idx - 1); - return Optional.of(task); - } catch (NumberFormatException ex) { - ui.printStyledMessage("Sorry, I didn't understand " + index + ", please give me a number."); - return Optional.empty(); - } catch (IndexOutOfBoundsException ex) { - ui.printStyledMessage("Sorry, the number " + index + ", wasn't in the range."); - return Optional.empty(); - } + /** + * Gets the current UI to interact with. + * + * @return UiInterface that helps display text to screen. + */ + public static UiInterface getUi() { + return ui; } private static void handleCommand(String command) { @@ -52,23 +48,22 @@ private static void handleCommand(String command) { * @param args Command line args which are not used. */ public static void main(String[] args) { - // allow for console tests to run - if (args.length >= 1 && args[0].equals("console-test")) { - ui = new Ui(); - } else { - ui = new GuiHandler(); - } - // initialization ui.greet(); TaskList.initializeTaskList(); commands = Parser.getCommands(); - Scanner input = new Scanner(System.in); + BufferedReader input = new BufferedReader(ui.getReader()); // main application logic boolean isStillRunning = true; while (isStillRunning) { - String command = input.nextLine(); + String command; + try { + command = input.readLine(); + } catch (IOException ex) { + System.out.println("IOException in application logic."); + throw new RuntimeException(ex); + } if (command.equals("bye")) { isStillRunning = false; } else { diff --git a/src/main/java/duke/GuiHandler.java b/src/main/java/duke/GuiHandler.java deleted file mode 100644 index edb652391c..0000000000 --- a/src/main/java/duke/GuiHandler.java +++ /dev/null @@ -1,32 +0,0 @@ -package duke; - -/** - * Used for GUI. - */ -public class GuiHandler implements UiInterface { - /** - * Prints lines, when actually implemented. - * - * @param lines Line to be printed - */ - @Override - public void printStyledMessage(String... lines) { - // stub - } - - /** - * Greets user, when actually implemented. - */ - @Override - public void greet() { - // stub - } - - /** - * Leaves the user, when actually implemented. - */ - @Override - public void leave() { - // stub - } -} diff --git a/src/main/java/duke/Parser.java b/src/main/java/duke/Parser.java index b176624af1..9bfa71fa0b 100644 --- a/src/main/java/duke/Parser.java +++ b/src/main/java/duke/Parser.java @@ -1,14 +1,14 @@ package duke; +import java.util.ArrayList; +import java.util.List; + import duke.task.Deadline; import duke.task.Event; import duke.task.Task; import duke.task.TaskList; import duke.task.ToDo; -import java.util.ArrayList; -import java.util.List; - /** * Handles creating commands through duke.CommandMatcher/duke.PrefixCommandMatcher. */ @@ -33,14 +33,14 @@ public static ArrayList getCommands() { commands.add(new CommandMatcher((str) -> str.equals("list"), (str) -> { List tasks = TaskList.getTaskList(); - Duke.getCurrentUi().printStyledMessage( + Duke.getUi().printStyledMessage( listTasks("Here, your tasks:", tasks)); })); commands.add(new PrefixCommandMatcher("mark", (str, map) -> { TaskList.getTask(str).ifPresent((task) -> { task.markAsDone(); - Duke.getCurrentUi().printStyledMessage( + Duke.getUi().printStyledMessage( "Marked your task as done:", task.toString()); }); @@ -49,7 +49,7 @@ public static ArrayList getCommands() { commands.add(new PrefixCommandMatcher("unmark", (str, map) -> { TaskList.getTask(str).ifPresent((task) -> { task.markAsNotDone(); - Duke.getCurrentUi().printStyledMessage( + Duke.getUi().printStyledMessage( "Aw... it's not done yet:", task.toString()); }); @@ -58,7 +58,7 @@ public static ArrayList getCommands() { commands.add(new PrefixCommandMatcher("deadline", (str, map) -> { Task task = new Deadline(str, map.getOrDefault("by", "[unknown]")); TaskList.getTaskList().add(task); - Duke.getCurrentUi().printStyledMessage( + Duke.getUi().printStyledMessage( "Good luck with the deadline, here's the task:", task.toString()); })); @@ -66,7 +66,7 @@ public static ArrayList getCommands() { commands.add(new PrefixCommandMatcher("todo", (str, map) -> { Task task = new ToDo(str); TaskList.getTaskList().add(task); - Duke.getCurrentUi().printStyledMessage( + Duke.getUi().printStyledMessage( "I've recorded this thing you need to do:", task.toString()); })); @@ -74,7 +74,7 @@ public static ArrayList getCommands() { commands.add(new PrefixCommandMatcher("event", (str, map) -> { Task task = new Event(str, map.getOrDefault("at", "[unknown]")); TaskList.getTaskList().add(task); - Duke.getCurrentUi().printStyledMessage( + Duke.getUi().printStyledMessage( "That's going to happen at some time later:", task.toString()); })); @@ -82,7 +82,7 @@ public static ArrayList getCommands() { commands.add(new PrefixCommandMatcher("delete", (str, map) -> { TaskList.getTask(str).ifPresent((task) -> { TaskList.getTaskList().remove(task); - Duke.getCurrentUi().printStyledMessage( + Duke.getUi().printStyledMessage( "It seems you didn't need this task anymore, so I removed it:", task.toString(), String.format("You have %d tasks left.", TaskList.getTaskList().size())); @@ -91,13 +91,13 @@ public static ArrayList getCommands() { commands.add(new PrefixCommandMatcher("find", (str, map) -> { List tasks = TaskList.filterTasks(str); - Duke.getCurrentUi().printStyledMessage( + Duke.getUi().printStyledMessage( listTasks("Here are the tasks that you might be looking for:", tasks)); })); // default command matcher - add to list commands.add(new CommandMatcher((str) -> true, (str) -> { - Duke.getCurrentUi().printStyledMessage( + Duke.getUi().printStyledMessage( "(>.<') I'm sorry, I don't really know what that means."); })); diff --git a/src/main/java/duke/PrefixCommandMatcher.java b/src/main/java/duke/PrefixCommandMatcher.java index 9c3fc0c506..705c0ba6e8 100644 --- a/src/main/java/duke/PrefixCommandMatcher.java +++ b/src/main/java/duke/PrefixCommandMatcher.java @@ -21,7 +21,7 @@ public PrefixCommandMatcher(String prefix, BiConsumer.<') Add a description to your " + prefix + "."); return; } @@ -41,7 +41,7 @@ public PrefixCommandMatcher(String prefix, BiConsumer.<') The description for " + prefix + " shouldn't be empty."); + Duke.getUi().printStyledMessage("(>.<') The description for " + prefix + " shouldn't be empty."); return; } diff --git a/src/main/java/duke/Storage.java b/src/main/java/duke/Storage.java index 064f1e4266..0700db4f00 100644 --- a/src/main/java/duke/Storage.java +++ b/src/main/java/duke/Storage.java @@ -90,7 +90,7 @@ public void saveLines(String[][] strings) { } writer.close(); } catch (IOException ex) { - Duke.getCurrentUi().printStyledMessage( + Duke.getUi().printStyledMessage( "(>.<') I was unable to record your tasks..."); ex.printStackTrace(); } diff --git a/src/main/java/duke/UiInterface.java b/src/main/java/duke/UiInterface.java index 8ee0e305f8..376ea33230 100644 --- a/src/main/java/duke/UiInterface.java +++ b/src/main/java/duke/UiInterface.java @@ -1,23 +1,32 @@ package duke; +import java.io.Reader; + /** * Functions that a UI interface needs to implement. */ public interface UiInterface { + /** + * Gives you the input stream to scan to interact with this interface. + * + * @return InputStream to scan. + */ + Reader getReader(); + /** * Styles and prints lines with a border. * * @param lines Lines to be printed */ - public void printStyledMessage(String... lines); + void printStyledMessage(String... lines); /** * Greets user. */ - public void greet(); + void greet(); /** * Leaves the user. */ - public void leave(); + void leave(); } diff --git a/src/main/java/duke/gui/DialogBox.java b/src/main/java/duke/gui/DialogBox.java new file mode 100644 index 0000000000..414d52690e --- /dev/null +++ b/src/main/java/duke/gui/DialogBox.java @@ -0,0 +1,61 @@ +package duke.gui; + +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; + +/** + * 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 class DialogBox extends HBox { + @FXML + private Label dialog; + @FXML + private ImageView displayPicture; + + private 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); + } + + /** + * Flips the dialog box such that the ImageView is on the left and text on the right. + */ + 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/gui/GraphicUi.java b/src/main/java/duke/gui/GraphicUi.java new file mode 100644 index 0000000000..28e9740b2f --- /dev/null +++ b/src/main/java/duke/gui/GraphicUi.java @@ -0,0 +1,80 @@ +package duke.gui; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.PipedReader; +import java.io.PipedWriter; +import java.io.Reader; + +import duke.UiInterface; + +/** + * Used for GUI. + */ +public class GraphicUi implements UiInterface { + private static PipedWriter writer = new PipedWriter(); + private static final BufferedWriter bufferedWriter = new BufferedWriter(writer); + + /** + * Gets writer that writes out of application logic into GUI. + * + * @return Writer from application logic. + */ + public static PipedWriter getWriter() { + return writer; + } + + /** + * {@inheritDoc} + */ + @Override + public Reader getReader() { + try { + return new PipedReader(MainWindow.getWriter()); + } catch (IOException e) { + System.out.println("Error: Did not obtain GUI reader."); + throw new RuntimeException(e); + } + } + + /** + * Prints lines. + * + * @param lines Line to be printed + */ + @Override + public void printStyledMessage(String... lines) { + StringBuilder result = new StringBuilder(); + for (String line : lines) { + result.append(line); + result.append('\n'); + } + result.append('\n'); + try { + bufferedWriter.write(result.toString(), 0, result.length()); + bufferedWriter.flush(); + } catch (IOException e) { + System.out.println("Error: Cannot write output."); + throw new RuntimeException(e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void greet() { + printStyledMessage("...where is this again?", + "Oh, hello, I didn't see you there - I'm Anthea, a chatbot...", + "...or at least that's what they told me."); + } + + /** + * {@inheritDoc} + */ + @Override + public void leave() { + printStyledMessage("It was nice to have you around, I'm going back to sleep...", + "(close the window yourself)"); + } +} diff --git a/src/main/java/duke/gui/Launcher.java b/src/main/java/duke/gui/Launcher.java new file mode 100644 index 0000000000..379b564819 --- /dev/null +++ b/src/main/java/duke/gui/Launcher.java @@ -0,0 +1,12 @@ +package duke.gui; + +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/gui/Main.java b/src/main/java/duke/gui/Main.java new file mode 100644 index 0000000000..aaf83d7a11 --- /dev/null +++ b/src/main/java/duke/gui/Main.java @@ -0,0 +1,41 @@ +package duke.gui; + +import java.io.IOException; + +import duke.Duke; +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +/** + * A GUI for Duke using FXML. + */ +public class Main extends Application { + /** + * Start the application and Duke logic. + * + * @param stage JavaFX stage. + */ + @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); + // Start application logic in thread + Thread appLogic = new Thread("appLogic") { + @Override + public void run() { + Duke.main(new String[]{}); + } + }; + appLogic.start(); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/duke/gui/MainWindow.java b/src/main/java/duke/gui/MainWindow.java new file mode 100644 index 0000000000..a3f3344ebf --- /dev/null +++ b/src/main/java/duke/gui/MainWindow.java @@ -0,0 +1,97 @@ +package duke.gui; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PipedReader; +import java.io.PipedWriter; + +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; + +/** + * Controller for MainWindow. Provides the layout for the other controls. + */ +public class MainWindow extends AnchorPane { + /** + * GUI writes to here. + */ + private static final PipedWriter writer = new PipedWriter(); + /** + * GUI reads from here. + */ + private static BufferedReader reader; + + @FXML + private ScrollPane scrollPane; + @FXML + private VBox dialogContainer; + @FXML + private TextField userInput; + @FXML + private Button sendButton; + + private final Image userImage = new Image( + this.getClass().getResourceAsStream("/images/userImage.png"), + 96, 96, false, true); + private final Image dukeImage = new Image( + this.getClass().getResourceAsStream("/images/dukeImage.png"), + 96, 96, false, true); + + /** + * Get the writer that writes out GUI interactions. + * + * @return The writer that writes out GUI interactions. + */ + public static PipedWriter getWriter() { + return writer; + } + + @FXML + private void initialize() { + try { + reader = new BufferedReader(new PipedReader(GraphicUi.getWriter())); + } catch (IOException e) { + System.out.println("Cannot initialize GUI output reader."); + throw new RuntimeException(e); + } + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + } + + /** + * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + @FXML + private void handleUserInput() { + String input = userInput.getText(); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage) + ); + userInput.clear(); + try { + writer.write(input); + StringBuilder result = new StringBuilder(); + while (true) { + String line = reader.readLine(); + if (line.length() == 0) { + break; + } + result.append(reader.readLine()); + result.append('\n'); + } + String response = result.substring(0, result.length() - 1); + dialogContainer.getChildren().addAll( + DialogBox.getDukeDialog(response, dukeImage) + ); + } catch (IOException ex) { + dialogContainer.getChildren().addAll( + DialogBox.getDukeDialog(String.format("I did not manage to read that:\n%s", input), dukeImage) + ); + } + } +} diff --git a/src/main/java/duke/task/TaskFactory.java b/src/main/java/duke/task/TaskFactory.java index d6bb4c0d2e..26dd7a31c3 100644 --- a/src/main/java/duke/task/TaskFactory.java +++ b/src/main/java/duke/task/TaskFactory.java @@ -3,7 +3,6 @@ import java.util.Optional; import duke.Duke; -import duke.Ui; /** * Constructs tasks from strings @@ -45,7 +44,7 @@ public static Optional constructOptionalTask(String[] taskData) { try { return Optional.of(constructTask(taskData)); } catch (IllegalArgumentException ex) { - Duke.getCurrentUi().printStyledMessage( + Duke.getUi().printStyledMessage( "(>.<') did not understand this task - dropping it", String.join(", ", taskData)); return Optional.empty(); diff --git a/src/main/java/duke/task/TaskList.java b/src/main/java/duke/task/TaskList.java index 73eee897f2..906dce4718 100644 --- a/src/main/java/duke/task/TaskList.java +++ b/src/main/java/duke/task/TaskList.java @@ -6,7 +6,6 @@ import java.util.stream.Collectors; import duke.Duke; -import duke.Ui; /** * Holds the list of tasks @@ -41,11 +40,11 @@ public static Optional getTask(String index) { Task task = taskList.get(idx - 1); return Optional.of(task); } catch (NumberFormatException ex) { - Duke.getCurrentUi().printStyledMessage( + Duke.getUi().printStyledMessage( "Sorry, I didn't understand " + index + ", please give me a number."); return Optional.empty(); } catch (IndexOutOfBoundsException ex) { - Duke.getCurrentUi().printStyledMessage( + Duke.getUi().printStyledMessage( "Sorry, the number " + index + ", wasn't in the range."); return Optional.empty(); } diff --git a/src/main/resources/images/dukeImage.png b/src/main/resources/images/dukeImage.png new file mode 100644 index 0000000000000000000000000000000000000000..8b1e1079ada4c64a7fc1dba5509ef0adea53d671 GIT binary patch literal 503 zcmVPx#1ZP1_K>z@;j|==^1poj532;bRa{vGmbN~PnbOGLGA9w%&0gOpRK~z{r#g>hZ zgD?z(VekLWck?zQW1O_Wq3t7h=~d51Uly1G9+0+7V`Ug;Xb_H$+T6)=!C3u!_11K%rN zC&eDV0tBh838Ouz@>48`Ujc$yhu(|oa9EIN zVHZJrmZ1QhETLz#Rj>+6l>d-;<5VOyB%WKenG?_zsF=-DfToT~dI=W`wYKm(GREhL)`IJ>& zV)_QW7#eUXNs#ZAX#ptGO@uQxXWS?U>;$kt1q-HyGv-!}lfS(ICa^HUUe_I_9s#FJ tVgP&^gGG2&(F#B!r%a8VGqG=$Px#1ZP1_K>z@;j|==^1poj532;bRa{vGmbN~PnbOGLGA9w%&0We8KK~z{r#gst~ z!ypUUe)S9_00#w=l-|57-vKq;}X0LTn2#yXmc0 z0Gx)gJ%E=RkDP#KdjRc)N3OtA{v*muX5S>724=_NB-xWjJ1B_}2h`vQkdw_XXWG1MJAq-$e z{IG&iVgF5^tE4?(Xh97E8dBdQmH{HHp#EBwc33u{pCJ?I`ixTm+l;_w2Jp-}Wd6C! z?JH3D)(vwC5N+0XFR#e5HQ@=^28I`AAgTm|O8|s6M{+IfWgRiJdccZDp(3hb!T`G2 zo-;Y?yQeTc0SaZ}73^DuT3&%|tjfJ)B~)5H(4iLmI6hcC5kz3tfu{ff002ovPDHLk FV1n@ + + + + + + + + + + + + + \ 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..de92974e28 --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,19 @@ + + + + + + + + + + + +