From 50d1085fcee239f3ac6e897f95d4d77b4c7e666e Mon Sep 17 00:00:00 2001 From: 9y5 Date: Tue, 17 Aug 2021 21:14:11 +0800 Subject: [PATCH 01/32] Basic bazel setup. --- .gitignore | 4 ++++ BUILD | 6 ++++++ WORKSPACE | 0 src/main/java/{ => com/lockarhythm/cmdline}/Duke.java | 2 ++ 4 files changed, 12 insertions(+) create mode 100644 BUILD create mode 100644 WORKSPACE rename src/main/java/{ => com/lockarhythm/cmdline}/Duke.java (91%) diff --git a/.gitignore b/.gitignore index f69985ef..8256048a 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,7 @@ bin/ /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT + +# Ignore all bazel-* symlinks. There is no full list since this can change +# based on the name of the directory bazel is cloned into. +/bazel-* diff --git a/BUILD b/BUILD new file mode 100644 index 00000000..39bca6aa --- /dev/null +++ b/BUILD @@ -0,0 +1,6 @@ +load("@rules_java//java:defs.bzl", "java_binary") + +java_binary( + name = "Duke", + srcs = glob(["src/main/java/com/lockarhythm/cmdline/*.java"]), +) diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 00000000..e69de29b diff --git a/src/main/java/Duke.java b/src/main/java/com/lockarhythm/cmdline/Duke.java similarity index 91% rename from src/main/java/Duke.java rename to src/main/java/com/lockarhythm/cmdline/Duke.java index 5d313334..b6a76693 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/com/lockarhythm/cmdline/Duke.java @@ -1,3 +1,5 @@ +package com.lockarhythm.cmdline; + public class Duke { public static void main(String[] args) { String logo = " ____ _ \n" From b5eb9ac02d5e0210821512169b18691f6bc859ea Mon Sep 17 00:00:00 2001 From: 9y5 Date: Tue, 17 Aug 2021 21:17:39 +0800 Subject: [PATCH 02/32] Update documentation. --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 8715d4d9..76435deb 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ 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. +## Local Build +Prerequisites: Have `bazel` installed. +```bash +# Following tested on OSX. +bazel build //:Duke +./bazel-bin/Duke +``` + ## Setting up in Intellij Prerequisites: JDK 11, update Intellij to the most recent version. From 7aa0bffeea7ef399bdd2b79a2126077edf4c527e Mon Sep 17 00:00:00 2001 From: 9y5 Date: Tue, 17 Aug 2021 22:36:13 +0800 Subject: [PATCH 03/32] Add unit test, test runner and refactor. --- BUILD | 11 ++++++++++ README.md | 6 ++++++ .../java/com/lockarhythm/cmdline/Duke.java | 16 ++++++++------ .../com/lockarhythm/cmdline/TestDuke.java | 21 +++++++++++++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 src/test/java/com/lockarhythm/cmdline/TestDuke.java diff --git a/BUILD b/BUILD index 39bca6aa..5f4fa483 100644 --- a/BUILD +++ b/BUILD @@ -1,6 +1,17 @@ load("@rules_java//java:defs.bzl", "java_binary") +load("@rules_java//java:defs.bzl", "java_test") java_binary( name = "Duke", srcs = glob(["src/main/java/com/lockarhythm/cmdline/*.java"]), ) + +java_test( + name = "AllTests", + size = "small", + test_class = "com.lockarhythm.cmdline.TestDuke", + srcs = glob([ + "src/main/java/com/lockarhythm/cmdline/*.java", + "src/test/java/com/lockarhythm/cmdline/*.java", + ]), +) diff --git a/README.md b/README.md index 76435deb..e511e631 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,12 @@ bazel build //:Duke ./bazel-bin/Duke ``` +## Testing + +```bash +bazel test --test_output=all //... +``` + ## Setting up in Intellij Prerequisites: JDK 11, update Intellij to the most recent version. diff --git a/src/main/java/com/lockarhythm/cmdline/Duke.java b/src/main/java/com/lockarhythm/cmdline/Duke.java index b6a76693..f6bed387 100644 --- a/src/main/java/com/lockarhythm/cmdline/Duke.java +++ b/src/main/java/com/lockarhythm/cmdline/Duke.java @@ -1,12 +1,16 @@ package com.lockarhythm.cmdline; +import java.io.PrintStream; + public class Duke { + static PrintStream out = System.out; + static String logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); + out.println("Hello from\n" + logo); } } diff --git a/src/test/java/com/lockarhythm/cmdline/TestDuke.java b/src/test/java/com/lockarhythm/cmdline/TestDuke.java new file mode 100644 index 00000000..5665dd5b --- /dev/null +++ b/src/test/java/com/lockarhythm/cmdline/TestDuke.java @@ -0,0 +1,21 @@ +package com.lockarhythm.cmdline; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; + +public class TestDuke { + + @Test + public void testNoArgument() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Duke.out = new PrintStream(out); + Duke.main(null); + assertEquals(("Hello from\n"+Duke.logo).trim(), new String(out.toByteArray(), StandardCharsets.UTF_8).trim()); + } + +} From f659ebbf42051134f2e407896a09c35db482765c Mon Sep 17 00:00:00 2001 From: 9y5 Date: Tue, 17 Aug 2021 23:58:19 +0800 Subject: [PATCH 04/32] Cleaner test. --- src/main/java/com/lockarhythm/cmdline/Duke.java | 5 +---- src/test/java/com/lockarhythm/cmdline/TestDuke.java | 9 +++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/lockarhythm/cmdline/Duke.java b/src/main/java/com/lockarhythm/cmdline/Duke.java index f6bed387..06051852 100644 --- a/src/main/java/com/lockarhythm/cmdline/Duke.java +++ b/src/main/java/com/lockarhythm/cmdline/Duke.java @@ -1,9 +1,6 @@ package com.lockarhythm.cmdline; -import java.io.PrintStream; - public class Duke { - static PrintStream out = System.out; static String logo = " ____ _ \n" + "| _ \\ _ _| | _____ \n" + "| | | | | | | |/ / _ \\\n" @@ -11,6 +8,6 @@ public class Duke { + "|____/ \\__,_|_|\\_\\___|\n"; public static void main(String[] args) { - out.println("Hello from\n" + logo); + System.out.println("Hello from\n" + logo); } } diff --git a/src/test/java/com/lockarhythm/cmdline/TestDuke.java b/src/test/java/com/lockarhythm/cmdline/TestDuke.java index 5665dd5b..2a0d9a5b 100644 --- a/src/test/java/com/lockarhythm/cmdline/TestDuke.java +++ b/src/test/java/com/lockarhythm/cmdline/TestDuke.java @@ -6,16 +6,17 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; -import java.nio.charset.StandardCharsets; public class TestDuke { + private final PrintStream standardOut = System.out; + private final ByteArrayOutputStream out = new ByteArrayOutputStream(); @Test public void testNoArgument() throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - Duke.out = new PrintStream(out); + System.setOut(new PrintStream(out)); Duke.main(null); - assertEquals(("Hello from\n"+Duke.logo).trim(), new String(out.toByteArray(), StandardCharsets.UTF_8).trim()); + assertEquals(("Hello from\n"+Duke.logo).trim(), out.toString().trim()); + System.setOut(standardOut); } } From 1e8da3d60bb220c08dd05462e086de4576e0b40d Mon Sep 17 00:00:00 2001 From: 9y5 Date: Fri, 20 Aug 2021 21:18:03 +0800 Subject: [PATCH 05/32] Split into packages. Implement Level 1. --- BUILD | 30 ++++++++-- WORKSPACE | 25 ++++++++ .../java/com/lockarhythm/cmdline/Duke.java | 57 +++++++++++++++++-- .../responders/QueryRespondable.java | 5 ++ .../com/lockarhythm/responders/Response.java | 19 +++++++ .../responders/echo/EchoResponder.java | 10 ++++ .../responders/exit/ExitResponder.java | 13 +++++ .../com/lockarhythm/cmdline/TestDuke.java | 23 +++++++- .../responders/echo/TestEchoResponder.java | 18 ++++++ .../responders/exit/TestExitResponder.java | 25 ++++++++ 10 files changed, 211 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/lockarhythm/responders/QueryRespondable.java create mode 100644 src/main/java/com/lockarhythm/responders/Response.java create mode 100644 src/main/java/com/lockarhythm/responders/echo/EchoResponder.java create mode 100644 src/main/java/com/lockarhythm/responders/exit/ExitResponder.java create mode 100644 src/test/java/com/lockarhythm/responders/echo/TestEchoResponder.java create mode 100644 src/test/java/com/lockarhythm/responders/exit/TestExitResponder.java diff --git a/BUILD b/BUILD index 5f4fa483..dd7185dc 100644 --- a/BUILD +++ b/BUILD @@ -3,15 +3,35 @@ load("@rules_java//java:defs.bzl", "java_test") java_binary( name = "Duke", - srcs = glob(["src/main/java/com/lockarhythm/cmdline/*.java"]), + srcs = glob([ + "src/main/java/com/lockarhythm/**/*.java", + ]), + deps = ["@maven//:org_apache_commons_commons_lang3"] +) + +java_test( + name = "TestDuke", + size = "small", + srcs = glob([ + "src/test/java/com/lockarhythm/**/*.java", + "src/main/java/com/lockarhythm/**/*.java", + ]), +) + +java_test( + name = "TestEchoResponder", + size = "small", + srcs = glob([ + "src/test/java/com/lockarhythm/**/*.java", + "src/main/java/com/lockarhythm/**/*.java", + ]), ) java_test( - name = "AllTests", + name = "TestExitResponder", size = "small", - test_class = "com.lockarhythm.cmdline.TestDuke", srcs = glob([ - "src/main/java/com/lockarhythm/cmdline/*.java", - "src/test/java/com/lockarhythm/cmdline/*.java", + "src/test/java/com/lockarhythm/**/*.java", + "src/main/java/com/lockarhythm/**/*.java", ]), ) diff --git a/WORKSPACE b/WORKSPACE index e69de29b..9784f207 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -0,0 +1,25 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +RULES_JVM_EXTERNAL_TAG = "4.0" +RULES_JVM_EXTERNAL_SHA = "31701ad93dbfe544d597dbe62c9a1fdd76d81d8a9150c2bf1ecf928ecdf97169" + +http_archive( + name = "rules_jvm_external", + strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG, + sha256 = RULES_JVM_EXTERNAL_SHA, + url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG, +) + +load("@rules_jvm_external//:defs.bzl", "maven_install") + +maven_install( + artifacts = [ + #"com.google.guava:guava:21.0", + "org.apache.commons:commons-lang3:3.11" + ], + repositories = [ + # Private repositories are supported through HTTP Basic auth + "https://maven.google.com", + "https://repo1.maven.org/maven2", + ], +) diff --git a/src/main/java/com/lockarhythm/cmdline/Duke.java b/src/main/java/com/lockarhythm/cmdline/Duke.java index 06051852..5b18a54e 100644 --- a/src/main/java/com/lockarhythm/cmdline/Duke.java +++ b/src/main/java/com/lockarhythm/cmdline/Duke.java @@ -1,13 +1,58 @@ package com.lockarhythm.cmdline; +import java.util.Scanner; +import java.util.Arrays; + +import com.lockarhythm.responders.QueryRespondable; +import com.lockarhythm.responders.Response; +import com.lockarhythm.responders.echo.EchoResponder; +import com.lockarhythm.responders.exit.ExitResponder; + public class Duke { - static String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; + static String logo = " \t____ _ \n" + + "\t| _ \\ _ _| | _____ \n" + + "\t| | | | | | | |/ / _ \\\n" + + "\t| |_| | |_| | < __/\n" + + "\t|____/ \\__,_|_|\\_\\___|\n"; + + static QueryRespondable[] responders = { + new ExitResponder(), + new EchoResponder(), + }; + + private static void print(String ...strings) { + System.out.println("\t____________________________________________________________"); + for (String s : strings) { + System.out.println("\t"+s); + } + System.out.println("\t____________________________________________________________\n"); + } + + private static Response getResponse(String query) { + Response res = null; + for (QueryRespondable responder : responders) { + res = responder.respondTo(query); + if (res != null) { + return res; + } + } + return res; + } public static void main(String[] args) { - System.out.println("Hello from\n" + logo); + String line; + Scanner in = new Scanner(System.in); + + print("Hello I'm\n" + logo, "What can I do for you?"); + + line = in.nextLine(); + Response res = getResponse(line); + while(!res.shouldExit()) { + print(line); + line = in.nextLine(); + res = getResponse(line); + } + + print(res.getText()); } } diff --git a/src/main/java/com/lockarhythm/responders/QueryRespondable.java b/src/main/java/com/lockarhythm/responders/QueryRespondable.java new file mode 100644 index 00000000..2501b113 --- /dev/null +++ b/src/main/java/com/lockarhythm/responders/QueryRespondable.java @@ -0,0 +1,5 @@ +package com.lockarhythm.responders; + +public interface QueryRespondable { + Response respondTo(String query); +} diff --git a/src/main/java/com/lockarhythm/responders/Response.java b/src/main/java/com/lockarhythm/responders/Response.java new file mode 100644 index 00000000..c9a7f9c2 --- /dev/null +++ b/src/main/java/com/lockarhythm/responders/Response.java @@ -0,0 +1,19 @@ +package com.lockarhythm.responders; + +public class Response { + private String text; + private boolean shouldExit; + + public Response(String text, boolean shouldExit) { + this.text = text; + this.shouldExit = shouldExit; + } + + public String getText() { + return text; + } + + public boolean shouldExit() { + return shouldExit; + } +} diff --git a/src/main/java/com/lockarhythm/responders/echo/EchoResponder.java b/src/main/java/com/lockarhythm/responders/echo/EchoResponder.java new file mode 100644 index 00000000..7547ba76 --- /dev/null +++ b/src/main/java/com/lockarhythm/responders/echo/EchoResponder.java @@ -0,0 +1,10 @@ +package com.lockarhythm.responders.echo; + +import com.lockarhythm.responders.Response; +import com.lockarhythm.responders.QueryRespondable; + +public class EchoResponder implements QueryRespondable { + public Response respondTo(String query) { + return new Response(query, false); + } +} diff --git a/src/main/java/com/lockarhythm/responders/exit/ExitResponder.java b/src/main/java/com/lockarhythm/responders/exit/ExitResponder.java new file mode 100644 index 00000000..34a1bb32 --- /dev/null +++ b/src/main/java/com/lockarhythm/responders/exit/ExitResponder.java @@ -0,0 +1,13 @@ +package com.lockarhythm.responders.exit; + +import com.lockarhythm.responders.Response; +import com.lockarhythm.responders.QueryRespondable; + +public class ExitResponder implements QueryRespondable { + public Response respondTo(String query) { + if (query.equals("bye")) { + return new Response("Bye. Hope to see you again soon!", true); + } + return null; + } +} diff --git a/src/test/java/com/lockarhythm/cmdline/TestDuke.java b/src/test/java/com/lockarhythm/cmdline/TestDuke.java index 2a0d9a5b..65c8d826 100644 --- a/src/test/java/com/lockarhythm/cmdline/TestDuke.java +++ b/src/test/java/com/lockarhythm/cmdline/TestDuke.java @@ -1,22 +1,39 @@ package com.lockarhythm.cmdline; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import org.junit.Test; import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; import java.io.PrintStream; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; public class TestDuke { private final PrintStream standardOut = System.out; + private final InputStream standardIn = System.in; private final ByteArrayOutputStream out = new ByteArrayOutputStream(); + private final ByteArrayInputStream in = new ByteArrayInputStream( + "list\nblah\nbye".getBytes() + ); @Test - public void testNoArgument() throws Exception { + public void testMeetsLevel1() throws Exception { + System.setIn(in); System.setOut(new PrintStream(out)); + Duke.main(null); - assertEquals(("Hello from\n"+Duke.logo).trim(), out.toString().trim()); + String output = out.toString().trim(); + assertFalse(output.isEmpty()); + + assertTrue("it greets the user", output.contains("Hello")); + assertTrue("it echos the command back", output.contains("blah")); + assertTrue("it exits", output.contains("Bye. Hope to see you again soon!")); + System.setOut(standardOut); + System.setIn(standardIn); } } diff --git a/src/test/java/com/lockarhythm/responders/echo/TestEchoResponder.java b/src/test/java/com/lockarhythm/responders/echo/TestEchoResponder.java new file mode 100644 index 00000000..e883f6f8 --- /dev/null +++ b/src/test/java/com/lockarhythm/responders/echo/TestEchoResponder.java @@ -0,0 +1,18 @@ +package com.lockarhythm.responders.echo; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import com.lockarhythm.responders.Response; + +public class TestEchoResponder { + @Test + public void testEchosBackText() throws Exception { + EchoResponder responder = new EchoResponder(); + Response res = responder.respondTo("hello"); + + assertEquals("hello", res.getText()); + assertFalse(res.shouldExit()); + } +} diff --git a/src/test/java/com/lockarhythm/responders/exit/TestExitResponder.java b/src/test/java/com/lockarhythm/responders/exit/TestExitResponder.java new file mode 100644 index 00000000..f2007459 --- /dev/null +++ b/src/test/java/com/lockarhythm/responders/exit/TestExitResponder.java @@ -0,0 +1,25 @@ +package com.lockarhythm.responders.exit; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import com.lockarhythm.responders.Response; + +public class TestExitResponder { + @Test + public void testExitsOnKeyword() throws Exception { + ExitResponder responder = new ExitResponder(); + Response res = responder.respondTo("bye"); + + assertTrue(res.shouldExit()); + } + + @Test + public void testNullOnNonkeyword() throws Exception { + ExitResponder responder = new ExitResponder(); + Response res = responder.respondTo("hello"); + + assertNull(res); + } +} From e15de968beb734f4ca2f125e4b718e80b6dbd4b1 Mon Sep 17 00:00:00 2001 From: 9y5 Date: Fri, 20 Aug 2021 22:30:53 +0800 Subject: [PATCH 06/32] Fix bug -- should return response text. --- src/main/java/com/lockarhythm/cmdline/Duke.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/lockarhythm/cmdline/Duke.java b/src/main/java/com/lockarhythm/cmdline/Duke.java index 5b18a54e..51dd5dee 100644 --- a/src/main/java/com/lockarhythm/cmdline/Duke.java +++ b/src/main/java/com/lockarhythm/cmdline/Duke.java @@ -48,7 +48,7 @@ public static void main(String[] args) { line = in.nextLine(); Response res = getResponse(line); while(!res.shouldExit()) { - print(line); + print(res.getText()); line = in.nextLine(); res = getResponse(line); } From 67cbff0657b9175a3f991b05f7fd8210d9468568 Mon Sep 17 00:00:00 2001 From: 9y5 Date: Fri, 20 Aug 2021 22:28:50 +0800 Subject: [PATCH 07/32] Implement level 2 -- Add, List. --- BUILD | 9 +++++ .../java/com/lockarhythm/cmdline/Duke.java | 7 ++-- .../responders/addlist/AddListResponder.java | 31 +++++++++++++++++ .../com/lockarhythm/cmdline/TestDuke.java | 10 +++--- .../addlist/TestAddListResponder.java | 33 +++++++++++++++++++ 5 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java create mode 100644 src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java diff --git a/BUILD b/BUILD index dd7185dc..ab7919a6 100644 --- a/BUILD +++ b/BUILD @@ -35,3 +35,12 @@ java_test( "src/main/java/com/lockarhythm/**/*.java", ]), ) + +java_test( + name = "TestAddListResponder", + size = "small", + srcs = glob([ + "src/test/java/com/lockarhythm/**/*.java", + "src/main/java/com/lockarhythm/**/*.java", + ]), +) diff --git a/src/main/java/com/lockarhythm/cmdline/Duke.java b/src/main/java/com/lockarhythm/cmdline/Duke.java index 51dd5dee..c3925a29 100644 --- a/src/main/java/com/lockarhythm/cmdline/Duke.java +++ b/src/main/java/com/lockarhythm/cmdline/Duke.java @@ -5,8 +5,8 @@ import com.lockarhythm.responders.QueryRespondable; import com.lockarhythm.responders.Response; -import com.lockarhythm.responders.echo.EchoResponder; import com.lockarhythm.responders.exit.ExitResponder; +import com.lockarhythm.responders.addlist.AddListResponder; public class Duke { static String logo = " \t____ _ \n" @@ -17,13 +17,14 @@ public class Duke { static QueryRespondable[] responders = { new ExitResponder(), - new EchoResponder(), + new AddListResponder(), }; private static void print(String ...strings) { System.out.println("\t____________________________________________________________"); for (String s : strings) { - System.out.println("\t"+s); + s = Arrays.stream(s.split("\n")).map(x -> "\t" + x).reduce("", (x, y) -> x + y + "\n"); + System.out.println(s); } System.out.println("\t____________________________________________________________\n"); } diff --git a/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java b/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java new file mode 100644 index 00000000..6bbd501c --- /dev/null +++ b/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java @@ -0,0 +1,31 @@ +package com.lockarhythm.responders.addlist; + +import com.lockarhythm.responders.Response; +import com.lockarhythm.responders.QueryRespondable; + +import java.util.ArrayList; + +public class AddListResponder implements QueryRespondable { + private ArrayList list; + + public AddListResponder() { + list = new ArrayList(); + } + + public Response respondTo(String query) { + if (query.equals("list")) { + StringBuilder s = new StringBuilder(); + int i = 1; + for (i = 0; i < list.size(); i++) { + s.append(i+1); + s.append(". "); + s.append(list.get(i)); + s.append("\n"); + } + return new Response(s.toString(), false); + } + // by default, adds the given query. + list.add(query); + return new Response("added: " + query, false); + } +} diff --git a/src/test/java/com/lockarhythm/cmdline/TestDuke.java b/src/test/java/com/lockarhythm/cmdline/TestDuke.java index 65c8d826..6a11ef76 100644 --- a/src/test/java/com/lockarhythm/cmdline/TestDuke.java +++ b/src/test/java/com/lockarhythm/cmdline/TestDuke.java @@ -16,11 +16,11 @@ public class TestDuke { private final InputStream standardIn = System.in; private final ByteArrayOutputStream out = new ByteArrayOutputStream(); private final ByteArrayInputStream in = new ByteArrayInputStream( - "list\nblah\nbye".getBytes() + "read book\nreturn book\nlist\nbye".getBytes() ); @Test - public void testMeetsLevel1() throws Exception { + public void testMeetsLevel2() throws Exception { System.setIn(in); System.setOut(new PrintStream(out)); @@ -28,8 +28,10 @@ public void testMeetsLevel1() throws Exception { String output = out.toString().trim(); assertFalse(output.isEmpty()); - assertTrue("it greets the user", output.contains("Hello")); - assertTrue("it echos the command back", output.contains("blah")); + assertTrue("it adds the 1st item", output.contains("added: read book")); + assertTrue("it adds the 2nd item", output.contains("added: return book")); + assertTrue("it lists the items", output.contains("1. read book")); + assertTrue("it lists the items", output.contains("2. return book")); assertTrue("it exits", output.contains("Bye. Hope to see you again soon!")); System.setOut(standardOut); diff --git a/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java b/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java new file mode 100644 index 00000000..c57f7195 --- /dev/null +++ b/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java @@ -0,0 +1,33 @@ +package com.lockarhythm.responders.addlist; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import com.lockarhythm.responders.Response; + +public class TestAddListResponder { + @Test + public void testAddsItemsAndLists() throws Exception { + AddListResponder responder = new AddListResponder(); + + Response res = responder.respondTo("read book"); + assertEquals("added: read book", res.getText()); + + res = responder.respondTo("return book"); + assertEquals("added: return book", res.getText()); + + res = responder.respondTo("list"); + assertEquals("1. read book\n2. return book\n", res.getText()); + + } + + @Test + public void testEmptyList() throws Exception { + AddListResponder responder = new AddListResponder(); + + Response res = responder.respondTo("list"); + assertEquals("", res.getText()); + + } +} From 7321f72ab8425cb1c43d4d7592e2c6b1e96d96b5 Mon Sep 17 00:00:00 2001 From: 9y5 Date: Fri, 20 Aug 2021 23:54:48 +0800 Subject: [PATCH 08/32] Refactor into separate method. --- .../responders/addlist/AddListResponder.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java b/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java index 6bbd501c..20ffe009 100644 --- a/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java +++ b/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java @@ -12,16 +12,20 @@ public AddListResponder() { list = new ArrayList(); } + private void printList(StringBuilder s) { + int i = 1; + for (i = 0; i < list.size(); i++) { + s.append(i+1); + s.append(". "); + s.append(list.get(i)); + s.append("\n"); + } + } + public Response respondTo(String query) { if (query.equals("list")) { StringBuilder s = new StringBuilder(); - int i = 1; - for (i = 0; i < list.size(); i++) { - s.append(i+1); - s.append(". "); - s.append(list.get(i)); - s.append("\n"); - } + printList(s); return new Response(s.toString(), false); } // by default, adds the given query. From 8545772e149f721dba869b6b38fb3657f90a7359 Mon Sep 17 00:00:00 2001 From: 9y5 Date: Sat, 21 Aug 2021 08:01:34 +0800 Subject: [PATCH 09/32] Implements Level-3: Mark as Done --- BUILD | 18 +++++++++ .../responders/addlist/AddListResponder.java | 37 +++++++++++------- src/main/java/com/lockarhythm/tasks/Task.java | 34 +++++++++++++++++ .../java/com/lockarhythm/tasks/TaskList.java | 38 +++++++++++++++++++ .../com/lockarhythm/cmdline/TestDuke.java | 4 +- .../addlist/TestAddListResponder.java | 36 ++++++++++++++++-- .../java/com/lockarhythm/tasks/TestTask.java | 15 ++++++++ .../com/lockarhythm/tasks/TestTaskList.java | 19 ++++++++++ 8 files changed, 182 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/lockarhythm/tasks/Task.java create mode 100644 src/main/java/com/lockarhythm/tasks/TaskList.java create mode 100644 src/test/java/com/lockarhythm/tasks/TestTask.java create mode 100644 src/test/java/com/lockarhythm/tasks/TestTaskList.java diff --git a/BUILD b/BUILD index ab7919a6..86e028ee 100644 --- a/BUILD +++ b/BUILD @@ -44,3 +44,21 @@ java_test( "src/main/java/com/lockarhythm/**/*.java", ]), ) + +java_test( + name = "TestTask", + size = "small", + srcs = glob([ + "src/test/java/com/lockarhythm/**/*.java", + "src/main/java/com/lockarhythm/**/*.java", + ]), +) + +java_test( + name = "TestTaskList", + size = "small", + srcs = glob([ + "src/test/java/com/lockarhythm/**/*.java", + "src/main/java/com/lockarhythm/**/*.java", + ]), +) diff --git a/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java b/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java index 20ffe009..efa4db0e 100644 --- a/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java +++ b/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java @@ -1,32 +1,43 @@ package com.lockarhythm.responders.addlist; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import com.lockarhythm.responders.Response; import com.lockarhythm.responders.QueryRespondable; -import java.util.ArrayList; +import com.lockarhythm.tasks.TaskList; +import com.lockarhythm.tasks.Task; public class AddListResponder implements QueryRespondable { - private ArrayList list; + private static String doneCommandPrefix = "done "; + private TaskList list; + Pattern pattern = Pattern.compile(doneCommandPrefix+"(\\d+)"); public AddListResponder() { - list = new ArrayList(); + list = new TaskList(); + } + + private boolean isDoneCommand(Matcher matcher) { + return matcher.find(); } - private void printList(StringBuilder s) { - int i = 1; - for (i = 0; i < list.size(); i++) { - s.append(i+1); - s.append(". "); - s.append(list.get(i)); - s.append("\n"); + private Response handleDoneCommand(Matcher matcher) { + int i = Integer.parseInt(matcher.group(1)); + if (i > 0 && i <= list.size()) { + Task task = list.markAsDone(i - 1); + return new Response("Nice! I've marked this task as done:\n\t" + task.toString(), false); } + return new Response(String.format("Item %d is not on the list. I cannot mark it as done!", i), false); } public Response respondTo(String query) { if (query.equals("list")) { - StringBuilder s = new StringBuilder(); - printList(s); - return new Response(s.toString(), false); + return new Response("Here are the tasks in your list:\n"+list.toString(), false); + } + Matcher matcher = pattern.matcher(query); + if (isDoneCommand(matcher)) { + return handleDoneCommand(matcher); } // by default, adds the given query. list.add(query); diff --git a/src/main/java/com/lockarhythm/tasks/Task.java b/src/main/java/com/lockarhythm/tasks/Task.java new file mode 100644 index 00000000..1230cbdd --- /dev/null +++ b/src/main/java/com/lockarhythm/tasks/Task.java @@ -0,0 +1,34 @@ +package com.lockarhythm.tasks; + +import java.util.ArrayList; + +public class Task { + private String description; + private boolean isDone; + + public Task(String description) { + this.description = description; + this.isDone = false; + } + + public void toggleDone() { + this.isDone = !this.isDone; + } + + public String getDescription() { + return description; + } + + public boolean isDone() { + return isDone; + } + + private String getDoneIcon() { + return isDone ? "X" : " "; + } + + @Override + public String toString() { + return String.format("[%s] %s", getDoneIcon(), description); + } +} diff --git a/src/main/java/com/lockarhythm/tasks/TaskList.java b/src/main/java/com/lockarhythm/tasks/TaskList.java new file mode 100644 index 00000000..2f514aa0 --- /dev/null +++ b/src/main/java/com/lockarhythm/tasks/TaskList.java @@ -0,0 +1,38 @@ +package com.lockarhythm.tasks; + +import java.util.ArrayList; + +public class TaskList { + private ArrayList list; + + public TaskList() { + list = new ArrayList(); + } + + public int size() { + return list.size(); + } + + public void add(String description) { + list.add(new Task(description)); + } + + public Task markAsDone(int index) { + Task t = list.get(index); + t.toggleDone(); + return t; + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder(); + int i = 1; + for (i = 0; i < list.size(); i++) { + s.append(i+1); + s.append("."); + s.append(list.get(i)); + s.append("\n"); + } + return s.toString(); + } +} diff --git a/src/test/java/com/lockarhythm/cmdline/TestDuke.java b/src/test/java/com/lockarhythm/cmdline/TestDuke.java index 6a11ef76..9a291299 100644 --- a/src/test/java/com/lockarhythm/cmdline/TestDuke.java +++ b/src/test/java/com/lockarhythm/cmdline/TestDuke.java @@ -30,8 +30,8 @@ public void testMeetsLevel2() throws Exception { assertTrue("it adds the 1st item", output.contains("added: read book")); assertTrue("it adds the 2nd item", output.contains("added: return book")); - assertTrue("it lists the items", output.contains("1. read book")); - assertTrue("it lists the items", output.contains("2. return book")); + assertTrue("it lists the items", output.contains("1.[ ] read book")); + assertTrue("it lists the items", output.contains("2.[ ] return book")); assertTrue("it exits", output.contains("Bye. Hope to see you again soon!")); System.setOut(standardOut); diff --git a/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java b/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java index c57f7195..eb89e15d 100644 --- a/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java +++ b/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java @@ -18,16 +18,44 @@ public void testAddsItemsAndLists() throws Exception { assertEquals("added: return book", res.getText()); res = responder.respondTo("list"); - assertEquals("1. read book\n2. return book\n", res.getText()); + assertTrue(res.getText().contains("1.[ ] read book\n2.[ ] return book\n")); } @Test - public void testEmptyList() throws Exception { + public void testMarkAsDoneHappyPath() throws Exception { AddListResponder responder = new AddListResponder(); - Response res = responder.respondTo("list"); - assertEquals("", res.getText()); + Response res = responder.respondTo("read book"); + assertEquals("added: read book", res.getText()); + + res = responder.respondTo("return book"); + assertEquals("added: return book", res.getText()); + + res = responder.respondTo("done 1"); + assertTrue(res.getText().contains("Nice! I've marked this task as done")); + assertTrue(res.getText().contains("read book")); + + res = responder.respondTo("list"); + assertTrue(res.getText().contains("1.[X] read book\n2.[ ] return book\n")); + + } + + @Test + public void testMarkAsDoneOnNonExistentItem() throws Exception { + AddListResponder responder = new AddListResponder(); + + Response res = responder.respondTo("read book"); + assertEquals("added: read book", res.getText()); + + res = responder.respondTo("return book"); + assertEquals("added: return book", res.getText()); + + res = responder.respondTo("done 10000"); + assertTrue(res.getText().contains("Item 10000 is not on the list. I cannot mark it as done!")); + + res = responder.respondTo("list"); + assertTrue(res.getText().contains("1.[ ] read book\n2.[ ] return book\n")); } } diff --git a/src/test/java/com/lockarhythm/tasks/TestTask.java b/src/test/java/com/lockarhythm/tasks/TestTask.java new file mode 100644 index 00000000..87a3ed5d --- /dev/null +++ b/src/test/java/com/lockarhythm/tasks/TestTask.java @@ -0,0 +1,15 @@ +package com.lockarhythm.tasks; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class TestTask { + @Test + public void testToggleDone() throws Exception { + Task task = new Task("learn java well"); + + task.toggleDone(); + assertTrue("task should be done", task.isDone()); + } +} diff --git a/src/test/java/com/lockarhythm/tasks/TestTaskList.java b/src/test/java/com/lockarhythm/tasks/TestTaskList.java new file mode 100644 index 00000000..5baa9133 --- /dev/null +++ b/src/test/java/com/lockarhythm/tasks/TestTaskList.java @@ -0,0 +1,19 @@ +package com.lockarhythm.tasks; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class TestTaskList { + @Test + public void testAddTask() throws Exception { + TaskList list = new TaskList(); + + list.add("learn useful libraries in java"); + list.add("learn bazel"); + assertEquals("has correct number of items", 2, list.size()); + + Task task = list.markAsDone(1); + assertEquals("can mark tasks as done", "learn bazel", task.getDescription()); + } +} From 9aa137f3a8f2444f4c9ebeadf5d40ea1a39d8498 Mon Sep 17 00:00:00 2001 From: 9y5 Date: Sat, 21 Aug 2021 22:22:06 +0800 Subject: [PATCH 10/32] Refactor markasdone as separate responder. --- BUILD | 9 ++++ .../java/com/lockarhythm/cmdline/Duke.java | 7 ++- .../responders/addlist/AddListResponder.java | 24 +-------- .../markasdone/MarkAsDoneResponder.java | 33 ++++++++++++ .../addlist/TestAddListResponder.java | 40 +------------- .../markasdone/TestMarkAsDoneResponder.java | 52 +++++++++++++++++++ 6 files changed, 104 insertions(+), 61 deletions(-) create mode 100644 src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java create mode 100644 src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java diff --git a/BUILD b/BUILD index 86e028ee..ea4e71a8 100644 --- a/BUILD +++ b/BUILD @@ -45,6 +45,15 @@ java_test( ]), ) +java_test( + name = "TestMarkAsDoneResponder", + size = "small", + srcs = glob([ + "src/test/java/com/lockarhythm/**/*.java", + "src/main/java/com/lockarhythm/**/*.java", + ]), +) + java_test( name = "TestTask", size = "small", diff --git a/src/main/java/com/lockarhythm/cmdline/Duke.java b/src/main/java/com/lockarhythm/cmdline/Duke.java index c3925a29..63a50e3e 100644 --- a/src/main/java/com/lockarhythm/cmdline/Duke.java +++ b/src/main/java/com/lockarhythm/cmdline/Duke.java @@ -7,6 +7,8 @@ import com.lockarhythm.responders.Response; import com.lockarhythm.responders.exit.ExitResponder; import com.lockarhythm.responders.addlist.AddListResponder; +import com.lockarhythm.responders.markasdone.MarkAsDoneResponder; +import com.lockarhythm.tasks.TaskList; public class Duke { static String logo = " \t____ _ \n" @@ -15,9 +17,12 @@ public class Duke { + "\t| |_| | |_| | < __/\n" + "\t|____/ \\__,_|_|\\_\\___|\n"; + static TaskList list = new TaskList(); + static QueryRespondable[] responders = { new ExitResponder(), - new AddListResponder(), + new MarkAsDoneResponder(list), + new AddListResponder(list), }; private static void print(String ...strings) { diff --git a/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java b/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java index efa4db0e..7205d87f 100644 --- a/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java +++ b/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java @@ -7,38 +7,18 @@ import com.lockarhythm.responders.QueryRespondable; import com.lockarhythm.tasks.TaskList; -import com.lockarhythm.tasks.Task; public class AddListResponder implements QueryRespondable { - private static String doneCommandPrefix = "done "; private TaskList list; - Pattern pattern = Pattern.compile(doneCommandPrefix+"(\\d+)"); - public AddListResponder() { - list = new TaskList(); - } - - private boolean isDoneCommand(Matcher matcher) { - return matcher.find(); - } - - private Response handleDoneCommand(Matcher matcher) { - int i = Integer.parseInt(matcher.group(1)); - if (i > 0 && i <= list.size()) { - Task task = list.markAsDone(i - 1); - return new Response("Nice! I've marked this task as done:\n\t" + task.toString(), false); - } - return new Response(String.format("Item %d is not on the list. I cannot mark it as done!", i), false); + public AddListResponder(TaskList list) { + this.list = list; } public Response respondTo(String query) { if (query.equals("list")) { return new Response("Here are the tasks in your list:\n"+list.toString(), false); } - Matcher matcher = pattern.matcher(query); - if (isDoneCommand(matcher)) { - return handleDoneCommand(matcher); - } // by default, adds the given query. list.add(query); return new Response("added: " + query, false); diff --git a/src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java b/src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java new file mode 100644 index 00000000..c7d66246 --- /dev/null +++ b/src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java @@ -0,0 +1,33 @@ +package com.lockarhythm.responders.markasdone; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.lockarhythm.responders.Response; +import com.lockarhythm.responders.QueryRespondable; + +import com.lockarhythm.tasks.TaskList; +import com.lockarhythm.tasks.Task; + +public class MarkAsDoneResponder implements QueryRespondable { + private static String doneCommandPrefix = "done "; + private TaskList list; + Pattern pattern = Pattern.compile(doneCommandPrefix+"(\\d+)"); + + public MarkAsDoneResponder(TaskList list) { + this.list = list; + } + + public Response respondTo(String query) { + Matcher matcher = pattern.matcher(query); + if (matcher.find()) { + int i = Integer.parseInt(matcher.group(1)); + if (i > 0 && i <= list.size()) { + Task task = list.markAsDone(i - 1); + return new Response("Nice! I've marked this task as done:\n\t" + task.toString(), false); + } + return new Response(String.format("Item %d is not on the list. I cannot mark it as done!", i), false); + } + return null; + } +} diff --git a/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java b/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java index eb89e15d..306f6f64 100644 --- a/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java +++ b/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java @@ -5,11 +5,12 @@ import org.junit.Test; import com.lockarhythm.responders.Response; +import com.lockarhythm.tasks.TaskList; public class TestAddListResponder { @Test public void testAddsItemsAndLists() throws Exception { - AddListResponder responder = new AddListResponder(); + AddListResponder responder = new AddListResponder(new TaskList()); Response res = responder.respondTo("read book"); assertEquals("added: read book", res.getText()); @@ -21,41 +22,4 @@ public void testAddsItemsAndLists() throws Exception { assertTrue(res.getText().contains("1.[ ] read book\n2.[ ] return book\n")); } - - @Test - public void testMarkAsDoneHappyPath() throws Exception { - AddListResponder responder = new AddListResponder(); - - Response res = responder.respondTo("read book"); - assertEquals("added: read book", res.getText()); - - res = responder.respondTo("return book"); - assertEquals("added: return book", res.getText()); - - res = responder.respondTo("done 1"); - assertTrue(res.getText().contains("Nice! I've marked this task as done")); - assertTrue(res.getText().contains("read book")); - - res = responder.respondTo("list"); - assertTrue(res.getText().contains("1.[X] read book\n2.[ ] return book\n")); - - } - - @Test - public void testMarkAsDoneOnNonExistentItem() throws Exception { - AddListResponder responder = new AddListResponder(); - - Response res = responder.respondTo("read book"); - assertEquals("added: read book", res.getText()); - - res = responder.respondTo("return book"); - assertEquals("added: return book", res.getText()); - - res = responder.respondTo("done 10000"); - assertTrue(res.getText().contains("Item 10000 is not on the list. I cannot mark it as done!")); - - res = responder.respondTo("list"); - assertTrue(res.getText().contains("1.[ ] read book\n2.[ ] return book\n")); - - } } diff --git a/src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java b/src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java new file mode 100644 index 00000000..b4c51647 --- /dev/null +++ b/src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java @@ -0,0 +1,52 @@ +package com.lockarhythm.responders.markasdone; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import com.lockarhythm.responders.Response; +import com.lockarhythm.responders.addlist.AddListResponder; +import com.lockarhythm.tasks.TaskList; + +public class TestMarkAsDoneResponder { + @Test + public void testMarkAsDoneHappyPath() throws Exception { + TaskList list = new TaskList(); + MarkAsDoneResponder mdresponder = new MarkAsDoneResponder(list); + AddListResponder alresponder = new AddListResponder(list); + + Response res = alresponder.respondTo("read book"); + assertEquals("added: read book", res.getText()); + + res = alresponder.respondTo("return book"); + assertEquals("added: return book", res.getText()); + + res = mdresponder.respondTo("done 1"); + assertTrue(res.getText().contains("Nice! I've marked this task as done")); + assertTrue(res.getText().contains("read book")); + + res = alresponder.respondTo("list"); + assertTrue(res.getText().contains("1.[X] read book\n2.[ ] return book\n")); + + } + + @Test + public void testMarkAsDoneOnNonExistentItem() throws Exception { + TaskList list = new TaskList(); + MarkAsDoneResponder mdresponder = new MarkAsDoneResponder(list); + AddListResponder alresponder = new AddListResponder(list); + + Response res = alresponder.respondTo("read book"); + assertEquals("added: read book", res.getText()); + + res = alresponder.respondTo("return book"); + assertEquals("added: return book", res.getText()); + + res = mdresponder.respondTo("done 10000"); + assertTrue(res.getText().contains("Item 10000 is not on the list. I cannot mark it as done!")); + + res = alresponder.respondTo("list"); + assertTrue(res.getText().contains("1.[ ] read book\n2.[ ] return book\n")); + + } +} From f6069de566bbaa4f417a8aeba1d726a133d14605 Mon Sep 17 00:00:00 2001 From: 9y5 Date: Mon, 23 Aug 2021 01:55:56 +0800 Subject: [PATCH 11/32] Refactor BUILD file. --- BUILD | 84 ++++++++++++++--------------------------------------------- 1 file changed, 20 insertions(+), 64 deletions(-) diff --git a/BUILD b/BUILD index ea4e71a8..ac1cceed 100644 --- a/BUILD +++ b/BUILD @@ -1,5 +1,4 @@ -load("@rules_java//java:defs.bzl", "java_binary") -load("@rules_java//java:defs.bzl", "java_test") +load("@rules_java//java:defs.bzl", "java_binary", "java_test") java_binary( name = "Duke", @@ -9,65 +8,22 @@ java_binary( deps = ["@maven//:org_apache_commons_commons_lang3"] ) -java_test( - name = "TestDuke", - size = "small", - srcs = glob([ - "src/test/java/com/lockarhythm/**/*.java", - "src/main/java/com/lockarhythm/**/*.java", - ]), -) - -java_test( - name = "TestEchoResponder", - size = "small", - srcs = glob([ - "src/test/java/com/lockarhythm/**/*.java", - "src/main/java/com/lockarhythm/**/*.java", - ]), -) - -java_test( - name = "TestExitResponder", - size = "small", - srcs = glob([ - "src/test/java/com/lockarhythm/**/*.java", - "src/main/java/com/lockarhythm/**/*.java", - ]), -) - -java_test( - name = "TestAddListResponder", - size = "small", - srcs = glob([ - "src/test/java/com/lockarhythm/**/*.java", - "src/main/java/com/lockarhythm/**/*.java", - ]), -) - -java_test( - name = "TestMarkAsDoneResponder", - size = "small", - srcs = glob([ - "src/test/java/com/lockarhythm/**/*.java", - "src/main/java/com/lockarhythm/**/*.java", - ]), -) - -java_test( - name = "TestTask", - size = "small", - srcs = glob([ - "src/test/java/com/lockarhythm/**/*.java", - "src/main/java/com/lockarhythm/**/*.java", - ]), -) - -java_test( - name = "TestTaskList", - size = "small", - srcs = glob([ - "src/test/java/com/lockarhythm/**/*.java", - "src/main/java/com/lockarhythm/**/*.java", - ]), -) +[ + java_test( + name = class_name, + size = "small", + srcs = glob([ + "src/test/java/com/lockarhythm/**/*.java", + "src/main/java/com/lockarhythm/**/*.java", + ]), + ) + for class_name in [ + "TestDuke", + "TestEchoResponder", + "TestExitResponder", + "TestAddListResponder", + "TestMarkAsDoneResponder", + "TestTask", + "TestTaskList", + ] +] From 85c0c594f39e4fa2f57092d06ec6320a6e24a5a0 Mon Sep 17 00:00:00 2001 From: 9y5 Date: Mon, 23 Aug 2021 22:03:03 +0800 Subject: [PATCH 12/32] Implement A-TextUiTesting for macOS and bazel. --- text-ui-test/EXPECTED.TXT | 57 ++++++++++++++++++++++++++++++++++----- text-ui-test/input.txt | 7 +++++ text-ui-test/runtest.sh | 6 ++--- 3 files changed, 61 insertions(+), 9 deletions(-) mode change 100644 => 100755 text-ui-test/runtest.sh diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6..69bbc0bb 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,52 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| + ____________________________________________________________ + Hello I'm + ____ _ + | _ \ _ _| | _____ + | | | | | | | |/ / _ \ + | |_| | |_| | < __/ + |____/ \__,_|_|\_\___| + + What can I do for you? + + ____________________________________________________________ + + ____________________________________________________________ + added: read book + + ____________________________________________________________ + + ____________________________________________________________ + added: return book + + ____________________________________________________________ + + ____________________________________________________________ + added: buy bread + + ____________________________________________________________ + + ____________________________________________________________ + Nice! I've marked this task as done: + [X] read book + + ____________________________________________________________ + + ____________________________________________________________ + Here are the tasks in your list: + 1.[X] read book + 2.[ ] return book + 3.[ ] buy bread + + ____________________________________________________________ + + ____________________________________________________________ + Nice! I've marked this task as done: + [X] return book + + ____________________________________________________________ + + ____________________________________________________________ + Bye. Hope to see you again soon! + + ____________________________________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29b..2cb3cf8a 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,7 @@ +read book +return book +buy bread +done 1 +list +done 2 +bye diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh old mode 100644 new mode 100755 index c9ec8700..220caaa7 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -13,14 +13,14 @@ then fi # compile the code into the bin folder, terminates if error occurred -if ! javac -cp ../src/main/java -Xlint:none -d ../bin ../src/main/java/*.java +if ! bazel build //:Duke then echo "********** BUILD FAILURE **********" exit 1 fi # run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ../bin Duke < input.txt > ACTUAL.TXT +../bazel-bin/Duke < input.txt > ACTUAL.TXT # convert to UNIX format cp EXPECTED.TXT EXPECTED-UNIX.TXT @@ -35,4 +35,4 @@ then else echo "Test result: FAILED" exit 1 -fi \ No newline at end of file +fi From 6a850922425bd1361ebea85ab974dd1be154ad1b Mon Sep 17 00:00:00 2001 From: 9y5 Date: Mon, 23 Aug 2021 22:25:22 +0800 Subject: [PATCH 13/32] Basic github workflow: * Installs bazelisk * Builds JAR * Runs unit tests * Upload JAR artifact. --- .github/workflows/workflow.yml | 39 ++++++++++++++++++++++++++++ README.md | 47 +++++++++++++++------------------- 2 files changed, 60 insertions(+), 26 deletions(-) create mode 100644 .github/workflows/workflow.yml diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml new file mode 100644 index 00000000..7fc94623 --- /dev/null +++ b/.github/workflows/workflow.yml @@ -0,0 +1,39 @@ +on: push +name: Build, Test and Upload JAR +jobs: + checks: + name: Run Unit Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + + - name: Mount bazel cache + uses: actions/cache@v1 + with: + path: "/home/runner/.cache/bazel" + key: bazel + + - name: Install bazelisk + run: | + curl -LO "https://github.com/bazelbuild/bazelisk/releases/download/v1.1.0/bazelisk-linux-amd64" + mkdir -p "${GITHUB_WORKSPACE}/bin/" + mv bazelisk-linux-amd64 "${GITHUB_WORKSPACE}/bin/bazel" + chmod +x "${GITHUB_WORKSPACE}/bin/bazel" + + - name: Run Unit Tests + run: | + "${GITHUB_WORKSPACE}/bin/bazel" test //... + + - name: Build JAR File + run: | + "${GITHUB_WORKSPACE}/bin/bazel" build //:Duke_deploy.jar + + - name: "Move file to non-symlink directory (a workaround for some bug See: https://github.com/actions/upload-artifact/issues/92)" + run: | + mv ${{ github.workspace }}/bazel-bin/Duke_deploy.jar ${{ github.workspace }}/Duke_deploy.jar + + - name: Upload JAR File + uses: actions/upload-artifact@v2 + with: + name: Duke_deploy.jar + path: ${{ github.workspace }}/Duke_deploy.jar diff --git a/README.md b/README.md index e511e631..31020453 100644 --- a/README.md +++ b/README.md @@ -2,37 +2,32 @@ 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. -## Local Build +## Build Prerequisites: Have `bazel` installed. + +To build for local development on macOS: ```bash -# Following tested on OSX. -bazel build //:Duke -./bazel-bin/Duke +# bazel makes a wrapper script around the jar file. +bazel build //:Duke && ./bazel-bin/Duke + +# or simply +bazel run //:Duke ``` -## Testing +To build jar file for deployment purposes: +```bash +bazel build //:Duke_deploy.jar + +java -jar ./bazel-bin/Duke_deploy.jar +``` +## Testing +For unit tests: ```bash bazel test --test_output=all //... ``` - -## Setting up in Intellij - -Prerequisites: JDK 11, update Intellij to the most recent version. - -1. Open Intellij (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project first) -1. Open the project into Intellij as follows: - 1. Click `Open`. - 1. Select the project directory, and click `OK`. - 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: - ``` - Hello from - ____ _ - | _ \ _ _| | _____ - | | | | | | | |/ / _ \ - | |_| | |_| | < __/ - |____/ \__,_|_|\_\___| - ``` +For terminal ui tests (`text-ui-test`): +```bash +cd text-ui-test +./runtest.sh +``` From f30fd0645b6087b995fb7e4c176a17180a6ea2ad Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Tue, 24 Aug 2021 20:15:36 +0800 Subject: [PATCH 14/32] Run through google-java-format. Add checkstyle config. (#1) --- README.md | 6 + config/checkstyle/checkstyle.xml | 403 ++++++++++++++++++ config/checkstyle/suppressions.xml | 10 + .../java/com/lockarhythm/cmdline/Duke.java | 92 ++-- .../responders/addlist/AddListResponder.java | 28 +- .../responders/echo/EchoResponder.java | 8 +- .../responders/exit/ExitResponder.java | 12 +- .../markasdone/MarkAsDoneResponder.java | 45 +- src/main/java/com/lockarhythm/tasks/Task.java | 58 ++- .../java/com/lockarhythm/tasks/TaskList.java | 52 +-- .../com/lockarhythm/cmdline/TestDuke.java | 59 ++- .../addlist/TestAddListResponder.java | 24 +- .../responders/echo/TestEchoResponder.java | 17 +- .../responders/exit/TestExitResponder.java | 27 +- .../markasdone/TestMarkAsDoneResponder.java | 63 ++- .../java/com/lockarhythm/tasks/TestTask.java | 12 +- .../com/lockarhythm/tasks/TestTaskList.java | 18 +- 17 files changed, 666 insertions(+), 268 deletions(-) create mode 100644 config/checkstyle/checkstyle.xml create mode 100644 config/checkstyle/suppressions.xml diff --git a/README.md b/README.md index 31020453..5bf303dd 100644 --- a/README.md +++ b/README.md @@ -31,3 +31,9 @@ For terminal ui tests (`text-ui-test`): cd text-ui-test ./runtest.sh ``` + +## Code Formatting & Style +Use [google-java-format](https://github.com/google/google-java-format): +```bash +google-java-format --replace **/*.java # executes recursively +``` diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 00000000..4c001417 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml new file mode 100644 index 00000000..39efb6e4 --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/src/main/java/com/lockarhythm/cmdline/Duke.java b/src/main/java/com/lockarhythm/cmdline/Duke.java index 63a50e3e..8697a580 100644 --- a/src/main/java/com/lockarhythm/cmdline/Duke.java +++ b/src/main/java/com/lockarhythm/cmdline/Duke.java @@ -1,64 +1,62 @@ package com.lockarhythm.cmdline; -import java.util.Scanner; -import java.util.Arrays; - import com.lockarhythm.responders.QueryRespondable; import com.lockarhythm.responders.Response; -import com.lockarhythm.responders.exit.ExitResponder; import com.lockarhythm.responders.addlist.AddListResponder; +import com.lockarhythm.responders.exit.ExitResponder; import com.lockarhythm.responders.markasdone.MarkAsDoneResponder; import com.lockarhythm.tasks.TaskList; +import java.util.Arrays; +import java.util.Scanner; public class Duke { - static String logo = " \t____ _ \n" - + "\t| _ \\ _ _| | _____ \n" - + "\t| | | | | | | |/ / _ \\\n" - + "\t| |_| | |_| | < __/\n" - + "\t|____/ \\__,_|_|\\_\\___|\n"; - - static TaskList list = new TaskList(); - - static QueryRespondable[] responders = { - new ExitResponder(), - new MarkAsDoneResponder(list), - new AddListResponder(list), - }; - - private static void print(String ...strings) { - System.out.println("\t____________________________________________________________"); - for (String s : strings) { - s = Arrays.stream(s.split("\n")).map(x -> "\t" + x).reduce("", (x, y) -> x + y + "\n"); - System.out.println(s); - } - System.out.println("\t____________________________________________________________\n"); + static String logo = + " \t____ _ \n" + + "\t| _ \\ _ _| | _____ \n" + + "\t| | | | | | | |/ / _ \\\n" + + "\t| |_| | |_| | < __/\n" + + "\t|____/ \\__,_|_|\\_\\___|\n"; + + static TaskList list = new TaskList(); + + static QueryRespondable[] responders = { + new ExitResponder(), new MarkAsDoneResponder(list), new AddListResponder(list), + }; + + private static void print(String... strings) { + System.out.println("\t____________________________________________________________"); + for (String s : strings) { + s = Arrays.stream(s.split("\n")).map(x -> "\t" + x).reduce("", (x, y) -> x + y + "\n"); + System.out.println(s); } - - private static Response getResponse(String query) { - Response res = null; - for (QueryRespondable responder : responders) { - res = responder.respondTo(query); - if (res != null) { - return res; - } - } + System.out.println("\t____________________________________________________________\n"); + } + + private static Response getResponse(String query) { + Response res = null; + for (QueryRespondable responder : responders) { + res = responder.respondTo(query); + if (res != null) { return res; + } } + return res; + } - public static void main(String[] args) { - String line; - Scanner in = new Scanner(System.in); - - print("Hello I'm\n" + logo, "What can I do for you?"); + public static void main(String[] args) { + String line; + Scanner in = new Scanner(System.in); - line = in.nextLine(); - Response res = getResponse(line); - while(!res.shouldExit()) { - print(res.getText()); - line = in.nextLine(); - res = getResponse(line); - } + print("Hello I'm\n" + logo, "What can I do for you?"); - print(res.getText()); + line = in.nextLine(); + Response res = getResponse(line); + while (!res.shouldExit()) { + print(res.getText()); + line = in.nextLine(); + res = getResponse(line); } + + print(res.getText()); + } } diff --git a/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java b/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java index 7205d87f..6253ca51 100644 --- a/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java +++ b/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java @@ -1,26 +1,22 @@ package com.lockarhythm.responders.addlist; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.lockarhythm.responders.Response; import com.lockarhythm.responders.QueryRespondable; - +import com.lockarhythm.responders.Response; import com.lockarhythm.tasks.TaskList; public class AddListResponder implements QueryRespondable { - private TaskList list; + private TaskList list; - public AddListResponder(TaskList list) { - this.list = list; - } + public AddListResponder(TaskList list) { + this.list = list; + } - public Response respondTo(String query) { - if (query.equals("list")) { - return new Response("Here are the tasks in your list:\n"+list.toString(), false); - } - // by default, adds the given query. - list.add(query); - return new Response("added: " + query, false); + public Response respondTo(String query) { + if (query.equals("list")) { + return new Response("Here are the tasks in your list:\n" + list.toString(), false); } + // by default, adds the given query. + list.add(query); + return new Response("added: " + query, false); + } } diff --git a/src/main/java/com/lockarhythm/responders/echo/EchoResponder.java b/src/main/java/com/lockarhythm/responders/echo/EchoResponder.java index 7547ba76..214acc5f 100644 --- a/src/main/java/com/lockarhythm/responders/echo/EchoResponder.java +++ b/src/main/java/com/lockarhythm/responders/echo/EchoResponder.java @@ -1,10 +1,10 @@ package com.lockarhythm.responders.echo; -import com.lockarhythm.responders.Response; import com.lockarhythm.responders.QueryRespondable; +import com.lockarhythm.responders.Response; public class EchoResponder implements QueryRespondable { - public Response respondTo(String query) { - return new Response(query, false); - } + public Response respondTo(String query) { + return new Response(query, false); + } } diff --git a/src/main/java/com/lockarhythm/responders/exit/ExitResponder.java b/src/main/java/com/lockarhythm/responders/exit/ExitResponder.java index 34a1bb32..0206732b 100644 --- a/src/main/java/com/lockarhythm/responders/exit/ExitResponder.java +++ b/src/main/java/com/lockarhythm/responders/exit/ExitResponder.java @@ -1,13 +1,13 @@ package com.lockarhythm.responders.exit; -import com.lockarhythm.responders.Response; import com.lockarhythm.responders.QueryRespondable; +import com.lockarhythm.responders.Response; public class ExitResponder implements QueryRespondable { - public Response respondTo(String query) { - if (query.equals("bye")) { - return new Response("Bye. Hope to see you again soon!", true); - } - return null; + public Response respondTo(String query) { + if (query.equals("bye")) { + return new Response("Bye. Hope to see you again soon!", true); } + return null; + } } diff --git a/src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java b/src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java index c7d66246..182c4230 100644 --- a/src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java +++ b/src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java @@ -1,33 +1,32 @@ package com.lockarhythm.responders.markasdone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.lockarhythm.responders.Response; import com.lockarhythm.responders.QueryRespondable; - -import com.lockarhythm.tasks.TaskList; +import com.lockarhythm.responders.Response; import com.lockarhythm.tasks.Task; +import com.lockarhythm.tasks.TaskList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class MarkAsDoneResponder implements QueryRespondable { - private static String doneCommandPrefix = "done "; - private TaskList list; - Pattern pattern = Pattern.compile(doneCommandPrefix+"(\\d+)"); + private static String doneCommandPrefix = "done "; + private TaskList list; + Pattern pattern = Pattern.compile(doneCommandPrefix + "(\\d+)"); - public MarkAsDoneResponder(TaskList list) { - this.list = list; - } + public MarkAsDoneResponder(TaskList list) { + this.list = list; + } - public Response respondTo(String query) { - Matcher matcher = pattern.matcher(query); - if (matcher.find()) { - int i = Integer.parseInt(matcher.group(1)); - if (i > 0 && i <= list.size()) { - Task task = list.markAsDone(i - 1); - return new Response("Nice! I've marked this task as done:\n\t" + task.toString(), false); - } - return new Response(String.format("Item %d is not on the list. I cannot mark it as done!", i), false); - } - return null; + public Response respondTo(String query) { + Matcher matcher = pattern.matcher(query); + if (matcher.find()) { + int i = Integer.parseInt(matcher.group(1)); + if (i > 0 && i <= list.size()) { + Task task = list.markAsDone(i - 1); + return new Response("Nice! I've marked this task as done:\n\t" + task.toString(), false); + } + return new Response( + String.format("Item %d is not on the list. I cannot mark it as done!", i), false); } + return null; + } } diff --git a/src/main/java/com/lockarhythm/tasks/Task.java b/src/main/java/com/lockarhythm/tasks/Task.java index 1230cbdd..943f6749 100644 --- a/src/main/java/com/lockarhythm/tasks/Task.java +++ b/src/main/java/com/lockarhythm/tasks/Task.java @@ -1,34 +1,32 @@ package com.lockarhythm.tasks; -import java.util.ArrayList; - public class Task { - private String description; - private boolean isDone; - - public Task(String description) { - this.description = description; - this.isDone = false; - } - - public void toggleDone() { - this.isDone = !this.isDone; - } - - public String getDescription() { - return description; - } - - public boolean isDone() { - return isDone; - } - - private String getDoneIcon() { - return isDone ? "X" : " "; - } - - @Override - public String toString() { - return String.format("[%s] %s", getDoneIcon(), description); - } + private String description; + private boolean isDone; + + public Task(String description) { + this.description = description; + this.isDone = false; + } + + public void toggleDone() { + this.isDone = !this.isDone; + } + + public String getDescription() { + return description; + } + + public boolean isDone() { + return isDone; + } + + private String getDoneIcon() { + return isDone ? "X" : " "; + } + + @Override + public String toString() { + return String.format("[%s] %s", getDoneIcon(), description); + } } diff --git a/src/main/java/com/lockarhythm/tasks/TaskList.java b/src/main/java/com/lockarhythm/tasks/TaskList.java index 2f514aa0..9732d032 100644 --- a/src/main/java/com/lockarhythm/tasks/TaskList.java +++ b/src/main/java/com/lockarhythm/tasks/TaskList.java @@ -3,36 +3,36 @@ import java.util.ArrayList; public class TaskList { - private ArrayList list; + private ArrayList list; - public TaskList() { - list = new ArrayList(); - } + public TaskList() { + list = new ArrayList(); + } - public int size() { - return list.size(); - } + public int size() { + return list.size(); + } - public void add(String description) { - list.add(new Task(description)); - } + public void add(String description) { + list.add(new Task(description)); + } - public Task markAsDone(int index) { - Task t = list.get(index); - t.toggleDone(); - return t; - } + public Task markAsDone(int index) { + Task t = list.get(index); + t.toggleDone(); + return t; + } - @Override - public String toString() { - StringBuilder s = new StringBuilder(); - int i = 1; - for (i = 0; i < list.size(); i++) { - s.append(i+1); - s.append("."); - s.append(list.get(i)); - s.append("\n"); - } - return s.toString(); + @Override + public String toString() { + StringBuilder s = new StringBuilder(); + int i = 1; + for (i = 0; i < list.size(); i++) { + s.append(i + 1); + s.append("."); + s.append(list.get(i)); + s.append("\n"); } + return s.toString(); + } } diff --git a/src/test/java/com/lockarhythm/cmdline/TestDuke.java b/src/test/java/com/lockarhythm/cmdline/TestDuke.java index 9a291299..a2d69327 100644 --- a/src/test/java/com/lockarhythm/cmdline/TestDuke.java +++ b/src/test/java/com/lockarhythm/cmdline/TestDuke.java @@ -2,40 +2,35 @@ import static org.junit.Assert.*; -import org.junit.Test; - -import java.io.ByteArrayOutputStream; import java.io.ByteArrayInputStream; -import java.io.PrintStream; +import java.io.ByteArrayOutputStream; import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Paths; +import java.io.PrintStream; +import org.junit.Test; public class TestDuke { - private final PrintStream standardOut = System.out; - private final InputStream standardIn = System.in; - private final ByteArrayOutputStream out = new ByteArrayOutputStream(); - private final ByteArrayInputStream in = new ByteArrayInputStream( - "read book\nreturn book\nlist\nbye".getBytes() - ); - - @Test - public void testMeetsLevel2() throws Exception { - System.setIn(in); - System.setOut(new PrintStream(out)); - - Duke.main(null); - String output = out.toString().trim(); - assertFalse(output.isEmpty()); - - assertTrue("it adds the 1st item", output.contains("added: read book")); - assertTrue("it adds the 2nd item", output.contains("added: return book")); - assertTrue("it lists the items", output.contains("1.[ ] read book")); - assertTrue("it lists the items", output.contains("2.[ ] return book")); - assertTrue("it exits", output.contains("Bye. Hope to see you again soon!")); - - System.setOut(standardOut); - System.setIn(standardIn); - } - + private final PrintStream standardOut = System.out; + private final InputStream standardIn = System.in; + private final ByteArrayOutputStream out = new ByteArrayOutputStream(); + private final ByteArrayInputStream in = + new ByteArrayInputStream("read book\nreturn book\nlist\nbye".getBytes()); + + @Test + public void testMeetsLevel2() throws Exception { + System.setIn(in); + System.setOut(new PrintStream(out)); + + Duke.main(null); + String output = out.toString().trim(); + assertFalse(output.isEmpty()); + + assertTrue("it adds the 1st item", output.contains("added: read book")); + assertTrue("it adds the 2nd item", output.contains("added: return book")); + assertTrue("it lists the items", output.contains("1.[ ] read book")); + assertTrue("it lists the items", output.contains("2.[ ] return book")); + assertTrue("it exits", output.contains("Bye. Hope to see you again soon!")); + + System.setOut(standardOut); + System.setIn(standardIn); + } } diff --git a/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java b/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java index 306f6f64..8e2d0cdb 100644 --- a/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java +++ b/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java @@ -2,24 +2,22 @@ import static org.junit.Assert.*; -import org.junit.Test; - import com.lockarhythm.responders.Response; import com.lockarhythm.tasks.TaskList; +import org.junit.Test; public class TestAddListResponder { - @Test - public void testAddsItemsAndLists() throws Exception { - AddListResponder responder = new AddListResponder(new TaskList()); - - Response res = responder.respondTo("read book"); - assertEquals("added: read book", res.getText()); + @Test + public void testAddsItemsAndLists() throws Exception { + AddListResponder responder = new AddListResponder(new TaskList()); - res = responder.respondTo("return book"); - assertEquals("added: return book", res.getText()); + Response res = responder.respondTo("read book"); + assertEquals("added: read book", res.getText()); - res = responder.respondTo("list"); - assertTrue(res.getText().contains("1.[ ] read book\n2.[ ] return book\n")); + res = responder.respondTo("return book"); + assertEquals("added: return book", res.getText()); - } + res = responder.respondTo("list"); + assertTrue(res.getText().contains("1.[ ] read book\n2.[ ] return book\n")); + } } diff --git a/src/test/java/com/lockarhythm/responders/echo/TestEchoResponder.java b/src/test/java/com/lockarhythm/responders/echo/TestEchoResponder.java index e883f6f8..e51c34a6 100644 --- a/src/test/java/com/lockarhythm/responders/echo/TestEchoResponder.java +++ b/src/test/java/com/lockarhythm/responders/echo/TestEchoResponder.java @@ -2,17 +2,16 @@ import static org.junit.Assert.*; -import org.junit.Test; - import com.lockarhythm.responders.Response; +import org.junit.Test; public class TestEchoResponder { - @Test - public void testEchosBackText() throws Exception { - EchoResponder responder = new EchoResponder(); - Response res = responder.respondTo("hello"); + @Test + public void testEchosBackText() throws Exception { + EchoResponder responder = new EchoResponder(); + Response res = responder.respondTo("hello"); - assertEquals("hello", res.getText()); - assertFalse(res.shouldExit()); - } + assertEquals("hello", res.getText()); + assertFalse(res.shouldExit()); + } } diff --git a/src/test/java/com/lockarhythm/responders/exit/TestExitResponder.java b/src/test/java/com/lockarhythm/responders/exit/TestExitResponder.java index f2007459..b3f18bd3 100644 --- a/src/test/java/com/lockarhythm/responders/exit/TestExitResponder.java +++ b/src/test/java/com/lockarhythm/responders/exit/TestExitResponder.java @@ -2,24 +2,23 @@ import static org.junit.Assert.*; -import org.junit.Test; - import com.lockarhythm.responders.Response; +import org.junit.Test; public class TestExitResponder { - @Test - public void testExitsOnKeyword() throws Exception { - ExitResponder responder = new ExitResponder(); - Response res = responder.respondTo("bye"); + @Test + public void testExitsOnKeyword() throws Exception { + ExitResponder responder = new ExitResponder(); + Response res = responder.respondTo("bye"); - assertTrue(res.shouldExit()); - } + assertTrue(res.shouldExit()); + } - @Test - public void testNullOnNonkeyword() throws Exception { - ExitResponder responder = new ExitResponder(); - Response res = responder.respondTo("hello"); + @Test + public void testNullOnNonkeyword() throws Exception { + ExitResponder responder = new ExitResponder(); + Response res = responder.respondTo("hello"); - assertNull(res); - } + assertNull(res); + } } diff --git a/src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java b/src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java index b4c51647..c87d6f4c 100644 --- a/src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java +++ b/src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java @@ -2,51 +2,48 @@ import static org.junit.Assert.*; -import org.junit.Test; - import com.lockarhythm.responders.Response; import com.lockarhythm.responders.addlist.AddListResponder; import com.lockarhythm.tasks.TaskList; +import org.junit.Test; public class TestMarkAsDoneResponder { - @Test - public void testMarkAsDoneHappyPath() throws Exception { - TaskList list = new TaskList(); - MarkAsDoneResponder mdresponder = new MarkAsDoneResponder(list); - AddListResponder alresponder = new AddListResponder(list); - - Response res = alresponder.respondTo("read book"); - assertEquals("added: read book", res.getText()); - - res = alresponder.respondTo("return book"); - assertEquals("added: return book", res.getText()); + @Test + public void testMarkAsDoneHappyPath() throws Exception { + TaskList list = new TaskList(); + MarkAsDoneResponder mdresponder = new MarkAsDoneResponder(list); + AddListResponder alresponder = new AddListResponder(list); - res = mdresponder.respondTo("done 1"); - assertTrue(res.getText().contains("Nice! I've marked this task as done")); - assertTrue(res.getText().contains("read book")); + Response res = alresponder.respondTo("read book"); + assertEquals("added: read book", res.getText()); - res = alresponder.respondTo("list"); - assertTrue(res.getText().contains("1.[X] read book\n2.[ ] return book\n")); + res = alresponder.respondTo("return book"); + assertEquals("added: return book", res.getText()); - } + res = mdresponder.respondTo("done 1"); + assertTrue(res.getText().contains("Nice! I've marked this task as done")); + assertTrue(res.getText().contains("read book")); - @Test - public void testMarkAsDoneOnNonExistentItem() throws Exception { - TaskList list = new TaskList(); - MarkAsDoneResponder mdresponder = new MarkAsDoneResponder(list); - AddListResponder alresponder = new AddListResponder(list); + res = alresponder.respondTo("list"); + assertTrue(res.getText().contains("1.[X] read book\n2.[ ] return book\n")); + } - Response res = alresponder.respondTo("read book"); - assertEquals("added: read book", res.getText()); + @Test + public void testMarkAsDoneOnNonExistentItem() throws Exception { + TaskList list = new TaskList(); + MarkAsDoneResponder mdresponder = new MarkAsDoneResponder(list); + AddListResponder alresponder = new AddListResponder(list); - res = alresponder.respondTo("return book"); - assertEquals("added: return book", res.getText()); + Response res = alresponder.respondTo("read book"); + assertEquals("added: read book", res.getText()); - res = mdresponder.respondTo("done 10000"); - assertTrue(res.getText().contains("Item 10000 is not on the list. I cannot mark it as done!")); + res = alresponder.respondTo("return book"); + assertEquals("added: return book", res.getText()); - res = alresponder.respondTo("list"); - assertTrue(res.getText().contains("1.[ ] read book\n2.[ ] return book\n")); + res = mdresponder.respondTo("done 10000"); + assertTrue(res.getText().contains("Item 10000 is not on the list. I cannot mark it as done!")); - } + res = alresponder.respondTo("list"); + assertTrue(res.getText().contains("1.[ ] read book\n2.[ ] return book\n")); + } } diff --git a/src/test/java/com/lockarhythm/tasks/TestTask.java b/src/test/java/com/lockarhythm/tasks/TestTask.java index 87a3ed5d..f70783c3 100644 --- a/src/test/java/com/lockarhythm/tasks/TestTask.java +++ b/src/test/java/com/lockarhythm/tasks/TestTask.java @@ -5,11 +5,11 @@ import org.junit.Test; public class TestTask { - @Test - public void testToggleDone() throws Exception { - Task task = new Task("learn java well"); + @Test + public void testToggleDone() throws Exception { + Task task = new Task("learn java well"); - task.toggleDone(); - assertTrue("task should be done", task.isDone()); - } + task.toggleDone(); + assertTrue("task should be done", task.isDone()); + } } diff --git a/src/test/java/com/lockarhythm/tasks/TestTaskList.java b/src/test/java/com/lockarhythm/tasks/TestTaskList.java index 5baa9133..ce5d6025 100644 --- a/src/test/java/com/lockarhythm/tasks/TestTaskList.java +++ b/src/test/java/com/lockarhythm/tasks/TestTaskList.java @@ -5,15 +5,15 @@ import org.junit.Test; public class TestTaskList { - @Test - public void testAddTask() throws Exception { - TaskList list = new TaskList(); + @Test + public void testAddTask() throws Exception { + TaskList list = new TaskList(); - list.add("learn useful libraries in java"); - list.add("learn bazel"); - assertEquals("has correct number of items", 2, list.size()); + list.add("learn useful libraries in java"); + list.add("learn bazel"); + assertEquals("has correct number of items", 2, list.size()); - Task task = list.markAsDone(1); - assertEquals("can mark tasks as done", "learn bazel", task.getDescription()); - } + Task task = list.markAsDone(1); + assertEquals("can mark tasks as done", "learn bazel", task.getDescription()); + } } From 6c2adf84ab35c06064f31b3317a4f315a96f4e6a Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Tue, 24 Aug 2021 21:18:43 +0800 Subject: [PATCH 15/32] Update README layout Credits: https://undraw.co/license --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5bf303dd..fbe06bcc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# Duke project template + + +# Duke 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. From dd6438f0c3a910efd9fcbbbbfe5a37078f1d0d97 Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Tue, 24 Aug 2021 23:36:12 +0800 Subject: [PATCH 16/32] Add pre-commit hook. (#2) Helps to enforce code style before committing --- .gitignore | 3 +++ .pre-commit-config.yaml | 7 +++++++ .pre-commit-hooks.yaml | 8 ++++++++ README.md | 10 ++++++++++ tools/format-code.sh | 13 +++++++++++++ 5 files changed, 41 insertions(+) create mode 100644 .pre-commit-config.yaml create mode 100644 .pre-commit-hooks.yaml create mode 100644 tools/format-code.sh diff --git a/.gitignore b/.gitignore index 8256048a..b957cd08 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ text-ui-test/EXPECTED-UNIX.TXT # Ignore all bazel-* symlinks. There is no full list since this can change # based on the name of the directory bazel is cloned into. /bazel-* + +# for pre-commit git-hooks +/.cache/* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..f4b6f43b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,7 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/maltzj/google-style-precommit-hook + rev: b7e9e7fcba4a5aea463e72fe9964c14877bd8130 + hooks: + - id: google-style-java diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml new file mode 100644 index 00000000..1641d4ff --- /dev/null +++ b/.pre-commit-hooks.yaml @@ -0,0 +1,8 @@ +# Pre-commit hook to run google-format-java right before commiting +# Source: https://github.com/maltzj/google-style-precommit-hook +- id: google-style-java + name: Google Java Code Style for Java + description: Formats code in Google's Java codestyle. + entry: ./tools/format-code.sh + language: script + files: \.java$ # We don't technically need this, as the script will filter for us, but this will allow the hook to be skipped if no Java is changed. diff --git a/README.md b/README.md index fbe06bcc..5dcbfdc8 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,13 @@ Use [google-java-format](https://github.com/google/google-java-format): ```bash google-java-format --replace **/*.java # executes recursively ``` + +## Pre-commit Git Hook (Tested on macOS) +`pre-commit` is a python tool that helps to create git pre-commit hook handlers. For this project, it is used to enforce Google Java Style (via `google-java-format`) on new commits. + +To setup, ensure you have [`pre-commit`](https://pre-commit.com/#install) installed, then run: +```bash +pre-commit install +``` +This only needs to be executed once, so that `pre-commit` can generate the script and write it to `.git/hooks/pre-commit` +Thereafter, it should be executed right before every commit! diff --git a/tools/format-code.sh b/tools/format-code.sh new file mode 100644 index 00000000..da44ba37 --- /dev/null +++ b/tools/format-code.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env sh +mkdir -p .cache +cd .cache +if [ ! -f google-java-format-1.7-all-deps.jar ] +then + curl -LJO "https://github.com/google/google-java-format/releases/download/google-java-format-1.7/google-java-format-1.7-all-deps.jar" + chmod 755 google-java-format-1.7-all-deps.jar +fi +cd .. + +changed_java_files=$(git diff --cached --name-only --diff-filter=ACMR | grep ".*java$" ) +echo $changed_java_files +java -jar .cache/google-java-format-1.7-all-deps.jar --replace $changed_java_files From ee1fd2926cf00d27b613a4373c7f5a4a3d42624f Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Wed, 25 Aug 2021 00:14:16 +0800 Subject: [PATCH 17/32] Fix Level-3 mark as done. (#3) --- .../markasdone/MarkAsDoneResponder.java | 3 +-- src/main/java/com/lockarhythm/tasks/Task.java | 4 ++++ .../java/com/lockarhythm/tasks/TaskList.java | 2 +- .../markasdone/TestMarkAsDoneResponder.java | 13 ++++++++++++ .../com/lockarhythm/tasks/TestTaskList.java | 4 ++++ text-ui-test/EXPECTED.TXT | 20 +++++++++++++++++++ text-ui-test/input.txt | 3 +++ 7 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java b/src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java index 182c4230..b1b2a512 100644 --- a/src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java +++ b/src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java @@ -8,9 +8,8 @@ import java.util.regex.Pattern; public class MarkAsDoneResponder implements QueryRespondable { - private static String doneCommandPrefix = "done "; private TaskList list; - Pattern pattern = Pattern.compile(doneCommandPrefix + "(\\d+)"); + Pattern pattern = Pattern.compile("^done (\\d+)"); public MarkAsDoneResponder(TaskList list) { this.list = list; diff --git a/src/main/java/com/lockarhythm/tasks/Task.java b/src/main/java/com/lockarhythm/tasks/Task.java index 943f6749..1fae579d 100644 --- a/src/main/java/com/lockarhythm/tasks/Task.java +++ b/src/main/java/com/lockarhythm/tasks/Task.java @@ -13,6 +13,10 @@ public void toggleDone() { this.isDone = !this.isDone; } + public void setDone(boolean isDone) { + this.isDone = isDone; + } + public String getDescription() { return description; } diff --git a/src/main/java/com/lockarhythm/tasks/TaskList.java b/src/main/java/com/lockarhythm/tasks/TaskList.java index 9732d032..e4e71148 100644 --- a/src/main/java/com/lockarhythm/tasks/TaskList.java +++ b/src/main/java/com/lockarhythm/tasks/TaskList.java @@ -19,7 +19,7 @@ public void add(String description) { public Task markAsDone(int index) { Task t = list.get(index); - t.toggleDone(); + t.setDone(true); return t; } diff --git a/src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java b/src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java index c87d6f4c..bdc66037 100644 --- a/src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java +++ b/src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java @@ -46,4 +46,17 @@ public void testMarkAsDoneOnNonExistentItem() throws Exception { res = alresponder.respondTo("list"); assertTrue(res.getText().contains("1.[ ] read book\n2.[ ] return book\n")); } + + @Test + public void testMarkAsDoneParseCommand() throws Exception { + TaskList list = new TaskList(); + MarkAsDoneResponder mdresponder = new MarkAsDoneResponder(list); + AddListResponder alresponder = new AddListResponder(list); + + Response res = alresponder.respondTo("read book"); + assertEquals("added: read book", res.getText()); + + res = mdresponder.respondTo("all done 10000"); + assertNull(res); + } } diff --git a/src/test/java/com/lockarhythm/tasks/TestTaskList.java b/src/test/java/com/lockarhythm/tasks/TestTaskList.java index ce5d6025..eaefcdb6 100644 --- a/src/test/java/com/lockarhythm/tasks/TestTaskList.java +++ b/src/test/java/com/lockarhythm/tasks/TestTaskList.java @@ -15,5 +15,9 @@ public void testAddTask() throws Exception { Task task = list.markAsDone(1); assertEquals("can mark tasks as done", "learn bazel", task.getDescription()); + assertTrue("task is set to done", task.isDone()); + + task = list.markAsDone(1); + assertTrue("it's an idempotent operation", task.isDone()); } } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 69bbc0bb..d74f1d13 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -45,6 +45,26 @@ ____________________________________________________________ + ____________________________________________________________ + Nice! I've marked this task as done: + [X] return book + + ____________________________________________________________ + + ____________________________________________________________ + added: all done 1 + + ____________________________________________________________ + + ____________________________________________________________ + Here are the tasks in your list: + 1.[X] read book + 2.[X] return book + 3.[ ] buy bread + 4.[ ] all done 1 + + ____________________________________________________________ + ____________________________________________________________ Bye. Hope to see you again soon! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 2cb3cf8a..8815fb5a 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -4,4 +4,7 @@ buy bread done 1 list done 2 +done 2 +all done 1 +list bye From 437b3859d88a2e5198d48f6f0863cd469e1a2baf Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Sun, 29 Aug 2021 13:00:51 +0800 Subject: [PATCH 18/32] More classes (#4) - To prepare for GUI version of Duke, we extract out the core Application that accepts a UI implementation. In this MR, we implement TerminalUI for terminal/console interfaces. Later on, we can add GraphicalUI for JavaFX. - The duke application is viewed as a REPL, where on the evaluate step, we ask the QueryInterpreter to evaluate the query inputted by the user. - The SimpleQueryInterpreter class implements QueryInterpreter. It delegates the query to smaller QueryInterpreters, each only responding to a subset of query/commands. - QueryResponder is renamed to QueryInterpreter. - Response is renamed to Result. The Result class represents the result of a query interpretation. --- .github/workflows/workflow.yml | 8 +-- BUILD | 4 +- .../lockarhythm/application/Application.java | 28 +++++++++ .../lockarhythm/application/TerminalDuke.java | 11 ++++ .../java/com/lockarhythm/cmdline/Duke.java | 62 ------------------- .../lockarhythm/query/QueryInterpreter.java | 5 ++ .../Response.java => query/Result.java} | 10 ++- .../query/SimpleQueryInterpreter.java | 29 +++++++++ .../query/addlist/AddListResponder.java | 22 +++++++ .../lockarhythm/query/echo/EchoResponder.java | 10 +++ .../lockarhythm/query/exit/ExitResponder.java | 13 ++++ .../markasdone/MarkAsDoneResponder.java | 16 ++--- .../responders/QueryRespondable.java | 5 -- .../responders/addlist/AddListResponder.java | 22 ------- .../responders/echo/EchoResponder.java | 10 --- .../responders/exit/ExitResponder.java | 13 ---- .../java/com/lockarhythm/ui/TerminalUI.java | 30 +++++++++ src/main/java/com/lockarhythm/ui/UI.java | 13 ++++ .../TestTerminalDuke.java} | 6 +- .../addlist/TestAddListResponder.java | 10 +-- .../echo/TestEchoResponder.java | 6 +- .../exit/TestExitResponder.java | 8 +-- .../markasdone/TestMarkAsDoneResponder.java | 26 ++++---- text-ui-test/runtest.sh | 4 +- 24 files changed, 212 insertions(+), 159 deletions(-) create mode 100644 src/main/java/com/lockarhythm/application/Application.java create mode 100644 src/main/java/com/lockarhythm/application/TerminalDuke.java delete mode 100644 src/main/java/com/lockarhythm/cmdline/Duke.java create mode 100644 src/main/java/com/lockarhythm/query/QueryInterpreter.java rename src/main/java/com/lockarhythm/{responders/Response.java => query/Result.java} (58%) create mode 100644 src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java create mode 100644 src/main/java/com/lockarhythm/query/addlist/AddListResponder.java create mode 100644 src/main/java/com/lockarhythm/query/echo/EchoResponder.java create mode 100644 src/main/java/com/lockarhythm/query/exit/ExitResponder.java rename src/main/java/com/lockarhythm/{responders => query}/markasdone/MarkAsDoneResponder.java (59%) delete mode 100644 src/main/java/com/lockarhythm/responders/QueryRespondable.java delete mode 100644 src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java delete mode 100644 src/main/java/com/lockarhythm/responders/echo/EchoResponder.java delete mode 100644 src/main/java/com/lockarhythm/responders/exit/ExitResponder.java create mode 100644 src/main/java/com/lockarhythm/ui/TerminalUI.java create mode 100644 src/main/java/com/lockarhythm/ui/UI.java rename src/test/java/com/lockarhythm/{cmdline/TestDuke.java => application/TestTerminalDuke.java} (92%) rename src/test/java/com/lockarhythm/{responders => query}/addlist/TestAddListResponder.java (67%) rename src/test/java/com/lockarhythm/{responders => query}/echo/TestEchoResponder.java (68%) rename src/test/java/com/lockarhythm/{responders => query}/exit/TestExitResponder.java (68%) rename src/test/java/com/lockarhythm/{responders => query}/markasdone/TestMarkAsDoneResponder.java (71%) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 7fc94623..25cc361b 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -26,14 +26,14 @@ jobs: - name: Build JAR File run: | - "${GITHUB_WORKSPACE}/bin/bazel" build //:Duke_deploy.jar + "${GITHUB_WORKSPACE}/bin/bazel" build //:TerminalDuke_deploy.jar - name: "Move file to non-symlink directory (a workaround for some bug See: https://github.com/actions/upload-artifact/issues/92)" run: | - mv ${{ github.workspace }}/bazel-bin/Duke_deploy.jar ${{ github.workspace }}/Duke_deploy.jar + mv ${{ github.workspace }}/bazel-bin/TerminalDuke_deploy.jar ${{ github.workspace }}/TerminalDuke_deploy.jar - name: Upload JAR File uses: actions/upload-artifact@v2 with: - name: Duke_deploy.jar - path: ${{ github.workspace }}/Duke_deploy.jar + name: TerminalDuke_deploy.jar + path: ${{ github.workspace }}/TerminalDuke_deploy.jar diff --git a/BUILD b/BUILD index ac1cceed..584ee814 100644 --- a/BUILD +++ b/BUILD @@ -1,7 +1,7 @@ load("@rules_java//java:defs.bzl", "java_binary", "java_test") java_binary( - name = "Duke", + name = "TerminalDuke", srcs = glob([ "src/main/java/com/lockarhythm/**/*.java", ]), @@ -18,7 +18,7 @@ java_binary( ]), ) for class_name in [ - "TestDuke", + "TestTerminalDuke", "TestEchoResponder", "TestExitResponder", "TestAddListResponder", diff --git a/src/main/java/com/lockarhythm/application/Application.java b/src/main/java/com/lockarhythm/application/Application.java new file mode 100644 index 00000000..b53f39ac --- /dev/null +++ b/src/main/java/com/lockarhythm/application/Application.java @@ -0,0 +1,28 @@ +package com.lockarhythm.application; + +import com.lockarhythm.query.QueryInterpreter; +import com.lockarhythm.query.Result; +import com.lockarhythm.ui.UI; + +abstract class Application { + static String logo = + " \t____ _ \n" + + "\t| _ \\ _ _| | _____ \n" + + "\t| | | | | | | |/ / _ \\\n" + + "\t| |_| | |_| | < __/\n" + + "\t|____/ \\__,_|_|\\_\\___|\n"; + + public static void run(UI ui, QueryInterpreter q) { + Result result; + + ui.print("Hello I'm\n" + logo, "What can I do for you?"); + + while (ui.hasNext()) { + result = q.interpret(ui.nextLine()); + ui.print(result); + if (result.shouldExit()) { + break; + } + } + } +} diff --git a/src/main/java/com/lockarhythm/application/TerminalDuke.java b/src/main/java/com/lockarhythm/application/TerminalDuke.java new file mode 100644 index 00000000..35379709 --- /dev/null +++ b/src/main/java/com/lockarhythm/application/TerminalDuke.java @@ -0,0 +1,11 @@ +package com.lockarhythm.application; + +import com.lockarhythm.query.SimpleQueryInterpreter; +import com.lockarhythm.tasks.TaskList; +import com.lockarhythm.ui.TerminalUI; + +public class TerminalDuke extends Application { + public static void main(String[] args) { + run(new TerminalUI(), new SimpleQueryInterpreter(new TaskList())); + } +} diff --git a/src/main/java/com/lockarhythm/cmdline/Duke.java b/src/main/java/com/lockarhythm/cmdline/Duke.java deleted file mode 100644 index 8697a580..00000000 --- a/src/main/java/com/lockarhythm/cmdline/Duke.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.lockarhythm.cmdline; - -import com.lockarhythm.responders.QueryRespondable; -import com.lockarhythm.responders.Response; -import com.lockarhythm.responders.addlist.AddListResponder; -import com.lockarhythm.responders.exit.ExitResponder; -import com.lockarhythm.responders.markasdone.MarkAsDoneResponder; -import com.lockarhythm.tasks.TaskList; -import java.util.Arrays; -import java.util.Scanner; - -public class Duke { - static String logo = - " \t____ _ \n" - + "\t| _ \\ _ _| | _____ \n" - + "\t| | | | | | | |/ / _ \\\n" - + "\t| |_| | |_| | < __/\n" - + "\t|____/ \\__,_|_|\\_\\___|\n"; - - static TaskList list = new TaskList(); - - static QueryRespondable[] responders = { - new ExitResponder(), new MarkAsDoneResponder(list), new AddListResponder(list), - }; - - private static void print(String... strings) { - System.out.println("\t____________________________________________________________"); - for (String s : strings) { - s = Arrays.stream(s.split("\n")).map(x -> "\t" + x).reduce("", (x, y) -> x + y + "\n"); - System.out.println(s); - } - System.out.println("\t____________________________________________________________\n"); - } - - private static Response getResponse(String query) { - Response res = null; - for (QueryRespondable responder : responders) { - res = responder.respondTo(query); - if (res != null) { - return res; - } - } - return res; - } - - public static void main(String[] args) { - String line; - Scanner in = new Scanner(System.in); - - print("Hello I'm\n" + logo, "What can I do for you?"); - - line = in.nextLine(); - Response res = getResponse(line); - while (!res.shouldExit()) { - print(res.getText()); - line = in.nextLine(); - res = getResponse(line); - } - - print(res.getText()); - } -} diff --git a/src/main/java/com/lockarhythm/query/QueryInterpreter.java b/src/main/java/com/lockarhythm/query/QueryInterpreter.java new file mode 100644 index 00000000..4bedb6a7 --- /dev/null +++ b/src/main/java/com/lockarhythm/query/QueryInterpreter.java @@ -0,0 +1,5 @@ +package com.lockarhythm.query; + +public interface QueryInterpreter { + public Result interpret(String query); +} diff --git a/src/main/java/com/lockarhythm/responders/Response.java b/src/main/java/com/lockarhythm/query/Result.java similarity index 58% rename from src/main/java/com/lockarhythm/responders/Response.java rename to src/main/java/com/lockarhythm/query/Result.java index c9a7f9c2..0a974d05 100644 --- a/src/main/java/com/lockarhythm/responders/Response.java +++ b/src/main/java/com/lockarhythm/query/Result.java @@ -1,14 +1,18 @@ -package com.lockarhythm.responders; +package com.lockarhythm.query; -public class Response { +public class Result { private String text; private boolean shouldExit; - public Response(String text, boolean shouldExit) { + public Result(String text, boolean shouldExit) { this.text = text; this.shouldExit = shouldExit; } + public Result(String text) { + this(text, false); + } + public String getText() { return text; } diff --git a/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java b/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java new file mode 100644 index 00000000..e67ab083 --- /dev/null +++ b/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java @@ -0,0 +1,29 @@ +package com.lockarhythm.query; + +import com.lockarhythm.query.addlist.AddListResponder; +import com.lockarhythm.query.exit.ExitResponder; +import com.lockarhythm.query.markasdone.MarkAsDoneResponder; +import com.lockarhythm.tasks.TaskList; + +/** SimpleQueryInterpreter finds the first QueryInterpreter that responds a non-null result. */ +public class SimpleQueryInterpreter implements QueryInterpreter { + static QueryInterpreter[] interpreters; + + public SimpleQueryInterpreter(TaskList list) { + QueryInterpreter[] res = { + new ExitResponder(), new MarkAsDoneResponder(list), new AddListResponder(list), + }; + interpreters = res; + } + + public Result interpret(String query) { + Result res = null; + for (QueryInterpreter interpreter : interpreters) { + res = interpreter.interpret(query); + if (res != null) { + return res; + } + } + return res; + } +} diff --git a/src/main/java/com/lockarhythm/query/addlist/AddListResponder.java b/src/main/java/com/lockarhythm/query/addlist/AddListResponder.java new file mode 100644 index 00000000..31598bfd --- /dev/null +++ b/src/main/java/com/lockarhythm/query/addlist/AddListResponder.java @@ -0,0 +1,22 @@ +package com.lockarhythm.query.addlist; + +import com.lockarhythm.query.QueryInterpreter; +import com.lockarhythm.query.Result; +import com.lockarhythm.tasks.TaskList; + +public class AddListResponder implements QueryInterpreter { + private TaskList list; + + public AddListResponder(TaskList list) { + this.list = list; + } + + public Result interpret(String query) { + if (query.equals("list")) { + return new Result("Here are the tasks in your list:\n" + list.toString()); + } + // by default, adds the given query. + list.add(query); + return new Result("added: " + query); + } +} diff --git a/src/main/java/com/lockarhythm/query/echo/EchoResponder.java b/src/main/java/com/lockarhythm/query/echo/EchoResponder.java new file mode 100644 index 00000000..7b6f8a32 --- /dev/null +++ b/src/main/java/com/lockarhythm/query/echo/EchoResponder.java @@ -0,0 +1,10 @@ +package com.lockarhythm.query.echo; + +import com.lockarhythm.query.QueryInterpreter; +import com.lockarhythm.query.Result; + +public class EchoResponder implements QueryInterpreter { + public Result interpret(String query) { + return new Result(query); + } +} diff --git a/src/main/java/com/lockarhythm/query/exit/ExitResponder.java b/src/main/java/com/lockarhythm/query/exit/ExitResponder.java new file mode 100644 index 00000000..452edcd3 --- /dev/null +++ b/src/main/java/com/lockarhythm/query/exit/ExitResponder.java @@ -0,0 +1,13 @@ +package com.lockarhythm.query.exit; + +import com.lockarhythm.query.QueryInterpreter; +import com.lockarhythm.query.Result; + +public class ExitResponder implements QueryInterpreter { + public Result interpret(String query) { + if (query.equals("bye")) { + return new Result("Bye. Hope to see you again soon!", true); + } + return null; + } +} diff --git a/src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java b/src/main/java/com/lockarhythm/query/markasdone/MarkAsDoneResponder.java similarity index 59% rename from src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java rename to src/main/java/com/lockarhythm/query/markasdone/MarkAsDoneResponder.java index b1b2a512..55875739 100644 --- a/src/main/java/com/lockarhythm/responders/markasdone/MarkAsDoneResponder.java +++ b/src/main/java/com/lockarhythm/query/markasdone/MarkAsDoneResponder.java @@ -1,13 +1,13 @@ -package com.lockarhythm.responders.markasdone; +package com.lockarhythm.query.markasdone; -import com.lockarhythm.responders.QueryRespondable; -import com.lockarhythm.responders.Response; +import com.lockarhythm.query.QueryInterpreter; +import com.lockarhythm.query.Result; import com.lockarhythm.tasks.Task; import com.lockarhythm.tasks.TaskList; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class MarkAsDoneResponder implements QueryRespondable { +public class MarkAsDoneResponder implements QueryInterpreter { private TaskList list; Pattern pattern = Pattern.compile("^done (\\d+)"); @@ -15,16 +15,16 @@ public MarkAsDoneResponder(TaskList list) { this.list = list; } - public Response respondTo(String query) { + public Result interpret(String query) { Matcher matcher = pattern.matcher(query); if (matcher.find()) { int i = Integer.parseInt(matcher.group(1)); if (i > 0 && i <= list.size()) { Task task = list.markAsDone(i - 1); - return new Response("Nice! I've marked this task as done:\n\t" + task.toString(), false); + return new Result("Nice! I've marked this task as done:\n\t" + task.toString()); } - return new Response( - String.format("Item %d is not on the list. I cannot mark it as done!", i), false); + return new Result( + String.format("Item %d is not on the list. I cannot mark it as done!", i)); } return null; } diff --git a/src/main/java/com/lockarhythm/responders/QueryRespondable.java b/src/main/java/com/lockarhythm/responders/QueryRespondable.java deleted file mode 100644 index 2501b113..00000000 --- a/src/main/java/com/lockarhythm/responders/QueryRespondable.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.lockarhythm.responders; - -public interface QueryRespondable { - Response respondTo(String query); -} diff --git a/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java b/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java deleted file mode 100644 index 6253ca51..00000000 --- a/src/main/java/com/lockarhythm/responders/addlist/AddListResponder.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.lockarhythm.responders.addlist; - -import com.lockarhythm.responders.QueryRespondable; -import com.lockarhythm.responders.Response; -import com.lockarhythm.tasks.TaskList; - -public class AddListResponder implements QueryRespondable { - private TaskList list; - - public AddListResponder(TaskList list) { - this.list = list; - } - - public Response respondTo(String query) { - if (query.equals("list")) { - return new Response("Here are the tasks in your list:\n" + list.toString(), false); - } - // by default, adds the given query. - list.add(query); - return new Response("added: " + query, false); - } -} diff --git a/src/main/java/com/lockarhythm/responders/echo/EchoResponder.java b/src/main/java/com/lockarhythm/responders/echo/EchoResponder.java deleted file mode 100644 index 214acc5f..00000000 --- a/src/main/java/com/lockarhythm/responders/echo/EchoResponder.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.lockarhythm.responders.echo; - -import com.lockarhythm.responders.QueryRespondable; -import com.lockarhythm.responders.Response; - -public class EchoResponder implements QueryRespondable { - public Response respondTo(String query) { - return new Response(query, false); - } -} diff --git a/src/main/java/com/lockarhythm/responders/exit/ExitResponder.java b/src/main/java/com/lockarhythm/responders/exit/ExitResponder.java deleted file mode 100644 index 0206732b..00000000 --- a/src/main/java/com/lockarhythm/responders/exit/ExitResponder.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.lockarhythm.responders.exit; - -import com.lockarhythm.responders.QueryRespondable; -import com.lockarhythm.responders.Response; - -public class ExitResponder implements QueryRespondable { - public Response respondTo(String query) { - if (query.equals("bye")) { - return new Response("Bye. Hope to see you again soon!", true); - } - return null; - } -} diff --git a/src/main/java/com/lockarhythm/ui/TerminalUI.java b/src/main/java/com/lockarhythm/ui/TerminalUI.java new file mode 100644 index 00000000..ba7025ff --- /dev/null +++ b/src/main/java/com/lockarhythm/ui/TerminalUI.java @@ -0,0 +1,30 @@ +package com.lockarhythm.ui; + +import com.lockarhythm.query.Result; +import java.util.Arrays; +import java.util.Scanner; + +public class TerminalUI implements UI { + private static Scanner in = new Scanner(System.in); + + public String nextLine() { + return in.nextLine(); + } + + public boolean hasNext() { + return in.hasNext(); + } + + public void print(String... strings) { + System.out.println("\t____________________________________________________________"); + for (String s : strings) { + s = Arrays.stream(s.split("\n")).map(x -> "\t" + x).reduce("", (x, y) -> x + y + "\n"); + System.out.println(s); + } + System.out.println("\t____________________________________________________________\n"); + } + + public void print(Result res) { + print(res.getText()); + } +} diff --git a/src/main/java/com/lockarhythm/ui/UI.java b/src/main/java/com/lockarhythm/ui/UI.java new file mode 100644 index 00000000..3a65d437 --- /dev/null +++ b/src/main/java/com/lockarhythm/ui/UI.java @@ -0,0 +1,13 @@ +package com.lockarhythm.ui; + +import com.lockarhythm.query.Result; + +public interface UI { + public String nextLine(); + + public boolean hasNext(); + + public void print(String... strings); + + public void print(Result res); +} diff --git a/src/test/java/com/lockarhythm/cmdline/TestDuke.java b/src/test/java/com/lockarhythm/application/TestTerminalDuke.java similarity index 92% rename from src/test/java/com/lockarhythm/cmdline/TestDuke.java rename to src/test/java/com/lockarhythm/application/TestTerminalDuke.java index a2d69327..6c2c11e2 100644 --- a/src/test/java/com/lockarhythm/cmdline/TestDuke.java +++ b/src/test/java/com/lockarhythm/application/TestTerminalDuke.java @@ -1,4 +1,4 @@ -package com.lockarhythm.cmdline; +package com.lockarhythm.application; import static org.junit.Assert.*; @@ -8,7 +8,7 @@ import java.io.PrintStream; import org.junit.Test; -public class TestDuke { +public class TestTerminalDuke { private final PrintStream standardOut = System.out; private final InputStream standardIn = System.in; private final ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -20,7 +20,7 @@ public void testMeetsLevel2() throws Exception { System.setIn(in); System.setOut(new PrintStream(out)); - Duke.main(null); + TerminalDuke.main(null); String output = out.toString().trim(); assertFalse(output.isEmpty()); diff --git a/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java b/src/test/java/com/lockarhythm/query/addlist/TestAddListResponder.java similarity index 67% rename from src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java rename to src/test/java/com/lockarhythm/query/addlist/TestAddListResponder.java index 8e2d0cdb..98e5cb05 100644 --- a/src/test/java/com/lockarhythm/responders/addlist/TestAddListResponder.java +++ b/src/test/java/com/lockarhythm/query/addlist/TestAddListResponder.java @@ -1,8 +1,8 @@ -package com.lockarhythm.responders.addlist; +package com.lockarhythm.query.addlist; import static org.junit.Assert.*; -import com.lockarhythm.responders.Response; +import com.lockarhythm.query.Result; import com.lockarhythm.tasks.TaskList; import org.junit.Test; @@ -11,13 +11,13 @@ public class TestAddListResponder { public void testAddsItemsAndLists() throws Exception { AddListResponder responder = new AddListResponder(new TaskList()); - Response res = responder.respondTo("read book"); + Result res = responder.interpret("read book"); assertEquals("added: read book", res.getText()); - res = responder.respondTo("return book"); + res = responder.interpret("return book"); assertEquals("added: return book", res.getText()); - res = responder.respondTo("list"); + res = responder.interpret("list"); assertTrue(res.getText().contains("1.[ ] read book\n2.[ ] return book\n")); } } diff --git a/src/test/java/com/lockarhythm/responders/echo/TestEchoResponder.java b/src/test/java/com/lockarhythm/query/echo/TestEchoResponder.java similarity index 68% rename from src/test/java/com/lockarhythm/responders/echo/TestEchoResponder.java rename to src/test/java/com/lockarhythm/query/echo/TestEchoResponder.java index e51c34a6..a528908a 100644 --- a/src/test/java/com/lockarhythm/responders/echo/TestEchoResponder.java +++ b/src/test/java/com/lockarhythm/query/echo/TestEchoResponder.java @@ -1,15 +1,15 @@ -package com.lockarhythm.responders.echo; +package com.lockarhythm.query.echo; import static org.junit.Assert.*; -import com.lockarhythm.responders.Response; +import com.lockarhythm.query.Result; import org.junit.Test; public class TestEchoResponder { @Test public void testEchosBackText() throws Exception { EchoResponder responder = new EchoResponder(); - Response res = responder.respondTo("hello"); + Result res = responder.interpret("hello"); assertEquals("hello", res.getText()); assertFalse(res.shouldExit()); diff --git a/src/test/java/com/lockarhythm/responders/exit/TestExitResponder.java b/src/test/java/com/lockarhythm/query/exit/TestExitResponder.java similarity index 68% rename from src/test/java/com/lockarhythm/responders/exit/TestExitResponder.java rename to src/test/java/com/lockarhythm/query/exit/TestExitResponder.java index b3f18bd3..da25c270 100644 --- a/src/test/java/com/lockarhythm/responders/exit/TestExitResponder.java +++ b/src/test/java/com/lockarhythm/query/exit/TestExitResponder.java @@ -1,15 +1,15 @@ -package com.lockarhythm.responders.exit; +package com.lockarhythm.query.exit; import static org.junit.Assert.*; -import com.lockarhythm.responders.Response; +import com.lockarhythm.query.Result; import org.junit.Test; public class TestExitResponder { @Test public void testExitsOnKeyword() throws Exception { ExitResponder responder = new ExitResponder(); - Response res = responder.respondTo("bye"); + Result res = responder.interpret("bye"); assertTrue(res.shouldExit()); } @@ -17,7 +17,7 @@ public void testExitsOnKeyword() throws Exception { @Test public void testNullOnNonkeyword() throws Exception { ExitResponder responder = new ExitResponder(); - Response res = responder.respondTo("hello"); + Result res = responder.interpret("hello"); assertNull(res); } diff --git a/src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java b/src/test/java/com/lockarhythm/query/markasdone/TestMarkAsDoneResponder.java similarity index 71% rename from src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java rename to src/test/java/com/lockarhythm/query/markasdone/TestMarkAsDoneResponder.java index bdc66037..d82a784d 100644 --- a/src/test/java/com/lockarhythm/responders/markasdone/TestMarkAsDoneResponder.java +++ b/src/test/java/com/lockarhythm/query/markasdone/TestMarkAsDoneResponder.java @@ -1,9 +1,9 @@ -package com.lockarhythm.responders.markasdone; +package com.lockarhythm.query.markasdone; import static org.junit.Assert.*; -import com.lockarhythm.responders.Response; -import com.lockarhythm.responders.addlist.AddListResponder; +import com.lockarhythm.query.Result; +import com.lockarhythm.query.addlist.AddListResponder; import com.lockarhythm.tasks.TaskList; import org.junit.Test; @@ -14,17 +14,17 @@ public void testMarkAsDoneHappyPath() throws Exception { MarkAsDoneResponder mdresponder = new MarkAsDoneResponder(list); AddListResponder alresponder = new AddListResponder(list); - Response res = alresponder.respondTo("read book"); + Result res = alresponder.interpret("read book"); assertEquals("added: read book", res.getText()); - res = alresponder.respondTo("return book"); + res = alresponder.interpret("return book"); assertEquals("added: return book", res.getText()); - res = mdresponder.respondTo("done 1"); + res = mdresponder.interpret("done 1"); assertTrue(res.getText().contains("Nice! I've marked this task as done")); assertTrue(res.getText().contains("read book")); - res = alresponder.respondTo("list"); + res = alresponder.interpret("list"); assertTrue(res.getText().contains("1.[X] read book\n2.[ ] return book\n")); } @@ -34,16 +34,16 @@ public void testMarkAsDoneOnNonExistentItem() throws Exception { MarkAsDoneResponder mdresponder = new MarkAsDoneResponder(list); AddListResponder alresponder = new AddListResponder(list); - Response res = alresponder.respondTo("read book"); + Result res = alresponder.interpret("read book"); assertEquals("added: read book", res.getText()); - res = alresponder.respondTo("return book"); + res = alresponder.interpret("return book"); assertEquals("added: return book", res.getText()); - res = mdresponder.respondTo("done 10000"); + res = mdresponder.interpret("done 10000"); assertTrue(res.getText().contains("Item 10000 is not on the list. I cannot mark it as done!")); - res = alresponder.respondTo("list"); + res = alresponder.interpret("list"); assertTrue(res.getText().contains("1.[ ] read book\n2.[ ] return book\n")); } @@ -53,10 +53,10 @@ public void testMarkAsDoneParseCommand() throws Exception { MarkAsDoneResponder mdresponder = new MarkAsDoneResponder(list); AddListResponder alresponder = new AddListResponder(list); - Response res = alresponder.respondTo("read book"); + Result res = alresponder.interpret("read book"); assertEquals("added: read book", res.getText()); - res = mdresponder.respondTo("all done 10000"); + res = mdresponder.interpret("all done 10000"); assertNull(res); } } diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index 220caaa7..c69a991f 100755 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -13,14 +13,14 @@ then fi # compile the code into the bin folder, terminates if error occurred -if ! bazel build //:Duke +if ! bazel build //:TerminalDuke then echo "********** BUILD FAILURE **********" exit 1 fi # run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -../bazel-bin/Duke < input.txt > ACTUAL.TXT +../bazel-bin/TerminalDuke < input.txt > ACTUAL.TXT # convert to UNIX format cp EXPECTED.TXT EXPECTED-UNIX.TXT From 0605449c50a6f586fa06b12518a18804d2b90156 Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Tue, 31 Aug 2021 20:59:06 +0800 Subject: [PATCH 19/32] Implement Level-4, A-Inheritance. Implement partially Level-5. (#5) * Implement Level-4, A-Inheritance. Implement partially Level-5. * Fix tests. * Deprecate list.add method. --- BUILD | 4 +- .../lockarhythm/application/Application.java | 12 ++++-- .../lockarhythm/application/TerminalDuke.java | 2 +- .../lockarhythm/query/AddedTaskResult.java | 22 ++++++++++ .../query/RegexQueryInterpreter.java | 33 +++++++++++++++ .../query/SimpleQueryInterpreter.java | 14 +++++-- .../query/deadline/DeadlineResponder.java | 24 +++++++++++ .../query/event/EventResponder.java | 24 +++++++++++ .../ListResponder.java} | 11 +++-- .../query/markasdone/MarkAsDoneResponder.java | 29 ++++++------- .../lockarhythm/query/todo/TodoResponder.java | 24 +++++++++++ .../com/lockarhythm/tasks/DeadlineTask.java | 20 +++++++++ .../java/com/lockarhythm/tasks/EventTask.java | 20 +++++++++ src/main/java/com/lockarhythm/tasks/Task.java | 6 ++- .../java/com/lockarhythm/tasks/TaskList.java | 18 +++++++- .../java/com/lockarhythm/tasks/TodoTask.java | 12 ++++++ .../java/com/lockarhythm/ui/TerminalUI.java | 2 +- .../application/TestTerminalDuke.java | 10 ++--- .../query/addlist/TestAddListResponder.java | 23 ----------- .../query/list/TestListResponder.java | 21 ++++++++++ .../markasdone/TestMarkAsDoneResponder.java | 39 ++++++------------ .../com/lockarhythm/tasks/TestTaskList.java | 4 +- .../{TestTask.java => TestTodoTask.java} | 4 +- text-ui-test/EXPECTED.TXT | 41 ++++++++++++------- text-ui-test/input.txt | 9 ++-- 25 files changed, 315 insertions(+), 113 deletions(-) create mode 100644 src/main/java/com/lockarhythm/query/AddedTaskResult.java create mode 100644 src/main/java/com/lockarhythm/query/RegexQueryInterpreter.java create mode 100644 src/main/java/com/lockarhythm/query/deadline/DeadlineResponder.java create mode 100644 src/main/java/com/lockarhythm/query/event/EventResponder.java rename src/main/java/com/lockarhythm/query/{addlist/AddListResponder.java => list/ListResponder.java} (58%) create mode 100644 src/main/java/com/lockarhythm/query/todo/TodoResponder.java create mode 100644 src/main/java/com/lockarhythm/tasks/DeadlineTask.java create mode 100644 src/main/java/com/lockarhythm/tasks/EventTask.java create mode 100644 src/main/java/com/lockarhythm/tasks/TodoTask.java delete mode 100644 src/test/java/com/lockarhythm/query/addlist/TestAddListResponder.java create mode 100644 src/test/java/com/lockarhythm/query/list/TestListResponder.java rename src/test/java/com/lockarhythm/tasks/{TestTask.java => TestTodoTask.java} (74%) diff --git a/BUILD b/BUILD index 584ee814..ff824a2b 100644 --- a/BUILD +++ b/BUILD @@ -21,9 +21,9 @@ java_binary( "TestTerminalDuke", "TestEchoResponder", "TestExitResponder", - "TestAddListResponder", + "TestListResponder", "TestMarkAsDoneResponder", - "TestTask", + "TestTodoTask", "TestTaskList", ] ] diff --git a/src/main/java/com/lockarhythm/application/Application.java b/src/main/java/com/lockarhythm/application/Application.java index b53f39ac..0f5db708 100644 --- a/src/main/java/com/lockarhythm/application/Application.java +++ b/src/main/java/com/lockarhythm/application/Application.java @@ -18,10 +18,14 @@ public static void run(UI ui, QueryInterpreter q) { ui.print("Hello I'm\n" + logo, "What can I do for you?"); while (ui.hasNext()) { - result = q.interpret(ui.nextLine()); - ui.print(result); - if (result.shouldExit()) { - break; + try { + result = q.interpret(ui.nextLine()); + ui.print(result); + if (result.shouldExit()) { + break; + } + } catch (NullPointerException e) { + ui.print("Sorry, I don't understand that yet!"); } } } diff --git a/src/main/java/com/lockarhythm/application/TerminalDuke.java b/src/main/java/com/lockarhythm/application/TerminalDuke.java index 35379709..6cd3823d 100644 --- a/src/main/java/com/lockarhythm/application/TerminalDuke.java +++ b/src/main/java/com/lockarhythm/application/TerminalDuke.java @@ -4,7 +4,7 @@ import com.lockarhythm.tasks.TaskList; import com.lockarhythm.ui.TerminalUI; -public class TerminalDuke extends Application { +final class TerminalDuke extends Application { public static void main(String[] args) { run(new TerminalUI(), new SimpleQueryInterpreter(new TaskList())); } diff --git a/src/main/java/com/lockarhythm/query/AddedTaskResult.java b/src/main/java/com/lockarhythm/query/AddedTaskResult.java new file mode 100644 index 00000000..dd000a56 --- /dev/null +++ b/src/main/java/com/lockarhythm/query/AddedTaskResult.java @@ -0,0 +1,22 @@ +package com.lockarhythm.query; + +import com.lockarhythm.tasks.Task; +import com.lockarhythm.tasks.TaskList; + +public class AddedTaskResult extends Result { + private Task task; + private int size; // the max size on add + + public AddedTaskResult(Task task, int size) { + super(task.toString()); + this.task = task; + this.size = size; + } + + @Override + public String getText() { + return String.format( + "Got it. I've added this task:\n\t%s\nNow you have %d tasks in the list.", + task, size); + } +} diff --git a/src/main/java/com/lockarhythm/query/RegexQueryInterpreter.java b/src/main/java/com/lockarhythm/query/RegexQueryInterpreter.java new file mode 100644 index 00000000..8c5484f2 --- /dev/null +++ b/src/main/java/com/lockarhythm/query/RegexQueryInterpreter.java @@ -0,0 +1,33 @@ +package com.lockarhythm.query; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class RegexQueryInterpreter implements QueryInterpreter { + private Pattern pattern; + + public RegexQueryInterpreter() { + pattern = Pattern.compile(commandRegex()); + } + + public Result interpret(String query) { + Matcher matcher = pattern.matcher(query); + if (matcher.find()) { + return onMatch(toGroupStrings(matcher)); + } + return null; + } + + private String[] toGroupStrings(Matcher matcher) { + String[] groups = new String[matcher.groupCount()+1]; + for (int i = 0; i <= matcher.groupCount(); i++) { + groups[i] = matcher.group(i); + } + return groups; + } + + // onMatch is called when regexp pattern matches. The regexp capturing groups is passed as arguments. + public abstract Result onMatch(String[] groups); + + protected abstract String commandRegex(); +} diff --git a/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java b/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java index e67ab083..3bd098f1 100644 --- a/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java +++ b/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java @@ -1,17 +1,25 @@ package com.lockarhythm.query; -import com.lockarhythm.query.addlist.AddListResponder; +import com.lockarhythm.query.list.ListResponder; +import com.lockarhythm.query.deadline.DeadlineResponder; +import com.lockarhythm.query.event.EventResponder; import com.lockarhythm.query.exit.ExitResponder; import com.lockarhythm.query.markasdone.MarkAsDoneResponder; +import com.lockarhythm.query.todo.TodoResponder; import com.lockarhythm.tasks.TaskList; /** SimpleQueryInterpreter finds the first QueryInterpreter that responds a non-null result. */ -public class SimpleQueryInterpreter implements QueryInterpreter { +public final class SimpleQueryInterpreter implements QueryInterpreter { static QueryInterpreter[] interpreters; public SimpleQueryInterpreter(TaskList list) { QueryInterpreter[] res = { - new ExitResponder(), new MarkAsDoneResponder(list), new AddListResponder(list), + new ExitResponder(), + new MarkAsDoneResponder(list), + new TodoResponder(list), + new ListResponder(list), + new DeadlineResponder(list), + new EventResponder(list), }; interpreters = res; } diff --git a/src/main/java/com/lockarhythm/query/deadline/DeadlineResponder.java b/src/main/java/com/lockarhythm/query/deadline/DeadlineResponder.java new file mode 100644 index 00000000..3bfabe5a --- /dev/null +++ b/src/main/java/com/lockarhythm/query/deadline/DeadlineResponder.java @@ -0,0 +1,24 @@ +package com.lockarhythm.query.deadline; + +import com.lockarhythm.query.RegexQueryInterpreter; +import com.lockarhythm.query.Result; +import com.lockarhythm.query.AddedTaskResult; +import com.lockarhythm.tasks.Task; +import com.lockarhythm.tasks.TaskList; + +public class DeadlineResponder extends RegexQueryInterpreter { + private TaskList list; + + public DeadlineResponder(TaskList list) { + this.list = list; + } + + protected String commandRegex() { + return "^deadline (.+) \\/by (.+)"; + } + + public Result onMatch(String[] groups) { + Task task = list.addDeadlineTask(groups[1], groups[2]); + return new AddedTaskResult(task, list.size()); + } +} diff --git a/src/main/java/com/lockarhythm/query/event/EventResponder.java b/src/main/java/com/lockarhythm/query/event/EventResponder.java new file mode 100644 index 00000000..d69a73bf --- /dev/null +++ b/src/main/java/com/lockarhythm/query/event/EventResponder.java @@ -0,0 +1,24 @@ +package com.lockarhythm.query.event; + +import com.lockarhythm.query.RegexQueryInterpreter; +import com.lockarhythm.query.Result; +import com.lockarhythm.query.AddedTaskResult; +import com.lockarhythm.tasks.Task; +import com.lockarhythm.tasks.TaskList; + +public class EventResponder extends RegexQueryInterpreter { + private TaskList list; + + public EventResponder(TaskList list) { + this.list = list; + } + + protected String commandRegex() { + return "^event (.+) \\/at (.+)"; + } + + public Result onMatch(String[] groups) { + Task task = list.addEventTask(groups[1], groups[2]); + return new AddedTaskResult(task, list.size()); + } +} diff --git a/src/main/java/com/lockarhythm/query/addlist/AddListResponder.java b/src/main/java/com/lockarhythm/query/list/ListResponder.java similarity index 58% rename from src/main/java/com/lockarhythm/query/addlist/AddListResponder.java rename to src/main/java/com/lockarhythm/query/list/ListResponder.java index 31598bfd..13077612 100644 --- a/src/main/java/com/lockarhythm/query/addlist/AddListResponder.java +++ b/src/main/java/com/lockarhythm/query/list/ListResponder.java @@ -1,13 +1,14 @@ -package com.lockarhythm.query.addlist; +package com.lockarhythm.query.list; import com.lockarhythm.query.QueryInterpreter; import com.lockarhythm.query.Result; +import com.lockarhythm.tasks.Task; import com.lockarhythm.tasks.TaskList; -public class AddListResponder implements QueryInterpreter { +public final class ListResponder implements QueryInterpreter { private TaskList list; - public AddListResponder(TaskList list) { + public ListResponder(TaskList list) { this.list = list; } @@ -15,8 +16,6 @@ public Result interpret(String query) { if (query.equals("list")) { return new Result("Here are the tasks in your list:\n" + list.toString()); } - // by default, adds the given query. - list.add(query); - return new Result("added: " + query); + return null; } } diff --git a/src/main/java/com/lockarhythm/query/markasdone/MarkAsDoneResponder.java b/src/main/java/com/lockarhythm/query/markasdone/MarkAsDoneResponder.java index 55875739..c9c330b4 100644 --- a/src/main/java/com/lockarhythm/query/markasdone/MarkAsDoneResponder.java +++ b/src/main/java/com/lockarhythm/query/markasdone/MarkAsDoneResponder.java @@ -1,31 +1,28 @@ package com.lockarhythm.query.markasdone; -import com.lockarhythm.query.QueryInterpreter; +import com.lockarhythm.query.RegexQueryInterpreter; import com.lockarhythm.query.Result; import com.lockarhythm.tasks.Task; import com.lockarhythm.tasks.TaskList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -public class MarkAsDoneResponder implements QueryInterpreter { +public class MarkAsDoneResponder extends RegexQueryInterpreter { private TaskList list; - Pattern pattern = Pattern.compile("^done (\\d+)"); public MarkAsDoneResponder(TaskList list) { this.list = list; } - public Result interpret(String query) { - Matcher matcher = pattern.matcher(query); - if (matcher.find()) { - int i = Integer.parseInt(matcher.group(1)); - if (i > 0 && i <= list.size()) { - Task task = list.markAsDone(i - 1); - return new Result("Nice! I've marked this task as done:\n\t" + task.toString()); - } - return new Result( - String.format("Item %d is not on the list. I cannot mark it as done!", i)); + protected String commandRegex() { + return "^done (\\d+)"; + } + + public Result onMatch(String[] groups) { + int i = Integer.parseInt(groups[1]); + try { + Task task = list.markAsDone(i - 1); + return new Result("Nice! I've marked this task as done:\n\t" + task.toString()); + } catch(IndexOutOfBoundsException e) { + return new Result(String.format("Item %d is not on the list. I cannot mark it as done!", i)); } - return null; } } diff --git a/src/main/java/com/lockarhythm/query/todo/TodoResponder.java b/src/main/java/com/lockarhythm/query/todo/TodoResponder.java new file mode 100644 index 00000000..1725ffc0 --- /dev/null +++ b/src/main/java/com/lockarhythm/query/todo/TodoResponder.java @@ -0,0 +1,24 @@ +package com.lockarhythm.query.todo; + +import com.lockarhythm.query.RegexQueryInterpreter; +import com.lockarhythm.query.AddedTaskResult; +import com.lockarhythm.query.Result; +import com.lockarhythm.tasks.Task; +import com.lockarhythm.tasks.TaskList; + +public class TodoResponder extends RegexQueryInterpreter { + private TaskList list; + + public TodoResponder(TaskList list) { + this.list = list; + } + + protected String commandRegex() { + return "^todo (.+)"; + } + + public Result onMatch(String[] groups) { + Task task = list.addTodoTask(groups[1]); + return new AddedTaskResult(task, list.size()); + } +} diff --git a/src/main/java/com/lockarhythm/tasks/DeadlineTask.java b/src/main/java/com/lockarhythm/tasks/DeadlineTask.java new file mode 100644 index 00000000..0973e7ef --- /dev/null +++ b/src/main/java/com/lockarhythm/tasks/DeadlineTask.java @@ -0,0 +1,20 @@ +package com.lockarhythm.tasks; + +final class DeadlineTask extends Task { + private String by; + + public DeadlineTask(String description, String by) { + super(description); + this.by = by; + } + + @Override + protected String getTaskTypeIcon() { + return "D"; + } + + @Override + public String toString() { + return String.format("%s (by: %s)", super.toString(), by); + } +} diff --git a/src/main/java/com/lockarhythm/tasks/EventTask.java b/src/main/java/com/lockarhythm/tasks/EventTask.java new file mode 100644 index 00000000..a2910830 --- /dev/null +++ b/src/main/java/com/lockarhythm/tasks/EventTask.java @@ -0,0 +1,20 @@ +package com.lockarhythm.tasks; + +final class EventTask extends Task { + private String at; + + public EventTask(String description, String at) { + super(description); + this.at = at; + } + + @Override + protected String getTaskTypeIcon() { + return "E"; + } + + @Override + public String toString() { + return String.format("%s (at: %s)", super.toString(), at); + } +} diff --git a/src/main/java/com/lockarhythm/tasks/Task.java b/src/main/java/com/lockarhythm/tasks/Task.java index 1fae579d..fb554905 100644 --- a/src/main/java/com/lockarhythm/tasks/Task.java +++ b/src/main/java/com/lockarhythm/tasks/Task.java @@ -1,6 +1,6 @@ package com.lockarhythm.tasks; -public class Task { +public abstract class Task { private String description; private boolean isDone; @@ -29,8 +29,10 @@ private String getDoneIcon() { return isDone ? "X" : " "; } + protected abstract String getTaskTypeIcon(); + @Override public String toString() { - return String.format("[%s] %s", getDoneIcon(), description); + return String.format("[%s][%s] %s", getTaskTypeIcon(), getDoneIcon(), description); } } diff --git a/src/main/java/com/lockarhythm/tasks/TaskList.java b/src/main/java/com/lockarhythm/tasks/TaskList.java index e4e71148..6b13544f 100644 --- a/src/main/java/com/lockarhythm/tasks/TaskList.java +++ b/src/main/java/com/lockarhythm/tasks/TaskList.java @@ -13,8 +13,22 @@ public int size() { return list.size(); } - public void add(String description) { - list.add(new Task(description)); + public TodoTask addTodoTask(String description) { + TodoTask task = new TodoTask(description); + list.add(task); + return task; + } + + public DeadlineTask addDeadlineTask(String description, String by) { + DeadlineTask task = new DeadlineTask(description, by); + list.add(task); + return task; + } + + public EventTask addEventTask(String description, String at) { + EventTask task = new EventTask(description, at); + list.add(task); + return task; } public Task markAsDone(int index) { diff --git a/src/main/java/com/lockarhythm/tasks/TodoTask.java b/src/main/java/com/lockarhythm/tasks/TodoTask.java new file mode 100644 index 00000000..38b1e9cc --- /dev/null +++ b/src/main/java/com/lockarhythm/tasks/TodoTask.java @@ -0,0 +1,12 @@ +package com.lockarhythm.tasks; + +public final class TodoTask extends Task { + public TodoTask(String description) { + super(description); + } + + @Override + protected String getTaskTypeIcon() { + return "T"; + } +} diff --git a/src/main/java/com/lockarhythm/ui/TerminalUI.java b/src/main/java/com/lockarhythm/ui/TerminalUI.java index ba7025ff..4c60aad5 100644 --- a/src/main/java/com/lockarhythm/ui/TerminalUI.java +++ b/src/main/java/com/lockarhythm/ui/TerminalUI.java @@ -4,7 +4,7 @@ import java.util.Arrays; import java.util.Scanner; -public class TerminalUI implements UI { +public final class TerminalUI implements UI { private static Scanner in = new Scanner(System.in); public String nextLine() { diff --git a/src/test/java/com/lockarhythm/application/TestTerminalDuke.java b/src/test/java/com/lockarhythm/application/TestTerminalDuke.java index 6c2c11e2..11d2f9b5 100644 --- a/src/test/java/com/lockarhythm/application/TestTerminalDuke.java +++ b/src/test/java/com/lockarhythm/application/TestTerminalDuke.java @@ -13,7 +13,7 @@ public class TestTerminalDuke { private final InputStream standardIn = System.in; private final ByteArrayOutputStream out = new ByteArrayOutputStream(); private final ByteArrayInputStream in = - new ByteArrayInputStream("read book\nreturn book\nlist\nbye".getBytes()); + new ByteArrayInputStream("todo read book\ntodo return book\nlist\nbye".getBytes()); @Test public void testMeetsLevel2() throws Exception { @@ -24,10 +24,10 @@ public void testMeetsLevel2() throws Exception { String output = out.toString().trim(); assertFalse(output.isEmpty()); - assertTrue("it adds the 1st item", output.contains("added: read book")); - assertTrue("it adds the 2nd item", output.contains("added: return book")); - assertTrue("it lists the items", output.contains("1.[ ] read book")); - assertTrue("it lists the items", output.contains("2.[ ] return book")); + assertTrue("it adds the 1st item", output.contains("Now you have 1 tasks in the list")); + assertTrue("it adds the 2nd item", output.contains("Now you have 2 tasks in the list")); + assertTrue("it lists the items", output.contains("1.[T][ ] read book")); + assertTrue("it lists the items", output.contains("2.[T][ ] return book")); assertTrue("it exits", output.contains("Bye. Hope to see you again soon!")); System.setOut(standardOut); diff --git a/src/test/java/com/lockarhythm/query/addlist/TestAddListResponder.java b/src/test/java/com/lockarhythm/query/addlist/TestAddListResponder.java deleted file mode 100644 index 98e5cb05..00000000 --- a/src/test/java/com/lockarhythm/query/addlist/TestAddListResponder.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.lockarhythm.query.addlist; - -import static org.junit.Assert.*; - -import com.lockarhythm.query.Result; -import com.lockarhythm.tasks.TaskList; -import org.junit.Test; - -public class TestAddListResponder { - @Test - public void testAddsItemsAndLists() throws Exception { - AddListResponder responder = new AddListResponder(new TaskList()); - - Result res = responder.interpret("read book"); - assertEquals("added: read book", res.getText()); - - res = responder.interpret("return book"); - assertEquals("added: return book", res.getText()); - - res = responder.interpret("list"); - assertTrue(res.getText().contains("1.[ ] read book\n2.[ ] return book\n")); - } -} diff --git a/src/test/java/com/lockarhythm/query/list/TestListResponder.java b/src/test/java/com/lockarhythm/query/list/TestListResponder.java new file mode 100644 index 00000000..03f7c638 --- /dev/null +++ b/src/test/java/com/lockarhythm/query/list/TestListResponder.java @@ -0,0 +1,21 @@ +package com.lockarhythm.query.list; + +import static org.junit.Assert.*; + +import com.lockarhythm.query.Result; +import com.lockarhythm.tasks.TaskList; +import org.junit.Test; + +public class TestListResponder { + @Test + public void testAddsItemsAndLists() throws Exception { + TaskList list = new TaskList(); + ListResponder responder = new ListResponder(list); + + list.addTodoTask("read book"); + list.addTodoTask("return book"); + + Result res = responder.interpret("list"); + assertTrue(res.getText().contains("1.[T][ ] read book\n2.[T][ ] return book\n")); + } +} diff --git a/src/test/java/com/lockarhythm/query/markasdone/TestMarkAsDoneResponder.java b/src/test/java/com/lockarhythm/query/markasdone/TestMarkAsDoneResponder.java index d82a784d..9655d533 100644 --- a/src/test/java/com/lockarhythm/query/markasdone/TestMarkAsDoneResponder.java +++ b/src/test/java/com/lockarhythm/query/markasdone/TestMarkAsDoneResponder.java @@ -3,7 +3,6 @@ import static org.junit.Assert.*; import com.lockarhythm.query.Result; -import com.lockarhythm.query.addlist.AddListResponder; import com.lockarhythm.tasks.TaskList; import org.junit.Test; @@ -11,52 +10,40 @@ public class TestMarkAsDoneResponder { @Test public void testMarkAsDoneHappyPath() throws Exception { TaskList list = new TaskList(); - MarkAsDoneResponder mdresponder = new MarkAsDoneResponder(list); - AddListResponder alresponder = new AddListResponder(list); + MarkAsDoneResponder responder = new MarkAsDoneResponder(list); - Result res = alresponder.interpret("read book"); - assertEquals("added: read book", res.getText()); + list.addTodoTask("read book"); + list.addTodoTask("return book"); - res = alresponder.interpret("return book"); - assertEquals("added: return book", res.getText()); - - res = mdresponder.interpret("done 1"); + Result res = responder.interpret("done 1"); assertTrue(res.getText().contains("Nice! I've marked this task as done")); assertTrue(res.getText().contains("read book")); - res = alresponder.interpret("list"); - assertTrue(res.getText().contains("1.[X] read book\n2.[ ] return book\n")); + assertTrue(list.toString().contains("1.[T][X] read book\n2.[T][ ] return book\n")); } @Test public void testMarkAsDoneOnNonExistentItem() throws Exception { TaskList list = new TaskList(); - MarkAsDoneResponder mdresponder = new MarkAsDoneResponder(list); - AddListResponder alresponder = new AddListResponder(list); - - Result res = alresponder.interpret("read book"); - assertEquals("added: read book", res.getText()); + MarkAsDoneResponder responder = new MarkAsDoneResponder(list); - res = alresponder.interpret("return book"); - assertEquals("added: return book", res.getText()); + list.addTodoTask("read book"); + list.addTodoTask("return book"); - res = mdresponder.interpret("done 10000"); + Result res = responder.interpret("done 10000"); assertTrue(res.getText().contains("Item 10000 is not on the list. I cannot mark it as done!")); - res = alresponder.interpret("list"); - assertTrue(res.getText().contains("1.[ ] read book\n2.[ ] return book\n")); + assertTrue(list.toString().contains("1.[T][ ] read book\n2.[T][ ] return book\n")); } @Test public void testMarkAsDoneParseCommand() throws Exception { TaskList list = new TaskList(); - MarkAsDoneResponder mdresponder = new MarkAsDoneResponder(list); - AddListResponder alresponder = new AddListResponder(list); + MarkAsDoneResponder responder = new MarkAsDoneResponder(list); - Result res = alresponder.interpret("read book"); - assertEquals("added: read book", res.getText()); + list.addTodoTask("read book"); - res = mdresponder.interpret("all done 10000"); + Result res = responder.interpret("all done 10000"); assertNull(res); } } diff --git a/src/test/java/com/lockarhythm/tasks/TestTaskList.java b/src/test/java/com/lockarhythm/tasks/TestTaskList.java index eaefcdb6..5c98bd5c 100644 --- a/src/test/java/com/lockarhythm/tasks/TestTaskList.java +++ b/src/test/java/com/lockarhythm/tasks/TestTaskList.java @@ -9,8 +9,8 @@ public class TestTaskList { public void testAddTask() throws Exception { TaskList list = new TaskList(); - list.add("learn useful libraries in java"); - list.add("learn bazel"); + list.addTodoTask("learn useful libraries in java"); + list.addTodoTask("learn bazel"); assertEquals("has correct number of items", 2, list.size()); Task task = list.markAsDone(1); diff --git a/src/test/java/com/lockarhythm/tasks/TestTask.java b/src/test/java/com/lockarhythm/tasks/TestTodoTask.java similarity index 74% rename from src/test/java/com/lockarhythm/tasks/TestTask.java rename to src/test/java/com/lockarhythm/tasks/TestTodoTask.java index f70783c3..a9fb2591 100644 --- a/src/test/java/com/lockarhythm/tasks/TestTask.java +++ b/src/test/java/com/lockarhythm/tasks/TestTodoTask.java @@ -4,10 +4,10 @@ import org.junit.Test; -public class TestTask { +public class TestTodoTask { @Test public void testToggleDone() throws Exception { - Task task = new Task("learn java well"); + TodoTask task = new TodoTask("learn java well"); task.toggleDone(); assertTrue("task should be done", task.isDone()); diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index d74f1d13..8411ae79 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -11,57 +11,70 @@ ____________________________________________________________ ____________________________________________________________ - added: read book + Got it. I've added this task: + [T][ ] borrow book + Now you have 1 tasks in the list. ____________________________________________________________ ____________________________________________________________ - added: return book + Got it. I've added this task: + [D][ ] return book (by: 10pm) + Now you have 2 tasks in the list. ____________________________________________________________ ____________________________________________________________ - added: buy bread + Got it. I've added this task: + [E][ ] buy bread (at: Sunday 11pm) + Now you have 3 tasks in the list. ____________________________________________________________ ____________________________________________________________ Nice! I've marked this task as done: - [X] read book + [T][X] borrow book ____________________________________________________________ ____________________________________________________________ Here are the tasks in your list: - 1.[X] read book - 2.[ ] return book - 3.[ ] buy bread + 1.[T][X] borrow book + 2.[D][ ] return book (by: 10pm) + 3.[E][ ] buy bread (at: Sunday 11pm) ____________________________________________________________ ____________________________________________________________ Nice! I've marked this task as done: - [X] return book + [D][X] return book (by: 10pm) ____________________________________________________________ ____________________________________________________________ Nice! I've marked this task as done: - [X] return book + [D][X] return book (by: 10pm) ____________________________________________________________ ____________________________________________________________ - added: all done 1 + Got it. I've added this task: + [T][ ] done 1 + Now you have 4 tasks in the list. ____________________________________________________________ ____________________________________________________________ Here are the tasks in your list: - 1.[X] read book - 2.[X] return book - 3.[ ] buy bread - 4.[ ] all done 1 + 1.[T][X] borrow book + 2.[D][X] return book (by: 10pm) + 3.[E][ ] buy bread (at: Sunday 11pm) + 4.[T][ ] done 1 + + ____________________________________________________________ + + ____________________________________________________________ + Sorry, I don't understand that yet! ____________________________________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 8815fb5a..23404b0a 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,10 +1,11 @@ -read book -return book -buy bread +todo borrow book +deadline return book /by 10pm +event buy bread /at Sunday 11pm done 1 list done 2 done 2 -all done 1 +todo done 1 list +a-command-that-duke-doesnt-know bye From 99c1e95d727e49d405602b7a97ed2bb6b93643ae Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Tue, 31 Aug 2021 21:17:43 +0800 Subject: [PATCH 20/32] Implement Level-5 and A-Exceptions. Throw DukeException instead of returning null. (#6) --- src/main/java/com/lockarhythm/application/Application.java | 3 ++- src/main/java/com/lockarhythm/query/DukeException.java | 4 ++++ src/main/java/com/lockarhythm/query/QueryInterpreter.java | 2 +- .../java/com/lockarhythm/query/SimpleQueryInterpreter.java | 7 +++---- 4 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/lockarhythm/query/DukeException.java diff --git a/src/main/java/com/lockarhythm/application/Application.java b/src/main/java/com/lockarhythm/application/Application.java index 0f5db708..ce327aee 100644 --- a/src/main/java/com/lockarhythm/application/Application.java +++ b/src/main/java/com/lockarhythm/application/Application.java @@ -1,5 +1,6 @@ package com.lockarhythm.application; +import com.lockarhythm.query.DukeException; import com.lockarhythm.query.QueryInterpreter; import com.lockarhythm.query.Result; import com.lockarhythm.ui.UI; @@ -24,7 +25,7 @@ public static void run(UI ui, QueryInterpreter q) { if (result.shouldExit()) { break; } - } catch (NullPointerException e) { + } catch (DukeException e) { ui.print("Sorry, I don't understand that yet!"); } } diff --git a/src/main/java/com/lockarhythm/query/DukeException.java b/src/main/java/com/lockarhythm/query/DukeException.java new file mode 100644 index 00000000..a6756f5e --- /dev/null +++ b/src/main/java/com/lockarhythm/query/DukeException.java @@ -0,0 +1,4 @@ +package com.lockarhythm.query; + +public class DukeException extends Exception { +} diff --git a/src/main/java/com/lockarhythm/query/QueryInterpreter.java b/src/main/java/com/lockarhythm/query/QueryInterpreter.java index 4bedb6a7..9d856d31 100644 --- a/src/main/java/com/lockarhythm/query/QueryInterpreter.java +++ b/src/main/java/com/lockarhythm/query/QueryInterpreter.java @@ -1,5 +1,5 @@ package com.lockarhythm.query; public interface QueryInterpreter { - public Result interpret(String query); + public Result interpret(String query) throws DukeException; } diff --git a/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java b/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java index 3bd098f1..a8a5342c 100644 --- a/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java +++ b/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java @@ -24,14 +24,13 @@ public SimpleQueryInterpreter(TaskList list) { interpreters = res; } - public Result interpret(String query) { - Result res = null; + public Result interpret(String query) throws DukeException { for (QueryInterpreter interpreter : interpreters) { - res = interpreter.interpret(query); + Result res = interpreter.interpret(query); if (res != null) { return res; } } - return res; + throw new DukeException(); } } From 52c3d42d71499adc4e2070c8d943d2bcdcc4b3ea Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Tue, 31 Aug 2021 23:24:47 +0800 Subject: [PATCH 21/32] Add gif demo in README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 5dcbfdc8..e260776c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ 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. +## Demo +![output](https://user-images.githubusercontent.com/88638946/131530452-cbe5ab59-9e43-4c08-affe-f9fdf2c75427.gif) + + ## Build Prerequisites: Have `bazel` installed. From 27456d4cce74ca24731517df09f41efbafaeebd6 Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Tue, 21 Sep 2021 20:10:33 +0800 Subject: [PATCH 22/32] Implement delete Level-6 (#8) --- .../lockarhythm/query/DeletedTaskResult.java | 20 +++++++++++++ .../query/SimpleQueryInterpreter.java | 2 ++ .../query/delete/DeleteResponder.java | 29 +++++++++++++++++++ .../java/com/lockarhythm/tasks/TaskList.java | 4 +++ text-ui-test/EXPECTED.TXT | 12 ++++++++ text-ui-test/input.txt | 2 ++ 6 files changed, 69 insertions(+) create mode 100644 src/main/java/com/lockarhythm/query/DeletedTaskResult.java create mode 100644 src/main/java/com/lockarhythm/query/delete/DeleteResponder.java diff --git a/src/main/java/com/lockarhythm/query/DeletedTaskResult.java b/src/main/java/com/lockarhythm/query/DeletedTaskResult.java new file mode 100644 index 00000000..128ac60a --- /dev/null +++ b/src/main/java/com/lockarhythm/query/DeletedTaskResult.java @@ -0,0 +1,20 @@ +package com.lockarhythm.query; + +import com.lockarhythm.tasks.Task; + +public class DeletedTaskResult extends Result { + private Task task; + private int size; // new size after deletion + + public DeletedTaskResult(Task task, int size) { + super(task.toString()); + this.task = task; + this.size = size; + } + + @Override + public String getText() { + return String.format( + "Noted. I've removed this task:\n\t%s\nNow you have %d tasks in the list.", task, size); + } +} diff --git a/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java b/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java index a8a5342c..64d03e4f 100644 --- a/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java +++ b/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java @@ -6,6 +6,7 @@ import com.lockarhythm.query.exit.ExitResponder; import com.lockarhythm.query.markasdone.MarkAsDoneResponder; import com.lockarhythm.query.todo.TodoResponder; +import com.lockarhythm.query.delete.DeleteResponder; import com.lockarhythm.tasks.TaskList; /** SimpleQueryInterpreter finds the first QueryInterpreter that responds a non-null result. */ @@ -20,6 +21,7 @@ public SimpleQueryInterpreter(TaskList list) { new ListResponder(list), new DeadlineResponder(list), new EventResponder(list), + new DeleteResponder(list), }; interpreters = res; } diff --git a/src/main/java/com/lockarhythm/query/delete/DeleteResponder.java b/src/main/java/com/lockarhythm/query/delete/DeleteResponder.java new file mode 100644 index 00000000..835e04fc --- /dev/null +++ b/src/main/java/com/lockarhythm/query/delete/DeleteResponder.java @@ -0,0 +1,29 @@ +package com.lockarhythm.query.delete; + +import com.lockarhythm.query.RegexQueryInterpreter; +import com.lockarhythm.query.Result; +import com.lockarhythm.query.DeletedTaskResult; +import com.lockarhythm.tasks.Task; +import com.lockarhythm.tasks.TaskList; + +public class DeleteResponder extends RegexQueryInterpreter { + private TaskList list; + + public DeleteResponder(TaskList list) { + this.list = list; + } + + protected String commandRegex() { + return "^delete (\\d+)"; + } + + public Result onMatch(String[] groups) { + int index = Integer.parseInt(groups[1]); + try { + Task task = list.deleteTask(index - 1); + return new DeletedTaskResult(task, list.size()); + } catch (IndexOutOfBoundsException e) { + return new Result(String.format("Item %d is not on the list. I cannot delete it!", index)); + } + } +} diff --git a/src/main/java/com/lockarhythm/tasks/TaskList.java b/src/main/java/com/lockarhythm/tasks/TaskList.java index 6b13544f..19b46e5e 100644 --- a/src/main/java/com/lockarhythm/tasks/TaskList.java +++ b/src/main/java/com/lockarhythm/tasks/TaskList.java @@ -37,6 +37,10 @@ public Task markAsDone(int index) { return t; } + public Task deleteTask(int index) { + return list.remove(index); + } + @Override public String toString() { StringBuilder s = new StringBuilder(); diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 8411ae79..846162e0 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -78,6 +78,18 @@ ____________________________________________________________ + ____________________________________________________________ + Item 0 is not on the list. I cannot delete it! + + ____________________________________________________________ + + ____________________________________________________________ + Noted. I've removed this task: + [D][X] return book (by: 10pm) + Now you have 3 tasks in the list. + + ____________________________________________________________ + ____________________________________________________________ Bye. Hope to see you again soon! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 23404b0a..a497bac8 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -8,4 +8,6 @@ done 2 todo done 1 list a-command-that-duke-doesnt-know +delete 0 +delete 2 bye From 38456e3fcec14dbac753f6c8ab8aef5bf9617065 Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Sun, 17 Oct 2021 21:53:24 +0800 Subject: [PATCH 23/32] Save tasks (#9) * WIP. Add Storage. * Working state. * And fixed test. * Basic implementation and cleanup. --- BUILD | 9 ++- WORKSPACE | 3 +- .../lockarhythm/application/Application.java | 7 ++- .../lockarhythm/application/TerminalDuke.java | 11 +++- .../java/com/lockarhythm/storage/Storage.java | 59 +++++++++++++++++++ .../com/lockarhythm/tasks/DeadlineTask.java | 1 + .../java/com/lockarhythm/tasks/EventTask.java | 1 + src/main/java/com/lockarhythm/tasks/Task.java | 4 +- .../lockarhythm/tasks/TaskDeserializer.java | 37 ++++++++++++ .../java/com/lockarhythm/tasks/TaskList.java | 6 +- .../java/com/lockarhythm/tasks/TodoTask.java | 2 + 11 files changed, 134 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/lockarhythm/storage/Storage.java create mode 100644 src/main/java/com/lockarhythm/tasks/TaskDeserializer.java diff --git a/BUILD b/BUILD index ff824a2b..8b8c4a6c 100644 --- a/BUILD +++ b/BUILD @@ -5,7 +5,10 @@ java_binary( srcs = glob([ "src/main/java/com/lockarhythm/**/*.java", ]), - deps = ["@maven//:org_apache_commons_commons_lang3"] + deps = [ + "@maven//:org_apache_commons_commons_lang3", + "@maven//:com_google_code_gson_gson", + ] ) [ @@ -16,6 +19,10 @@ java_binary( "src/test/java/com/lockarhythm/**/*.java", "src/main/java/com/lockarhythm/**/*.java", ]), + deps = [ + "@maven//:org_apache_commons_commons_lang3", + "@maven//:com_google_code_gson_gson", + ] ) for class_name in [ "TestTerminalDuke", diff --git a/WORKSPACE b/WORKSPACE index 9784f207..960a9371 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -15,7 +15,8 @@ load("@rules_jvm_external//:defs.bzl", "maven_install") maven_install( artifacts = [ #"com.google.guava:guava:21.0", - "org.apache.commons:commons-lang3:3.11" + "org.apache.commons:commons-lang3:3.11", + "com.google.code.gson:gson:2.8.8" ], repositories = [ # Private repositories are supported through HTTP Basic auth diff --git a/src/main/java/com/lockarhythm/application/Application.java b/src/main/java/com/lockarhythm/application/Application.java index ce327aee..bc803d17 100644 --- a/src/main/java/com/lockarhythm/application/Application.java +++ b/src/main/java/com/lockarhythm/application/Application.java @@ -3,7 +3,9 @@ import com.lockarhythm.query.DukeException; import com.lockarhythm.query.QueryInterpreter; import com.lockarhythm.query.Result; +import com.lockarhythm.storage.Storage; import com.lockarhythm.ui.UI; +import java.io.IOException; abstract class Application { static String logo = @@ -13,7 +15,7 @@ abstract class Application { + "\t| |_| | |_| | < __/\n" + "\t|____/ \\__,_|_|\\_\\___|\n"; - public static void run(UI ui, QueryInterpreter q) { + public static void run(UI ui, QueryInterpreter q, Storage storage) { Result result; ui.print("Hello I'm\n" + logo, "What can I do for you?"); @@ -21,10 +23,13 @@ public static void run(UI ui, QueryInterpreter q) { while (ui.hasNext()) { try { result = q.interpret(ui.nextLine()); + storage.overwrite(); ui.print(result); if (result.shouldExit()) { break; } + } catch (IOException e) { + ui.print(String.format("Sorry, I cannot save the task to file: %s", e)); } catch (DukeException e) { ui.print("Sorry, I don't understand that yet!"); } diff --git a/src/main/java/com/lockarhythm/application/TerminalDuke.java b/src/main/java/com/lockarhythm/application/TerminalDuke.java index 6cd3823d..693c6209 100644 --- a/src/main/java/com/lockarhythm/application/TerminalDuke.java +++ b/src/main/java/com/lockarhythm/application/TerminalDuke.java @@ -2,10 +2,19 @@ import com.lockarhythm.query.SimpleQueryInterpreter; import com.lockarhythm.tasks.TaskList; +import com.lockarhythm.tasks.Task; import com.lockarhythm.ui.TerminalUI; +import com.lockarhythm.storage.Storage; +import java.util.ArrayList; +import java.nio.file.Paths; +import java.nio.file.Path; final class TerminalDuke extends Application { public static void main(String[] args) { - run(new TerminalUI(), new SimpleQueryInterpreter(new TaskList())); + Path path = Paths.get(".", "tasks.json"); + Storage storage = new Storage(path.toString()); + ArrayList list = storage.load(Task.class); + storage.registerList(list); + run(new TerminalUI(), new SimpleQueryInterpreter(new TaskList(list)), storage); } } diff --git a/src/main/java/com/lockarhythm/storage/Storage.java b/src/main/java/com/lockarhythm/storage/Storage.java new file mode 100644 index 00000000..bd5ce962 --- /dev/null +++ b/src/main/java/com/lockarhythm/storage/Storage.java @@ -0,0 +1,59 @@ +package com.lockarhythm.storage; + + +import com.google.gson.GsonBuilder; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Type; + +import java.io.FileOutputStream; +import java.io.ObjectOutputStream; +import java.io.IOException; +import java.io.Serializable; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.charset.StandardCharsets; + +import com.lockarhythm.tasks.TaskDeserializer; +import com.lockarhythm.tasks.Task; + +import java.util.ArrayList; + +public class Storage { + private String filePath; + + private ArrayList list; + + public Storage(String filePath) { + this.filePath = filePath; + } + + public void registerList(ArrayList list) { + this.list = list; + } + + public ArrayList load(Class type) { + String content; + try { + content = Files.readString(Path.of(filePath), StandardCharsets.UTF_8); + + TaskDeserializer deserializer = new TaskDeserializer("_type"); + Gson gson = new GsonBuilder() + .registerTypeAdapter(Task.class, deserializer) + .create(); + Type typeOfT = TypeToken.getParameterized(ArrayList.class, type).getType(); + return gson.fromJson(content, new TypeToken>(){}.getType()); + } catch (IOException e) { + return new ArrayList(); + } + } + + public void overwrite() throws IOException { + FileOutputStream fo = new FileOutputStream(filePath); + + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + String js = gson.toJson(list); + fo.write(js.getBytes()); + } +} diff --git a/src/main/java/com/lockarhythm/tasks/DeadlineTask.java b/src/main/java/com/lockarhythm/tasks/DeadlineTask.java index 0973e7ef..030d47cd 100644 --- a/src/main/java/com/lockarhythm/tasks/DeadlineTask.java +++ b/src/main/java/com/lockarhythm/tasks/DeadlineTask.java @@ -2,6 +2,7 @@ final class DeadlineTask extends Task { private String by; + private String _type = "DEADLINE"; public DeadlineTask(String description, String by) { super(description); diff --git a/src/main/java/com/lockarhythm/tasks/EventTask.java b/src/main/java/com/lockarhythm/tasks/EventTask.java index a2910830..9b521d65 100644 --- a/src/main/java/com/lockarhythm/tasks/EventTask.java +++ b/src/main/java/com/lockarhythm/tasks/EventTask.java @@ -2,6 +2,7 @@ final class EventTask extends Task { private String at; + private String _type = "EVENT"; public EventTask(String description, String at) { super(description); diff --git a/src/main/java/com/lockarhythm/tasks/Task.java b/src/main/java/com/lockarhythm/tasks/Task.java index fb554905..6a114622 100644 --- a/src/main/java/com/lockarhythm/tasks/Task.java +++ b/src/main/java/com/lockarhythm/tasks/Task.java @@ -1,6 +1,8 @@ package com.lockarhythm.tasks; -public abstract class Task { +import java.io.Serializable; + +public abstract class Task implements Serializable { private String description; private boolean isDone; diff --git a/src/main/java/com/lockarhythm/tasks/TaskDeserializer.java b/src/main/java/com/lockarhythm/tasks/TaskDeserializer.java new file mode 100644 index 00000000..362edbc1 --- /dev/null +++ b/src/main/java/com/lockarhythm/tasks/TaskDeserializer.java @@ -0,0 +1,37 @@ +package com.lockarhythm.tasks; + +import com.google.gson.*; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +/** + * Technique credit to https://www.baeldung.com/gson-list + */ +public class TaskDeserializer implements JsonDeserializer { + private String taskTypeElementName; + private Gson gson; + private Map> taskTypeRegistry; + + public TaskDeserializer(String taskTypeElementName) { + this.taskTypeElementName = taskTypeElementName; + this.gson = new Gson(); + this.taskTypeRegistry = new HashMap<>(); + + this.registerTaskType("DEADLINE", DeadlineTask.class); + this.registerTaskType("TODO", TodoTask.class); + this.registerTaskType("EVENT", EventTask.class); + } + + public void registerTaskType(String taskTypeName, Class taskType) { + taskTypeRegistry.put(taskTypeName, taskType); + } + + public Task deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { + JsonObject taskObject = json.getAsJsonObject(); + JsonElement taskTypeElement = taskObject.get(taskTypeElementName); + + Class taskType = taskTypeRegistry.get(taskTypeElement.getAsString()); + return gson.fromJson(taskObject, taskType); + } +} diff --git a/src/main/java/com/lockarhythm/tasks/TaskList.java b/src/main/java/com/lockarhythm/tasks/TaskList.java index 19b46e5e..781a85c5 100644 --- a/src/main/java/com/lockarhythm/tasks/TaskList.java +++ b/src/main/java/com/lockarhythm/tasks/TaskList.java @@ -6,7 +6,11 @@ public class TaskList { private ArrayList list; public TaskList() { - list = new ArrayList(); + this.list = new ArrayList(); + } + + public TaskList(ArrayList list) { + this.list = list; } public int size() { diff --git a/src/main/java/com/lockarhythm/tasks/TodoTask.java b/src/main/java/com/lockarhythm/tasks/TodoTask.java index 38b1e9cc..f7e2c263 100644 --- a/src/main/java/com/lockarhythm/tasks/TodoTask.java +++ b/src/main/java/com/lockarhythm/tasks/TodoTask.java @@ -1,6 +1,8 @@ package com.lockarhythm.tasks; public final class TodoTask extends Task { + private String _type = "TODO"; + public TodoTask(String description) { super(description); } From 59884c4352631f3995e17835991f63edce54c2e8 Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Mon, 18 Oct 2021 00:10:18 +0800 Subject: [PATCH 24/32] Implement Level-8 minimum. (#10) * Implement Level-8 minimum. * Fix and add more tests. And basic date time parsing. * Store datetime. --- .../query/deadline/DeadlineResponder.java | 11 ++++++-- .../query/event/EventResponder.java | 8 ++++-- .../com/lockarhythm/tasks/DeadlineTask.java | 4 +-- .../java/com/lockarhythm/tasks/EventTask.java | 4 +-- .../java/com/lockarhythm/tasks/TaskDate.java | 18 ++++++++++++ text-ui-test/EXPECTED.TXT | 28 +++++++++++++------ text-ui-test/input.txt | 2 ++ text-ui-test/runtest.sh | 10 +++++++ 8 files changed, 67 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/lockarhythm/tasks/TaskDate.java diff --git a/src/main/java/com/lockarhythm/query/deadline/DeadlineResponder.java b/src/main/java/com/lockarhythm/query/deadline/DeadlineResponder.java index 3bfabe5a..8e6cc1bd 100644 --- a/src/main/java/com/lockarhythm/query/deadline/DeadlineResponder.java +++ b/src/main/java/com/lockarhythm/query/deadline/DeadlineResponder.java @@ -1,8 +1,8 @@ package com.lockarhythm.query.deadline; +import com.lockarhythm.query.AddedTaskResult; import com.lockarhythm.query.RegexQueryInterpreter; import com.lockarhythm.query.Result; -import com.lockarhythm.query.AddedTaskResult; import com.lockarhythm.tasks.Task; import com.lockarhythm.tasks.TaskList; @@ -18,7 +18,12 @@ protected String commandRegex() { } public Result onMatch(String[] groups) { - Task task = list.addDeadlineTask(groups[1], groups[2]); - return new AddedTaskResult(task, list.size()); + try { + Task task = list.addDeadlineTask(groups[1], groups[2]); + return new AddedTaskResult(task, list.size()); + } catch (java.time.format.DateTimeParseException e) { + return new Result( + String.format("Please give me a valid LocalDate pattern: %s", e.getMessage())); + } } } diff --git a/src/main/java/com/lockarhythm/query/event/EventResponder.java b/src/main/java/com/lockarhythm/query/event/EventResponder.java index d69a73bf..6db6f085 100644 --- a/src/main/java/com/lockarhythm/query/event/EventResponder.java +++ b/src/main/java/com/lockarhythm/query/event/EventResponder.java @@ -18,7 +18,11 @@ protected String commandRegex() { } public Result onMatch(String[] groups) { - Task task = list.addEventTask(groups[1], groups[2]); - return new AddedTaskResult(task, list.size()); + try { + Task task = list.addEventTask(groups[1], groups[2]); + return new AddedTaskResult(task, list.size()); + } catch(java.time.format.DateTimeParseException e) { + return new Result(String.format("Please give me a valid LocalDate pattern: %s", e.getMessage())); + } } } diff --git a/src/main/java/com/lockarhythm/tasks/DeadlineTask.java b/src/main/java/com/lockarhythm/tasks/DeadlineTask.java index 030d47cd..d5a1eaa9 100644 --- a/src/main/java/com/lockarhythm/tasks/DeadlineTask.java +++ b/src/main/java/com/lockarhythm/tasks/DeadlineTask.java @@ -1,12 +1,12 @@ package com.lockarhythm.tasks; final class DeadlineTask extends Task { - private String by; + private TaskDate by; private String _type = "DEADLINE"; public DeadlineTask(String description, String by) { super(description); - this.by = by; + this.by = new TaskDate(by); } @Override diff --git a/src/main/java/com/lockarhythm/tasks/EventTask.java b/src/main/java/com/lockarhythm/tasks/EventTask.java index 9b521d65..67fcc45f 100644 --- a/src/main/java/com/lockarhythm/tasks/EventTask.java +++ b/src/main/java/com/lockarhythm/tasks/EventTask.java @@ -1,12 +1,12 @@ package com.lockarhythm.tasks; final class EventTask extends Task { - private String at; + private TaskDate at; private String _type = "EVENT"; public EventTask(String description, String at) { super(description); - this.at = at; + this.at = new TaskDate(at); } @Override diff --git a/src/main/java/com/lockarhythm/tasks/TaskDate.java b/src/main/java/com/lockarhythm/tasks/TaskDate.java new file mode 100644 index 00000000..1c68a5e3 --- /dev/null +++ b/src/main/java/com/lockarhythm/tasks/TaskDate.java @@ -0,0 +1,18 @@ +package com.lockarhythm.tasks; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +class TaskDate implements Serializable { + private LocalDateTime d; + + public TaskDate(String s) { + d = LocalDateTime.parse(s); + } + + @Override + public String toString() { + return d.format(DateTimeFormatter.ofPattern("MMM d yyyy")); + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 846162e0..d499a6f7 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -17,16 +17,26 @@ ____________________________________________________________ + ____________________________________________________________ + Please give me a valid LocalDate pattern: Text '10pm' could not be parsed at index 0 + + ____________________________________________________________ + + ____________________________________________________________ + Please give me a valid LocalDate pattern: Text 'Sunday 11pm' could not be parsed at index 0 + + ____________________________________________________________ + ____________________________________________________________ Got it. I've added this task: - [D][ ] return book (by: 10pm) + [D][ ] return book (by: Dec 31 2021) Now you have 2 tasks in the list. ____________________________________________________________ ____________________________________________________________ Got it. I've added this task: - [E][ ] buy bread (at: Sunday 11pm) + [E][ ] buy bread (at: Nov 30 2021) Now you have 3 tasks in the list. ____________________________________________________________ @@ -40,20 +50,20 @@ ____________________________________________________________ Here are the tasks in your list: 1.[T][X] borrow book - 2.[D][ ] return book (by: 10pm) - 3.[E][ ] buy bread (at: Sunday 11pm) + 2.[D][ ] return book (by: Dec 31 2021) + 3.[E][ ] buy bread (at: Nov 30 2021) ____________________________________________________________ ____________________________________________________________ Nice! I've marked this task as done: - [D][X] return book (by: 10pm) + [D][X] return book (by: Dec 31 2021) ____________________________________________________________ ____________________________________________________________ Nice! I've marked this task as done: - [D][X] return book (by: 10pm) + [D][X] return book (by: Dec 31 2021) ____________________________________________________________ @@ -67,8 +77,8 @@ ____________________________________________________________ Here are the tasks in your list: 1.[T][X] borrow book - 2.[D][X] return book (by: 10pm) - 3.[E][ ] buy bread (at: Sunday 11pm) + 2.[D][X] return book (by: Dec 31 2021) + 3.[E][ ] buy bread (at: Nov 30 2021) 4.[T][ ] done 1 ____________________________________________________________ @@ -85,7 +95,7 @@ ____________________________________________________________ Noted. I've removed this task: - [D][X] return book (by: 10pm) + [D][X] return book (by: Dec 31 2021) Now you have 3 tasks in the list. ____________________________________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index a497bac8..6075af08 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,6 +1,8 @@ todo borrow book deadline return book /by 10pm event buy bread /at Sunday 11pm +deadline return book /by 2021-12-31T23:59:59 +event buy bread /at 2021-11-30T23:59:59 done 1 list done 2 diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index c69a991f..5504e370 100755 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -12,9 +12,16 @@ then rm ACTUAL.TXT fi +# delete tasks.json from previous run +if [ -e "./tasks.json" ] +then + rm tasks.json +fi + # compile the code into the bin folder, terminates if error occurred if ! bazel build //:TerminalDuke then + rm tasks.json echo "********** BUILD FAILURE **********" exit 1 fi @@ -26,6 +33,9 @@ fi cp EXPECTED.TXT EXPECTED-UNIX.TXT dos2unix ACTUAL.TXT EXPECTED-UNIX.TXT +# Clean up +rm tasks.json + # compare the output to the expected output diff ACTUAL.TXT EXPECTED-UNIX.TXT if [ $? -eq 0 ] From a8eccff79a2805adc7f5e9a680509f082cfcbde6 Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Mon, 18 Oct 2021 00:55:15 +0800 Subject: [PATCH 25/32] Improve Text UI tests. Added tests for saved serialized data. (#11) * Improve ui tests. Added serialization tests. * Fix workflow. * Remove tasks.json --- .github/workflows/workflow.yml | 6 ++++++ .gitignore | 1 + text-ui-test/EXPECTED.json | 32 ++++++++++++++++++++++++++++++++ text-ui-test/runtest.sh | 18 ++++++++++++------ 4 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 text-ui-test/EXPECTED.json diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 25cc361b..57f68586 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -24,6 +24,12 @@ jobs: run: | "${GITHUB_WORKSPACE}/bin/bazel" test //... + - name: Run UI and Serialization Tests + working-directory: ./text-ui-test + run: | + chmod +x runtest.sh + ./runtest.sh + - name: Build JAR File run: | "${GITHUB_WORKSPACE}/bin/bazel" build //:TerminalDuke_deploy.jar diff --git a/.gitignore b/.gitignore index b957cd08..d08823f6 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ src/main/resources/docs/ bin/ /text-ui-test/ACTUAL.txt +/text-ui-test/tasks.json text-ui-test/EXPECTED-UNIX.TXT # Ignore all bazel-* symlinks. There is no full list since this can change diff --git a/text-ui-test/EXPECTED.json b/text-ui-test/EXPECTED.json new file mode 100644 index 00000000..7bde0bb7 --- /dev/null +++ b/text-ui-test/EXPECTED.json @@ -0,0 +1,32 @@ +[ + { + "_type": "TODO", + "description": "borrow book", + "isDone": true + }, + { + "at": { + "d": { + "date": { + "year": 2021, + "month": 11, + "day": 30 + }, + "time": { + "hour": 23, + "minute": 59, + "second": 59, + "nano": 0 + } + } + }, + "_type": "EVENT", + "description": "buy bread", + "isDone": false + }, + { + "_type": "TODO", + "description": "done 1", + "isDone": false + } +] \ No newline at end of file diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index 5504e370..450fb1c3 100755 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -21,7 +21,6 @@ fi # compile the code into the bin folder, terminates if error occurred if ! bazel build //:TerminalDuke then - rm tasks.json echo "********** BUILD FAILURE **********" exit 1 fi @@ -33,16 +32,23 @@ fi cp EXPECTED.TXT EXPECTED-UNIX.TXT dos2unix ACTUAL.TXT EXPECTED-UNIX.TXT -# Clean up -rm tasks.json - # compare the output to the expected output diff ACTUAL.TXT EXPECTED-UNIX.TXT if [ $? -eq 0 ] then - echo "Test result: PASSED" + echo "Test result: PASSED [UI TESTS]" +else + echo "Test result: FAILED [UI TESTS]" + exit 1 +fi + +# compare the output to the expected output +diff tasks.json EXPECTED.json +if [ $? -eq 0 ] +then + echo "Test result: PASSED [SERIALIZATION TESTS]" exit 0 else - echo "Test result: FAILED" + echo "Test result: FAILED [SERIALIZATION TESTS]" exit 1 fi From 0c8a48eee4e70667a9a7d9437a07c64885ab9123 Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Sun, 24 Oct 2021 23:59:53 +0800 Subject: [PATCH 26/32] Implement C-Sort: Sort tasks by "Date" or" Done" fields (#12) * Add sorting by date or done field, with ui tests. * Use own Gson date serializers * Use a fixed bazelisk version for more deterministic builds. * Update README.md --- README.md | 7 +- .../query/SimpleQueryInterpreter.java | 2 + .../lockarhythm/query/sort/SortResponder.java | 56 ++++++++++++ .../lockarhythm/storage/GsonLocalDate.java | 21 +++++ .../storage/GsonLocalDateTime.java | 21 +++++ .../java/com/lockarhythm/storage/Storage.java | 24 +++-- .../com/lockarhythm/tasks/DeadlineTask.java | 5 ++ .../java/com/lockarhythm/tasks/EventTask.java | 5 ++ src/main/java/com/lockarhythm/tasks/Task.java | 7 ++ .../java/com/lockarhythm/tasks/TaskDate.java | 4 + .../lockarhythm/tasks/TaskDeserializer.java | 12 ++- .../java/com/lockarhythm/tasks/TaskList.java | 30 +++++++ text-ui-test/EXPECTED.TXT | 90 +++++++++++++++++++ text-ui-test/EXPECTED.json | 30 ++++--- text-ui-test/input.txt | 10 +++ text-ui-test/runtest.sh | 2 +- 16 files changed, 301 insertions(+), 25 deletions(-) create mode 100644 src/main/java/com/lockarhythm/query/sort/SortResponder.java create mode 100644 src/main/java/com/lockarhythm/storage/GsonLocalDate.java create mode 100644 src/main/java/com/lockarhythm/storage/GsonLocalDateTime.java diff --git a/README.md b/README.md index e260776c..a54cf64b 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,13 @@ Prerequisites: Have `bazel` installed. To build for local development on macOS: ```bash # bazel makes a wrapper script around the jar file. -bazel build //:Duke && ./bazel-bin/Duke +bazel build //:TerminalDuke && ./bazel-bin/TerminalDuke # or simply -bazel run //:Duke +bazel run //:TerminalDuke + +# Use bazelisk for M1 Macs (Apple Silicon) +USE_BAZEL_VERSION=ac9353fab161efae4af72e73fbb657a762b3620d bazelisk run //:TerminalDuke ``` To build jar file for deployment purposes: diff --git a/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java b/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java index 64d03e4f..417edf61 100644 --- a/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java +++ b/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java @@ -7,6 +7,7 @@ import com.lockarhythm.query.markasdone.MarkAsDoneResponder; import com.lockarhythm.query.todo.TodoResponder; import com.lockarhythm.query.delete.DeleteResponder; +import com.lockarhythm.query.sort.SortResponder; import com.lockarhythm.tasks.TaskList; /** SimpleQueryInterpreter finds the first QueryInterpreter that responds a non-null result. */ @@ -22,6 +23,7 @@ public SimpleQueryInterpreter(TaskList list) { new DeadlineResponder(list), new EventResponder(list), new DeleteResponder(list), + new SortResponder(list), }; interpreters = res; } diff --git a/src/main/java/com/lockarhythm/query/sort/SortResponder.java b/src/main/java/com/lockarhythm/query/sort/SortResponder.java new file mode 100644 index 00000000..71ae7a9b --- /dev/null +++ b/src/main/java/com/lockarhythm/query/sort/SortResponder.java @@ -0,0 +1,56 @@ +package com.lockarhythm.query.sort; + +import com.lockarhythm.query.RegexQueryInterpreter; +import com.lockarhythm.query.Result; +import com.lockarhythm.tasks.TaskList; + +public class SortResponder extends RegexQueryInterpreter { + private final static boolean DEFAULT_SORT_ORDER = true; + + private TaskList list; + + public SortResponder(TaskList list) { + this.list = list; + } + + protected String commandRegex() { + return "^sort by (date|done)( desc| asc)?$"; + } + + public Result onMatch(String[] groups) { + String sortKey = groups[1]; + boolean isAscending = parseIsAscending(groups[2]); + + TaskList sortedList = sortBy(sortKey, isAscending); + + return new Result(String.format("Ok! I have sorted the tasks by %s in %s order!\n%s", sortKey, isAscending ? "ascending" : "descending", sortedList)); + } + + private boolean parseIsAscending(String value) { + if (value == null) { + return DEFAULT_SORT_ORDER; + } + String s = value.trim(); + if (s.equals("desc")) { + return false; + } else if (s.equals("asc")) { + return true; + } else { + return DEFAULT_SORT_ORDER; + } + } + + /** + * Sorts by the `sortKey` in the order given by `isAscending`. + */ + private TaskList sortBy(String sortKey, boolean isAscending) { + switch(sortKey) { + case "done": + return list.sortByDone(isAscending); + case "date": + return list.sortByTaskDate(isAscending); + default: + }; + return list.sortByTaskDate(isAscending); + } +} diff --git a/src/main/java/com/lockarhythm/storage/GsonLocalDate.java b/src/main/java/com/lockarhythm/storage/GsonLocalDate.java new file mode 100644 index 00000000..7eb94a47 --- /dev/null +++ b/src/main/java/com/lockarhythm/storage/GsonLocalDate.java @@ -0,0 +1,21 @@ +package com.lockarhythm.storage; + +import com.google.gson.*; + +import java.lang.reflect.Type; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +public class GsonLocalDate implements JsonSerializer, JsonDeserializer { + + @Override + public LocalDate deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + String ldtString = jsonElement.getAsString(); + return LocalDate.parse(ldtString,DateTimeFormatter.ISO_LOCAL_DATE); + } + + @Override + public JsonElement serialize(LocalDate localDate, Type type, JsonSerializationContext jsonSerializationContext) { + return new JsonPrimitive(localDate.format(DateTimeFormatter.ISO_LOCAL_DATE)); + } +} diff --git a/src/main/java/com/lockarhythm/storage/GsonLocalDateTime.java b/src/main/java/com/lockarhythm/storage/GsonLocalDateTime.java new file mode 100644 index 00000000..95878f75 --- /dev/null +++ b/src/main/java/com/lockarhythm/storage/GsonLocalDateTime.java @@ -0,0 +1,21 @@ +package com.lockarhythm.storage; + +import com.google.gson.*; + +import java.lang.reflect.Type; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class GsonLocalDateTime implements JsonSerializer, JsonDeserializer { + + @Override + public LocalDateTime deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + String ldtString = jsonElement.getAsString(); + return LocalDateTime.parse(ldtString,DateTimeFormatter.ISO_LOCAL_DATE_TIME); + } + + @Override + public JsonElement serialize(LocalDateTime localDateTime, Type type, JsonSerializationContext jsonSerializationContext) { + return new JsonPrimitive(localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); + } +} diff --git a/src/main/java/com/lockarhythm/storage/Storage.java b/src/main/java/com/lockarhythm/storage/Storage.java index bd5ce962..052d2e04 100644 --- a/src/main/java/com/lockarhythm/storage/Storage.java +++ b/src/main/java/com/lockarhythm/storage/Storage.java @@ -5,6 +5,8 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; +import java.time.LocalDateTime; +import java.time.LocalDate; import java.io.FileOutputStream; import java.io.ObjectOutputStream; @@ -24,8 +26,17 @@ public class Storage { private ArrayList list; + private Gson gson; + public Storage(String filePath) { this.filePath = filePath; + + gson = new GsonBuilder() + .registerTypeAdapter(Task.class, new TaskDeserializer("_type")) + .registerTypeAdapter(LocalDateTime.class, new GsonLocalDateTime()) + .registerTypeAdapter(LocalDate.class, new GsonLocalDate()) + .setPrettyPrinting() + .create(); } public void registerList(ArrayList list) { @@ -37,12 +48,12 @@ public ArrayList load(Class type) { try { content = Files.readString(Path.of(filePath), StandardCharsets.UTF_8); - TaskDeserializer deserializer = new TaskDeserializer("_type"); - Gson gson = new GsonBuilder() - .registerTypeAdapter(Task.class, deserializer) - .create(); Type typeOfT = TypeToken.getParameterized(ArrayList.class, type).getType(); - return gson.fromJson(content, new TypeToken>(){}.getType()); + ArrayList deserialized = gson.fromJson(content, new TypeToken>(){}.getType()); + if (deserialized == null) { + return new ArrayList(); + } + return deserialized; } catch (IOException e) { return new ArrayList(); } @@ -50,9 +61,6 @@ public ArrayList load(Class type) { public void overwrite() throws IOException { FileOutputStream fo = new FileOutputStream(filePath); - - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - String js = gson.toJson(list); fo.write(js.getBytes()); } diff --git a/src/main/java/com/lockarhythm/tasks/DeadlineTask.java b/src/main/java/com/lockarhythm/tasks/DeadlineTask.java index d5a1eaa9..d6239988 100644 --- a/src/main/java/com/lockarhythm/tasks/DeadlineTask.java +++ b/src/main/java/com/lockarhythm/tasks/DeadlineTask.java @@ -9,6 +9,11 @@ public DeadlineTask(String description, String by) { this.by = new TaskDate(by); } + @Override + public TaskDate getTaskDate() { + return by; + } + @Override protected String getTaskTypeIcon() { return "D"; diff --git a/src/main/java/com/lockarhythm/tasks/EventTask.java b/src/main/java/com/lockarhythm/tasks/EventTask.java index 67fcc45f..60b8c5b2 100644 --- a/src/main/java/com/lockarhythm/tasks/EventTask.java +++ b/src/main/java/com/lockarhythm/tasks/EventTask.java @@ -9,6 +9,11 @@ public EventTask(String description, String at) { this.at = new TaskDate(at); } + @Override + public TaskDate getTaskDate() { + return at; + } + @Override protected String getTaskTypeIcon() { return "E"; diff --git a/src/main/java/com/lockarhythm/tasks/Task.java b/src/main/java/com/lockarhythm/tasks/Task.java index 6a114622..e140b65f 100644 --- a/src/main/java/com/lockarhythm/tasks/Task.java +++ b/src/main/java/com/lockarhythm/tasks/Task.java @@ -3,6 +3,9 @@ import java.io.Serializable; public abstract class Task implements Serializable { + private transient static String DEFAULT_TASK_DATE_VALUE = "3000-12-31T23:59:59"; + private transient static TaskDate DEFAULT_TASK_DATE = new TaskDate(DEFAULT_TASK_DATE_VALUE); + private String description; private boolean isDone; @@ -27,6 +30,10 @@ public boolean isDone() { return isDone; } + public TaskDate getTaskDate() { + return DEFAULT_TASK_DATE; + } + private String getDoneIcon() { return isDone ? "X" : " "; } diff --git a/src/main/java/com/lockarhythm/tasks/TaskDate.java b/src/main/java/com/lockarhythm/tasks/TaskDate.java index 1c68a5e3..2dd89b18 100644 --- a/src/main/java/com/lockarhythm/tasks/TaskDate.java +++ b/src/main/java/com/lockarhythm/tasks/TaskDate.java @@ -11,6 +11,10 @@ public TaskDate(String s) { d = LocalDateTime.parse(s); } + public static int compare(TaskDate x, TaskDate y) { + return x.d.compareTo(y.d); + } + @Override public String toString() { return d.format(DateTimeFormatter.ofPattern("MMM d yyyy")); diff --git a/src/main/java/com/lockarhythm/tasks/TaskDeserializer.java b/src/main/java/com/lockarhythm/tasks/TaskDeserializer.java index 362edbc1..a00f4b0b 100644 --- a/src/main/java/com/lockarhythm/tasks/TaskDeserializer.java +++ b/src/main/java/com/lockarhythm/tasks/TaskDeserializer.java @@ -5,6 +5,12 @@ import java.util.HashMap; import java.util.Map; +import com.lockarhythm.storage.GsonLocalDateTime; +import com.lockarhythm.storage.GsonLocalDate; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + /** * Technique credit to https://www.baeldung.com/gson-list */ @@ -14,8 +20,12 @@ public class TaskDeserializer implements JsonDeserializer { private Map> taskTypeRegistry; public TaskDeserializer(String taskTypeElementName) { + this.gson = new GsonBuilder() + .registerTypeAdapter(LocalDateTime.class, new GsonLocalDateTime()) + .registerTypeAdapter(LocalDate.class, new GsonLocalDate()) + .create(); + this.taskTypeElementName = taskTypeElementName; - this.gson = new Gson(); this.taskTypeRegistry = new HashMap<>(); this.registerTaskType("DEADLINE", DeadlineTask.class); diff --git a/src/main/java/com/lockarhythm/tasks/TaskList.java b/src/main/java/com/lockarhythm/tasks/TaskList.java index 781a85c5..299a7dd0 100644 --- a/src/main/java/com/lockarhythm/tasks/TaskList.java +++ b/src/main/java/com/lockarhythm/tasks/TaskList.java @@ -1,6 +1,8 @@ package com.lockarhythm.tasks; import java.util.ArrayList; +import java.util.Comparator; +import java.util.Collections; public class TaskList { private ArrayList list; @@ -45,6 +47,34 @@ public Task deleteTask(int index) { return list.remove(index); } + /** + * Returns a copy of TaskList sorted by the task date. + */ + public TaskList sortByTaskDate(boolean isAscending) { + ArrayList copy = new ArrayList(list); + Collections.sort(copy, (a, b) -> { + if (isAscending) { + return TaskDate.compare(a.getTaskDate(), b.getTaskDate()); + } + return TaskDate.compare(b.getTaskDate(), a.getTaskDate()); + }); + return new TaskList(copy); + } + + /** + * Returns a copy of TaskList sorted by the "done" field. + */ + public TaskList sortByDone(boolean isAscending) { + ArrayList copy = new ArrayList(list); + Collections.sort(copy, (a, b) -> { + if (isAscending) { + return Boolean.compare(b.isDone(), a.isDone()); + } + return Boolean.compare(a.isDone(), b.isDone()); + }); + return new TaskList(copy); + } + @Override public String toString() { StringBuilder s = new StringBuilder(); diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index d499a6f7..1f1854db 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -100,6 +100,96 @@ ____________________________________________________________ + ____________________________________________________________ + Got it. I've added this task: + [E][ ] this task is so 2008 (at: Oct 31 2008) + Now you have 4 tasks in the list. + + ____________________________________________________________ + + ____________________________________________________________ + Got it. I've added this task: + [E][ ] this task is so 2000-and-late (at: Dec 31 2000) + Now you have 5 tasks in the list. + + ____________________________________________________________ + + ____________________________________________________________ + Ok! I have sorted the tasks by date in ascending order! + 1.[E][ ] this task is so 2000-and-late (at: Dec 31 2000) + 2.[E][ ] this task is so 2008 (at: Oct 31 2008) + 3.[E][ ] buy bread (at: Nov 30 2021) + 4.[T][X] borrow book + 5.[T][ ] done 1 + + ____________________________________________________________ + + ____________________________________________________________ + Ok! I have sorted the tasks by date in descending order! + 1.[T][X] borrow book + 2.[T][ ] done 1 + 3.[E][ ] buy bread (at: Nov 30 2021) + 4.[E][ ] this task is so 2008 (at: Oct 31 2008) + 5.[E][ ] this task is so 2000-and-late (at: Dec 31 2000) + + ____________________________________________________________ + + ____________________________________________________________ + Here are the tasks in your list: + 1.[T][X] borrow book + 2.[E][ ] buy bread (at: Nov 30 2021) + 3.[T][ ] done 1 + 4.[E][ ] this task is so 2008 (at: Oct 31 2008) + 5.[E][ ] this task is so 2000-and-late (at: Dec 31 2000) + + ____________________________________________________________ + + ____________________________________________________________ + Nice! I've marked this task as done: + [T][X] done 1 + + ____________________________________________________________ + + ____________________________________________________________ + Ok! I have sorted the tasks by done in ascending order! + 1.[T][X] borrow book + 2.[T][X] done 1 + 3.[E][ ] buy bread (at: Nov 30 2021) + 4.[E][ ] this task is so 2008 (at: Oct 31 2008) + 5.[E][ ] this task is so 2000-and-late (at: Dec 31 2000) + + ____________________________________________________________ + + ____________________________________________________________ + Ok! I have sorted the tasks by done in ascending order! + 1.[T][X] borrow book + 2.[T][X] done 1 + 3.[E][ ] buy bread (at: Nov 30 2021) + 4.[E][ ] this task is so 2008 (at: Oct 31 2008) + 5.[E][ ] this task is so 2000-and-late (at: Dec 31 2000) + + ____________________________________________________________ + + ____________________________________________________________ + Ok! I have sorted the tasks by done in descending order! + 1.[E][ ] buy bread (at: Nov 30 2021) + 2.[E][ ] this task is so 2008 (at: Oct 31 2008) + 3.[E][ ] this task is so 2000-and-late (at: Dec 31 2000) + 4.[T][X] borrow book + 5.[T][X] done 1 + + ____________________________________________________________ + + ____________________________________________________________ + Here are the tasks in your list: + 1.[T][X] borrow book + 2.[E][ ] buy bread (at: Nov 30 2021) + 3.[T][X] done 1 + 4.[E][ ] this task is so 2008 (at: Oct 31 2008) + 5.[E][ ] this task is so 2000-and-late (at: Dec 31 2000) + + ____________________________________________________________ + ____________________________________________________________ Bye. Hope to see you again soon! diff --git a/text-ui-test/EXPECTED.json b/text-ui-test/EXPECTED.json index 7bde0bb7..19d2198c 100644 --- a/text-ui-test/EXPECTED.json +++ b/text-ui-test/EXPECTED.json @@ -6,19 +6,7 @@ }, { "at": { - "d": { - "date": { - "year": 2021, - "month": 11, - "day": 30 - }, - "time": { - "hour": 23, - "minute": 59, - "second": 59, - "nano": 0 - } - } + "d": "2021-11-30T23:59:59" }, "_type": "EVENT", "description": "buy bread", @@ -27,6 +15,22 @@ { "_type": "TODO", "description": "done 1", + "isDone": true + }, + { + "at": { + "d": "2008-10-31T23:59:59" + }, + "_type": "EVENT", + "description": "this task is so 2008", + "isDone": false + }, + { + "at": { + "d": "2000-12-31T23:59:59" + }, + "_type": "EVENT", + "description": "this task is so 2000-and-late", "isDone": false } ] \ No newline at end of file diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 6075af08..0369d4ae 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -12,4 +12,14 @@ list a-command-that-duke-doesnt-know delete 0 delete 2 +event this task is so 2008 /at 2008-10-31T23:59:59 +event this task is so 2000-and-late /at 2000-12-31T23:59:59 +sort by date +sort by date desc +list +done 3 +sort by done +sort by done asc +sort by done desc +list bye diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index 450fb1c3..607729ac 100755 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -19,7 +19,7 @@ then fi # compile the code into the bin folder, terminates if error occurred -if ! bazel build //:TerminalDuke +if ! USE_BAZEL_VERSION=ac9353fab161efae4af72e73fbb657a762b3620d bazelisk run //:TerminalDuke then echo "********** BUILD FAILURE **********" exit 1 From 50d0cce867ea6301bbbc960ca7ff2b543f10a9b1 Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Mon, 25 Oct 2021 00:23:51 +0800 Subject: [PATCH 27/32] Basic implementation for Level-9: find feature. (#13) --- .../query/SimpleQueryInterpreter.java | 2 ++ .../lockarhythm/query/find/FindResponder.java | 28 +++++++++++++++++++ .../java/com/lockarhythm/tasks/TaskList.java | 13 +++++++++ text-ui-test/EXPECTED.TXT | 11 ++++++++ text-ui-test/input.txt | 2 ++ text-ui-test/runtest.sh | 2 +- 6 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/lockarhythm/query/find/FindResponder.java diff --git a/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java b/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java index 417edf61..169a7118 100644 --- a/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java +++ b/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java @@ -8,6 +8,7 @@ import com.lockarhythm.query.todo.TodoResponder; import com.lockarhythm.query.delete.DeleteResponder; import com.lockarhythm.query.sort.SortResponder; +import com.lockarhythm.query.find.FindResponder; import com.lockarhythm.tasks.TaskList; /** SimpleQueryInterpreter finds the first QueryInterpreter that responds a non-null result. */ @@ -24,6 +25,7 @@ public SimpleQueryInterpreter(TaskList list) { new EventResponder(list), new DeleteResponder(list), new SortResponder(list), + new FindResponder(list), }; interpreters = res; } diff --git a/src/main/java/com/lockarhythm/query/find/FindResponder.java b/src/main/java/com/lockarhythm/query/find/FindResponder.java new file mode 100644 index 00000000..0b9bf4e1 --- /dev/null +++ b/src/main/java/com/lockarhythm/query/find/FindResponder.java @@ -0,0 +1,28 @@ +package com.lockarhythm.query.find; + +import com.lockarhythm.query.RegexQueryInterpreter; +import com.lockarhythm.query.Result; +import com.lockarhythm.tasks.TaskList; + +public class FindResponder extends RegexQueryInterpreter { + private TaskList list; + + public FindResponder(TaskList list) { + this.list = list; + } + + protected String commandRegex() { + return "^find (.*)$"; + } + + public Result onMatch(String[] groups) { + String query = groups[1]; + + TaskList filteredList = list.find(query); + + if (filteredList.size() == 0) { + return new Result(String.format("I could not find any tasks matching your query '%s'", query)); + } + return new Result(String.format("Ok! I have found these tasks for your query '%s'!\n%s", query, filteredList)); + } +} diff --git a/src/main/java/com/lockarhythm/tasks/TaskList.java b/src/main/java/com/lockarhythm/tasks/TaskList.java index 299a7dd0..a4418922 100644 --- a/src/main/java/com/lockarhythm/tasks/TaskList.java +++ b/src/main/java/com/lockarhythm/tasks/TaskList.java @@ -75,6 +75,19 @@ public TaskList sortByDone(boolean isAscending) { return new TaskList(copy); } + /** + * Returns a new TaskList that matches the given "query" string. + */ + public TaskList find(String query) { + ArrayList filtered = new ArrayList(); + for (Task task : list) { + if (task.getDescription().contains(query)) { + filtered.add(task); + } + } + return new TaskList(filtered); + } + @Override public String toString() { StringBuilder s = new StringBuilder(); diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 1f1854db..48ec722b 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -190,6 +190,17 @@ ____________________________________________________________ + ____________________________________________________________ + Ok! I have found these tasks for your query 'bread'! + 1.[E][ ] buy bread (at: Nov 30 2021) + + ____________________________________________________________ + + ____________________________________________________________ + I could not find any tasks matching your query 'answer2life' + + ____________________________________________________________ + ____________________________________________________________ Bye. Hope to see you again soon! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 0369d4ae..08540a85 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -22,4 +22,6 @@ sort by done sort by done asc sort by done desc list +find bread +find answer2life bye diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index 607729ac..7489bc9c 100755 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -19,7 +19,7 @@ then fi # compile the code into the bin folder, terminates if error occurred -if ! USE_BAZEL_VERSION=ac9353fab161efae4af72e73fbb657a762b3620d bazelisk run //:TerminalDuke +if ! USE_BAZEL_VERSION=ac9353fab161efae4af72e73fbb657a762b3620d bazelisk build //:TerminalDuke then echo "********** BUILD FAILURE **********" exit 1 From 11ef984fcfaba268c4b2877d489f8f521cf65edb Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Mon, 25 Oct 2021 01:35:21 +0800 Subject: [PATCH 28/32] Use NLP for dates parsing and include time in formatting. (#14) * Better dates parsing and formatting. * Fix test command. --- BUILD | 2 + WORKSPACE | 3 +- .../java/com/lockarhythm/tasks/TaskDate.java | 15 ++- text-ui-test/EXPECTED.TXT | 125 +++++++++++------- text-ui-test/EXPECTED.json | 18 ++- text-ui-test/input.txt | 4 +- 6 files changed, 111 insertions(+), 56 deletions(-) diff --git a/BUILD b/BUILD index 8b8c4a6c..d1bae7dc 100644 --- a/BUILD +++ b/BUILD @@ -8,6 +8,7 @@ java_binary( deps = [ "@maven//:org_apache_commons_commons_lang3", "@maven//:com_google_code_gson_gson", + "@maven//:org_ocpsoft_prettytime_prettytime_nlp", ] ) @@ -22,6 +23,7 @@ java_binary( deps = [ "@maven//:org_apache_commons_commons_lang3", "@maven//:com_google_code_gson_gson", + "@maven//:org_ocpsoft_prettytime_prettytime_nlp", ] ) for class_name in [ diff --git a/WORKSPACE b/WORKSPACE index 960a9371..7c359742 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -16,7 +16,8 @@ maven_install( artifacts = [ #"com.google.guava:guava:21.0", "org.apache.commons:commons-lang3:3.11", - "com.google.code.gson:gson:2.8.8" + "com.google.code.gson:gson:2.8.8", + "org.ocpsoft.prettytime:prettytime-nlp:5.0.2.Final" ], repositories = [ # Private repositories are supported through HTTP Basic auth diff --git a/src/main/java/com/lockarhythm/tasks/TaskDate.java b/src/main/java/com/lockarhythm/tasks/TaskDate.java index 2dd89b18..96e5efb6 100644 --- a/src/main/java/com/lockarhythm/tasks/TaskDate.java +++ b/src/main/java/com/lockarhythm/tasks/TaskDate.java @@ -3,11 +3,20 @@ import java.io.Serializable; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import org.ocpsoft.prettytime.nlp.PrettyTimeParser; +import java.util.Date; +import java.util.List; +import java.time.ZoneId; class TaskDate implements Serializable { private LocalDateTime d; public TaskDate(String s) { + List dates = new PrettyTimeParser().parse(s); + if (dates.size() > 0) { + d = toLocalDateTime(dates.get(0)); + return; + } d = LocalDateTime.parse(s); } @@ -17,6 +26,10 @@ public static int compare(TaskDate x, TaskDate y) { @Override public String toString() { - return d.format(DateTimeFormatter.ofPattern("MMM d yyyy")); + return d.format(DateTimeFormatter.ofPattern("MMM d yyyy, HH:mm")); + } + + private LocalDateTime toLocalDateTime(Date dateToConvert) { + return LocalDateTime.ofInstant(dateToConvert.toInstant(), ZoneId.systemDefault()); } } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 48ec722b..11540f9b 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -18,26 +18,30 @@ ____________________________________________________________ ____________________________________________________________ - Please give me a valid LocalDate pattern: Text '10pm' could not be parsed at index 0 + Got it. I've added this task: + [D][ ] return book (by: Oct 25 2021, 22:00) + Now you have 2 tasks in the list. ____________________________________________________________ ____________________________________________________________ - Please give me a valid LocalDate pattern: Text 'Sunday 11pm' could not be parsed at index 0 + Got it. I've added this task: + [E][ ] buy bread (at: Oct 25 2021, 08:00) + Now you have 3 tasks in the list. ____________________________________________________________ ____________________________________________________________ Got it. I've added this task: - [D][ ] return book (by: Dec 31 2021) - Now you have 2 tasks in the list. + [D][ ] return book (by: Dec 31 2021, 23:59) + Now you have 4 tasks in the list. ____________________________________________________________ ____________________________________________________________ Got it. I've added this task: - [E][ ] buy bread (at: Nov 30 2021) - Now you have 3 tasks in the list. + [E][ ] buy bread (at: Nov 30 2021, 23:59) + Now you have 5 tasks in the list. ____________________________________________________________ @@ -50,36 +54,40 @@ ____________________________________________________________ Here are the tasks in your list: 1.[T][X] borrow book - 2.[D][ ] return book (by: Dec 31 2021) - 3.[E][ ] buy bread (at: Nov 30 2021) + 2.[D][ ] return book (by: Oct 25 2021, 22:00) + 3.[E][ ] buy bread (at: Oct 25 2021, 08:00) + 4.[D][ ] return book (by: Dec 31 2021, 23:59) + 5.[E][ ] buy bread (at: Nov 30 2021, 23:59) ____________________________________________________________ ____________________________________________________________ Nice! I've marked this task as done: - [D][X] return book (by: Dec 31 2021) + [D][X] return book (by: Oct 25 2021, 22:00) ____________________________________________________________ ____________________________________________________________ Nice! I've marked this task as done: - [D][X] return book (by: Dec 31 2021) + [D][X] return book (by: Oct 25 2021, 22:00) ____________________________________________________________ ____________________________________________________________ Got it. I've added this task: [T][ ] done 1 - Now you have 4 tasks in the list. + Now you have 6 tasks in the list. ____________________________________________________________ ____________________________________________________________ Here are the tasks in your list: 1.[T][X] borrow book - 2.[D][X] return book (by: Dec 31 2021) - 3.[E][ ] buy bread (at: Nov 30 2021) - 4.[T][ ] done 1 + 2.[D][X] return book (by: Oct 25 2021, 22:00) + 3.[E][ ] buy bread (at: Oct 25 2021, 08:00) + 4.[D][ ] return book (by: Dec 31 2021, 23:59) + 5.[E][ ] buy bread (at: Nov 30 2021, 23:59) + 6.[T][ ] done 1 ____________________________________________________________ @@ -95,32 +103,34 @@ ____________________________________________________________ Noted. I've removed this task: - [D][X] return book (by: Dec 31 2021) - Now you have 3 tasks in the list. + [D][X] return book (by: Oct 25 2021, 22:00) + Now you have 5 tasks in the list. ____________________________________________________________ ____________________________________________________________ Got it. I've added this task: - [E][ ] this task is so 2008 (at: Oct 31 2008) - Now you have 4 tasks in the list. + [E][ ] this task is so 2008 (at: Oct 31 2008, 23:59) + Now you have 6 tasks in the list. ____________________________________________________________ ____________________________________________________________ Got it. I've added this task: - [E][ ] this task is so 2000-and-late (at: Dec 31 2000) - Now you have 5 tasks in the list. + [E][ ] this task is so 2000-and-late (at: Dec 31 2000, 23:59) + Now you have 7 tasks in the list. ____________________________________________________________ ____________________________________________________________ Ok! I have sorted the tasks by date in ascending order! - 1.[E][ ] this task is so 2000-and-late (at: Dec 31 2000) - 2.[E][ ] this task is so 2008 (at: Oct 31 2008) - 3.[E][ ] buy bread (at: Nov 30 2021) - 4.[T][X] borrow book - 5.[T][ ] done 1 + 1.[E][ ] this task is so 2000-and-late (at: Dec 31 2000, 23:59) + 2.[E][ ] this task is so 2008 (at: Oct 31 2008, 23:59) + 3.[E][ ] buy bread (at: Oct 25 2021, 08:00) + 4.[E][ ] buy bread (at: Nov 30 2021, 23:59) + 5.[D][ ] return book (by: Dec 31 2021, 23:59) + 6.[T][X] borrow book + 7.[T][ ] done 1 ____________________________________________________________ @@ -128,71 +138,84 @@ Ok! I have sorted the tasks by date in descending order! 1.[T][X] borrow book 2.[T][ ] done 1 - 3.[E][ ] buy bread (at: Nov 30 2021) - 4.[E][ ] this task is so 2008 (at: Oct 31 2008) - 5.[E][ ] this task is so 2000-and-late (at: Dec 31 2000) + 3.[D][ ] return book (by: Dec 31 2021, 23:59) + 4.[E][ ] buy bread (at: Nov 30 2021, 23:59) + 5.[E][ ] buy bread (at: Oct 25 2021, 08:00) + 6.[E][ ] this task is so 2008 (at: Oct 31 2008, 23:59) + 7.[E][ ] this task is so 2000-and-late (at: Dec 31 2000, 23:59) ____________________________________________________________ ____________________________________________________________ Here are the tasks in your list: 1.[T][X] borrow book - 2.[E][ ] buy bread (at: Nov 30 2021) - 3.[T][ ] done 1 - 4.[E][ ] this task is so 2008 (at: Oct 31 2008) - 5.[E][ ] this task is so 2000-and-late (at: Dec 31 2000) + 2.[E][ ] buy bread (at: Oct 25 2021, 08:00) + 3.[D][ ] return book (by: Dec 31 2021, 23:59) + 4.[E][ ] buy bread (at: Nov 30 2021, 23:59) + 5.[T][ ] done 1 + 6.[E][ ] this task is so 2008 (at: Oct 31 2008, 23:59) + 7.[E][ ] this task is so 2000-and-late (at: Dec 31 2000, 23:59) ____________________________________________________________ ____________________________________________________________ Nice! I've marked this task as done: - [T][X] done 1 + [D][X] return book (by: Dec 31 2021, 23:59) ____________________________________________________________ ____________________________________________________________ Ok! I have sorted the tasks by done in ascending order! 1.[T][X] borrow book - 2.[T][X] done 1 - 3.[E][ ] buy bread (at: Nov 30 2021) - 4.[E][ ] this task is so 2008 (at: Oct 31 2008) - 5.[E][ ] this task is so 2000-and-late (at: Dec 31 2000) + 2.[D][X] return book (by: Dec 31 2021, 23:59) + 3.[E][ ] buy bread (at: Oct 25 2021, 08:00) + 4.[E][ ] buy bread (at: Nov 30 2021, 23:59) + 5.[T][ ] done 1 + 6.[E][ ] this task is so 2008 (at: Oct 31 2008, 23:59) + 7.[E][ ] this task is so 2000-and-late (at: Dec 31 2000, 23:59) ____________________________________________________________ ____________________________________________________________ Ok! I have sorted the tasks by done in ascending order! 1.[T][X] borrow book - 2.[T][X] done 1 - 3.[E][ ] buy bread (at: Nov 30 2021) - 4.[E][ ] this task is so 2008 (at: Oct 31 2008) - 5.[E][ ] this task is so 2000-and-late (at: Dec 31 2000) + 2.[D][X] return book (by: Dec 31 2021, 23:59) + 3.[E][ ] buy bread (at: Oct 25 2021, 08:00) + 4.[E][ ] buy bread (at: Nov 30 2021, 23:59) + 5.[T][ ] done 1 + 6.[E][ ] this task is so 2008 (at: Oct 31 2008, 23:59) + 7.[E][ ] this task is so 2000-and-late (at: Dec 31 2000, 23:59) ____________________________________________________________ ____________________________________________________________ Ok! I have sorted the tasks by done in descending order! - 1.[E][ ] buy bread (at: Nov 30 2021) - 2.[E][ ] this task is so 2008 (at: Oct 31 2008) - 3.[E][ ] this task is so 2000-and-late (at: Dec 31 2000) - 4.[T][X] borrow book - 5.[T][X] done 1 + 1.[E][ ] buy bread (at: Oct 25 2021, 08:00) + 2.[E][ ] buy bread (at: Nov 30 2021, 23:59) + 3.[T][ ] done 1 + 4.[E][ ] this task is so 2008 (at: Oct 31 2008, 23:59) + 5.[E][ ] this task is so 2000-and-late (at: Dec 31 2000, 23:59) + 6.[T][X] borrow book + 7.[D][X] return book (by: Dec 31 2021, 23:59) ____________________________________________________________ ____________________________________________________________ Here are the tasks in your list: 1.[T][X] borrow book - 2.[E][ ] buy bread (at: Nov 30 2021) - 3.[T][X] done 1 - 4.[E][ ] this task is so 2008 (at: Oct 31 2008) - 5.[E][ ] this task is so 2000-and-late (at: Dec 31 2000) + 2.[E][ ] buy bread (at: Oct 25 2021, 08:00) + 3.[D][X] return book (by: Dec 31 2021, 23:59) + 4.[E][ ] buy bread (at: Nov 30 2021, 23:59) + 5.[T][ ] done 1 + 6.[E][ ] this task is so 2008 (at: Oct 31 2008, 23:59) + 7.[E][ ] this task is so 2000-and-late (at: Dec 31 2000, 23:59) ____________________________________________________________ ____________________________________________________________ Ok! I have found these tasks for your query 'bread'! - 1.[E][ ] buy bread (at: Nov 30 2021) + 1.[E][ ] buy bread (at: Oct 25 2021, 08:00) + 2.[E][ ] buy bread (at: Nov 30 2021, 23:59) ____________________________________________________________ diff --git a/text-ui-test/EXPECTED.json b/text-ui-test/EXPECTED.json index 19d2198c..86afcdee 100644 --- a/text-ui-test/EXPECTED.json +++ b/text-ui-test/EXPECTED.json @@ -4,6 +4,22 @@ "description": "borrow book", "isDone": true }, + { + "at": { + "d": "2021-10-25T08:00:00" + }, + "_type": "EVENT", + "description": "buy bread", + "isDone": false + }, + { + "by": { + "d": "2021-12-31T23:59:59" + }, + "_type": "DEADLINE", + "description": "return book", + "isDone": true + }, { "at": { "d": "2021-11-30T23:59:59" @@ -15,7 +31,7 @@ { "_type": "TODO", "description": "done 1", - "isDone": true + "isDone": false }, { "at": { diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 08540a85..1d8120d4 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,6 +1,6 @@ todo borrow book -deadline return book /by 10pm -event buy bread /at Sunday 11pm +deadline return book /by Oct 25 10pm +event buy bread /at Oct 25 morning deadline return book /by 2021-12-31T23:59:59 event buy bread /at 2021-11-30T23:59:59 done 1 From 94ad2b65ad49b79c7d5aee01174f933cb6c44546 Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Mon, 25 Oct 2021 19:58:54 +0800 Subject: [PATCH 29/32] Run through code formatter. (#15) --- .../lockarhythm/application/TerminalDuke.java | 8 +- .../lockarhythm/query/AddedTaskResult.java | 4 +- .../com/lockarhythm/query/DukeException.java | 3 +- .../query/RegexQueryInterpreter.java | 5 +- .../query/SimpleQueryInterpreter.java | 8 +- .../query/delete/DeleteResponder.java | 2 +- .../query/event/EventResponder.java | 7 +- .../lockarhythm/query/find/FindResponder.java | 7 +- .../lockarhythm/query/list/ListResponder.java | 1 - .../query/markasdone/MarkAsDoneResponder.java | 2 +- .../lockarhythm/query/sort/SortResponder.java | 16 ++-- .../lockarhythm/query/todo/TodoResponder.java | 2 +- .../lockarhythm/storage/GsonLocalDate.java | 22 +++--- .../storage/GsonLocalDateTime.java | 25 ++++--- .../java/com/lockarhythm/storage/Storage.java | 36 ++++----- src/main/java/com/lockarhythm/tasks/Task.java | 4 +- .../java/com/lockarhythm/tasks/TaskDate.java | 6 +- .../lockarhythm/tasks/TaskDeserializer.java | 73 +++++++++---------- .../java/com/lockarhythm/tasks/TaskList.java | 41 +++++------ 19 files changed, 135 insertions(+), 137 deletions(-) diff --git a/src/main/java/com/lockarhythm/application/TerminalDuke.java b/src/main/java/com/lockarhythm/application/TerminalDuke.java index 693c6209..a7c6e835 100644 --- a/src/main/java/com/lockarhythm/application/TerminalDuke.java +++ b/src/main/java/com/lockarhythm/application/TerminalDuke.java @@ -1,13 +1,13 @@ package com.lockarhythm.application; import com.lockarhythm.query.SimpleQueryInterpreter; -import com.lockarhythm.tasks.TaskList; +import com.lockarhythm.storage.Storage; import com.lockarhythm.tasks.Task; +import com.lockarhythm.tasks.TaskList; import com.lockarhythm.ui.TerminalUI; -import com.lockarhythm.storage.Storage; -import java.util.ArrayList; -import java.nio.file.Paths; import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; final class TerminalDuke extends Application { public static void main(String[] args) { diff --git a/src/main/java/com/lockarhythm/query/AddedTaskResult.java b/src/main/java/com/lockarhythm/query/AddedTaskResult.java index dd000a56..fe81905a 100644 --- a/src/main/java/com/lockarhythm/query/AddedTaskResult.java +++ b/src/main/java/com/lockarhythm/query/AddedTaskResult.java @@ -1,7 +1,6 @@ package com.lockarhythm.query; import com.lockarhythm.tasks.Task; -import com.lockarhythm.tasks.TaskList; public class AddedTaskResult extends Result { private Task task; @@ -16,7 +15,6 @@ public AddedTaskResult(Task task, int size) { @Override public String getText() { return String.format( - "Got it. I've added this task:\n\t%s\nNow you have %d tasks in the list.", - task, size); + "Got it. I've added this task:\n\t%s\nNow you have %d tasks in the list.", task, size); } } diff --git a/src/main/java/com/lockarhythm/query/DukeException.java b/src/main/java/com/lockarhythm/query/DukeException.java index a6756f5e..42f0711f 100644 --- a/src/main/java/com/lockarhythm/query/DukeException.java +++ b/src/main/java/com/lockarhythm/query/DukeException.java @@ -1,4 +1,3 @@ package com.lockarhythm.query; -public class DukeException extends Exception { -} +public class DukeException extends Exception {} diff --git a/src/main/java/com/lockarhythm/query/RegexQueryInterpreter.java b/src/main/java/com/lockarhythm/query/RegexQueryInterpreter.java index 8c5484f2..1c19bd8e 100644 --- a/src/main/java/com/lockarhythm/query/RegexQueryInterpreter.java +++ b/src/main/java/com/lockarhythm/query/RegexQueryInterpreter.java @@ -19,14 +19,15 @@ public Result interpret(String query) { } private String[] toGroupStrings(Matcher matcher) { - String[] groups = new String[matcher.groupCount()+1]; + String[] groups = new String[matcher.groupCount() + 1]; for (int i = 0; i <= matcher.groupCount(); i++) { groups[i] = matcher.group(i); } return groups; } - // onMatch is called when regexp pattern matches. The regexp capturing groups is passed as arguments. + // onMatch is called when regexp pattern matches. The regexp capturing groups is passed as + // arguments. public abstract Result onMatch(String[] groups); protected abstract String commandRegex(); diff --git a/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java b/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java index 169a7118..b7dc2e39 100644 --- a/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java +++ b/src/main/java/com/lockarhythm/query/SimpleQueryInterpreter.java @@ -1,14 +1,14 @@ package com.lockarhythm.query; -import com.lockarhythm.query.list.ListResponder; import com.lockarhythm.query.deadline.DeadlineResponder; +import com.lockarhythm.query.delete.DeleteResponder; import com.lockarhythm.query.event.EventResponder; import com.lockarhythm.query.exit.ExitResponder; +import com.lockarhythm.query.find.FindResponder; +import com.lockarhythm.query.list.ListResponder; import com.lockarhythm.query.markasdone.MarkAsDoneResponder; -import com.lockarhythm.query.todo.TodoResponder; -import com.lockarhythm.query.delete.DeleteResponder; import com.lockarhythm.query.sort.SortResponder; -import com.lockarhythm.query.find.FindResponder; +import com.lockarhythm.query.todo.TodoResponder; import com.lockarhythm.tasks.TaskList; /** SimpleQueryInterpreter finds the first QueryInterpreter that responds a non-null result. */ diff --git a/src/main/java/com/lockarhythm/query/delete/DeleteResponder.java b/src/main/java/com/lockarhythm/query/delete/DeleteResponder.java index 835e04fc..7dfa9b10 100644 --- a/src/main/java/com/lockarhythm/query/delete/DeleteResponder.java +++ b/src/main/java/com/lockarhythm/query/delete/DeleteResponder.java @@ -1,8 +1,8 @@ package com.lockarhythm.query.delete; +import com.lockarhythm.query.DeletedTaskResult; import com.lockarhythm.query.RegexQueryInterpreter; import com.lockarhythm.query.Result; -import com.lockarhythm.query.DeletedTaskResult; import com.lockarhythm.tasks.Task; import com.lockarhythm.tasks.TaskList; diff --git a/src/main/java/com/lockarhythm/query/event/EventResponder.java b/src/main/java/com/lockarhythm/query/event/EventResponder.java index 6db6f085..2a8a088d 100644 --- a/src/main/java/com/lockarhythm/query/event/EventResponder.java +++ b/src/main/java/com/lockarhythm/query/event/EventResponder.java @@ -1,8 +1,8 @@ package com.lockarhythm.query.event; +import com.lockarhythm.query.AddedTaskResult; import com.lockarhythm.query.RegexQueryInterpreter; import com.lockarhythm.query.Result; -import com.lockarhythm.query.AddedTaskResult; import com.lockarhythm.tasks.Task; import com.lockarhythm.tasks.TaskList; @@ -21,8 +21,9 @@ public Result onMatch(String[] groups) { try { Task task = list.addEventTask(groups[1], groups[2]); return new AddedTaskResult(task, list.size()); - } catch(java.time.format.DateTimeParseException e) { - return new Result(String.format("Please give me a valid LocalDate pattern: %s", e.getMessage())); + } catch (java.time.format.DateTimeParseException e) { + return new Result( + String.format("Please give me a valid LocalDate pattern: %s", e.getMessage())); } } } diff --git a/src/main/java/com/lockarhythm/query/find/FindResponder.java b/src/main/java/com/lockarhythm/query/find/FindResponder.java index 0b9bf4e1..0ef60507 100644 --- a/src/main/java/com/lockarhythm/query/find/FindResponder.java +++ b/src/main/java/com/lockarhythm/query/find/FindResponder.java @@ -21,8 +21,11 @@ public Result onMatch(String[] groups) { TaskList filteredList = list.find(query); if (filteredList.size() == 0) { - return new Result(String.format("I could not find any tasks matching your query '%s'", query)); + return new Result( + String.format("I could not find any tasks matching your query '%s'", query)); } - return new Result(String.format("Ok! I have found these tasks for your query '%s'!\n%s", query, filteredList)); + return new Result( + String.format( + "Ok! I have found these tasks for your query '%s'!\n%s", query, filteredList)); } } diff --git a/src/main/java/com/lockarhythm/query/list/ListResponder.java b/src/main/java/com/lockarhythm/query/list/ListResponder.java index 13077612..ba52a90c 100644 --- a/src/main/java/com/lockarhythm/query/list/ListResponder.java +++ b/src/main/java/com/lockarhythm/query/list/ListResponder.java @@ -2,7 +2,6 @@ import com.lockarhythm.query.QueryInterpreter; import com.lockarhythm.query.Result; -import com.lockarhythm.tasks.Task; import com.lockarhythm.tasks.TaskList; public final class ListResponder implements QueryInterpreter { diff --git a/src/main/java/com/lockarhythm/query/markasdone/MarkAsDoneResponder.java b/src/main/java/com/lockarhythm/query/markasdone/MarkAsDoneResponder.java index c9c330b4..bcb629c2 100644 --- a/src/main/java/com/lockarhythm/query/markasdone/MarkAsDoneResponder.java +++ b/src/main/java/com/lockarhythm/query/markasdone/MarkAsDoneResponder.java @@ -21,7 +21,7 @@ public Result onMatch(String[] groups) { try { Task task = list.markAsDone(i - 1); return new Result("Nice! I've marked this task as done:\n\t" + task.toString()); - } catch(IndexOutOfBoundsException e) { + } catch (IndexOutOfBoundsException e) { return new Result(String.format("Item %d is not on the list. I cannot mark it as done!", i)); } } diff --git a/src/main/java/com/lockarhythm/query/sort/SortResponder.java b/src/main/java/com/lockarhythm/query/sort/SortResponder.java index 71ae7a9b..69b4dc9d 100644 --- a/src/main/java/com/lockarhythm/query/sort/SortResponder.java +++ b/src/main/java/com/lockarhythm/query/sort/SortResponder.java @@ -5,7 +5,7 @@ import com.lockarhythm.tasks.TaskList; public class SortResponder extends RegexQueryInterpreter { - private final static boolean DEFAULT_SORT_ORDER = true; + private static final boolean DEFAULT_SORT_ORDER = true; private TaskList list; @@ -23,7 +23,10 @@ public Result onMatch(String[] groups) { TaskList sortedList = sortBy(sortKey, isAscending); - return new Result(String.format("Ok! I have sorted the tasks by %s in %s order!\n%s", sortKey, isAscending ? "ascending" : "descending", sortedList)); + return new Result( + String.format( + "Ok! I have sorted the tasks by %s in %s order!\n%s", + sortKey, isAscending ? "ascending" : "descending", sortedList)); } private boolean parseIsAscending(String value) { @@ -40,17 +43,16 @@ private boolean parseIsAscending(String value) { } } - /** - * Sorts by the `sortKey` in the order given by `isAscending`. - */ + /** Sorts by the `sortKey` in the order given by `isAscending`. */ private TaskList sortBy(String sortKey, boolean isAscending) { - switch(sortKey) { + switch (sortKey) { case "done": return list.sortByDone(isAscending); case "date": return list.sortByTaskDate(isAscending); default: - }; + } + ; return list.sortByTaskDate(isAscending); } } diff --git a/src/main/java/com/lockarhythm/query/todo/TodoResponder.java b/src/main/java/com/lockarhythm/query/todo/TodoResponder.java index 1725ffc0..9e144e27 100644 --- a/src/main/java/com/lockarhythm/query/todo/TodoResponder.java +++ b/src/main/java/com/lockarhythm/query/todo/TodoResponder.java @@ -1,7 +1,7 @@ package com.lockarhythm.query.todo; -import com.lockarhythm.query.RegexQueryInterpreter; import com.lockarhythm.query.AddedTaskResult; +import com.lockarhythm.query.RegexQueryInterpreter; import com.lockarhythm.query.Result; import com.lockarhythm.tasks.Task; import com.lockarhythm.tasks.TaskList; diff --git a/src/main/java/com/lockarhythm/storage/GsonLocalDate.java b/src/main/java/com/lockarhythm/storage/GsonLocalDate.java index 7eb94a47..e2fe70bb 100644 --- a/src/main/java/com/lockarhythm/storage/GsonLocalDate.java +++ b/src/main/java/com/lockarhythm/storage/GsonLocalDate.java @@ -1,21 +1,23 @@ package com.lockarhythm.storage; import com.google.gson.*; - import java.lang.reflect.Type; import java.time.LocalDate; import java.time.format.DateTimeFormatter; public class GsonLocalDate implements JsonSerializer, JsonDeserializer { - @Override - public LocalDate deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { - String ldtString = jsonElement.getAsString(); - return LocalDate.parse(ldtString,DateTimeFormatter.ISO_LOCAL_DATE); - } + @Override + public LocalDate deserialize( + JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) + throws JsonParseException { + String ldtString = jsonElement.getAsString(); + return LocalDate.parse(ldtString, DateTimeFormatter.ISO_LOCAL_DATE); + } - @Override - public JsonElement serialize(LocalDate localDate, Type type, JsonSerializationContext jsonSerializationContext) { - return new JsonPrimitive(localDate.format(DateTimeFormatter.ISO_LOCAL_DATE)); - } + @Override + public JsonElement serialize( + LocalDate localDate, Type type, JsonSerializationContext jsonSerializationContext) { + return new JsonPrimitive(localDate.format(DateTimeFormatter.ISO_LOCAL_DATE)); + } } diff --git a/src/main/java/com/lockarhythm/storage/GsonLocalDateTime.java b/src/main/java/com/lockarhythm/storage/GsonLocalDateTime.java index 95878f75..1fea45ef 100644 --- a/src/main/java/com/lockarhythm/storage/GsonLocalDateTime.java +++ b/src/main/java/com/lockarhythm/storage/GsonLocalDateTime.java @@ -1,21 +1,24 @@ package com.lockarhythm.storage; import com.google.gson.*; - import java.lang.reflect.Type; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -public class GsonLocalDateTime implements JsonSerializer, JsonDeserializer { +public class GsonLocalDateTime + implements JsonSerializer, JsonDeserializer { - @Override - public LocalDateTime deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { - String ldtString = jsonElement.getAsString(); - return LocalDateTime.parse(ldtString,DateTimeFormatter.ISO_LOCAL_DATE_TIME); - } + @Override + public LocalDateTime deserialize( + JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) + throws JsonParseException { + String ldtString = jsonElement.getAsString(); + return LocalDateTime.parse(ldtString, DateTimeFormatter.ISO_LOCAL_DATE_TIME); + } - @Override - public JsonElement serialize(LocalDateTime localDateTime, Type type, JsonSerializationContext jsonSerializationContext) { - return new JsonPrimitive(localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); - } + @Override + public JsonElement serialize( + LocalDateTime localDateTime, Type type, JsonSerializationContext jsonSerializationContext) { + return new JsonPrimitive(localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); + } } diff --git a/src/main/java/com/lockarhythm/storage/Storage.java b/src/main/java/com/lockarhythm/storage/Storage.java index 052d2e04..4413ea66 100644 --- a/src/main/java/com/lockarhythm/storage/Storage.java +++ b/src/main/java/com/lockarhythm/storage/Storage.java @@ -1,24 +1,18 @@ package com.lockarhythm.storage; - -import com.google.gson.GsonBuilder; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; -import java.lang.reflect.Type; -import java.time.LocalDateTime; -import java.time.LocalDate; - +import com.lockarhythm.tasks.Task; +import com.lockarhythm.tasks.TaskDeserializer; import java.io.FileOutputStream; -import java.io.ObjectOutputStream; import java.io.IOException; -import java.io.Serializable; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.charset.StandardCharsets; - -import com.lockarhythm.tasks.TaskDeserializer; -import com.lockarhythm.tasks.Task; - +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.ArrayList; public class Storage { @@ -31,12 +25,13 @@ public class Storage { public Storage(String filePath) { this.filePath = filePath; - gson = new GsonBuilder() - .registerTypeAdapter(Task.class, new TaskDeserializer("_type")) - .registerTypeAdapter(LocalDateTime.class, new GsonLocalDateTime()) - .registerTypeAdapter(LocalDate.class, new GsonLocalDate()) - .setPrettyPrinting() - .create(); + gson = + new GsonBuilder() + .registerTypeAdapter(Task.class, new TaskDeserializer("_type")) + .registerTypeAdapter(LocalDateTime.class, new GsonLocalDateTime()) + .registerTypeAdapter(LocalDate.class, new GsonLocalDate()) + .setPrettyPrinting() + .create(); } public void registerList(ArrayList list) { @@ -49,7 +44,8 @@ public ArrayList load(Class type) { content = Files.readString(Path.of(filePath), StandardCharsets.UTF_8); Type typeOfT = TypeToken.getParameterized(ArrayList.class, type).getType(); - ArrayList deserialized = gson.fromJson(content, new TypeToken>(){}.getType()); + ArrayList deserialized = + gson.fromJson(content, new TypeToken>() {}.getType()); if (deserialized == null) { return new ArrayList(); } diff --git a/src/main/java/com/lockarhythm/tasks/Task.java b/src/main/java/com/lockarhythm/tasks/Task.java index e140b65f..4a739823 100644 --- a/src/main/java/com/lockarhythm/tasks/Task.java +++ b/src/main/java/com/lockarhythm/tasks/Task.java @@ -3,8 +3,8 @@ import java.io.Serializable; public abstract class Task implements Serializable { - private transient static String DEFAULT_TASK_DATE_VALUE = "3000-12-31T23:59:59"; - private transient static TaskDate DEFAULT_TASK_DATE = new TaskDate(DEFAULT_TASK_DATE_VALUE); + private static transient String DEFAULT_TASK_DATE_VALUE = "3000-12-31T23:59:59"; + private static transient TaskDate DEFAULT_TASK_DATE = new TaskDate(DEFAULT_TASK_DATE_VALUE); private String description; private boolean isDone; diff --git a/src/main/java/com/lockarhythm/tasks/TaskDate.java b/src/main/java/com/lockarhythm/tasks/TaskDate.java index 96e5efb6..46552183 100644 --- a/src/main/java/com/lockarhythm/tasks/TaskDate.java +++ b/src/main/java/com/lockarhythm/tasks/TaskDate.java @@ -2,11 +2,11 @@ import java.io.Serializable; import java.time.LocalDateTime; +import java.time.ZoneId; import java.time.format.DateTimeFormatter; -import org.ocpsoft.prettytime.nlp.PrettyTimeParser; import java.util.Date; import java.util.List; -import java.time.ZoneId; +import org.ocpsoft.prettytime.nlp.PrettyTimeParser; class TaskDate implements Serializable { private LocalDateTime d; @@ -30,6 +30,6 @@ public String toString() { } private LocalDateTime toLocalDateTime(Date dateToConvert) { - return LocalDateTime.ofInstant(dateToConvert.toInstant(), ZoneId.systemDefault()); + return LocalDateTime.ofInstant(dateToConvert.toInstant(), ZoneId.systemDefault()); } } diff --git a/src/main/java/com/lockarhythm/tasks/TaskDeserializer.java b/src/main/java/com/lockarhythm/tasks/TaskDeserializer.java index a00f4b0b..d157e563 100644 --- a/src/main/java/com/lockarhythm/tasks/TaskDeserializer.java +++ b/src/main/java/com/lockarhythm/tasks/TaskDeserializer.java @@ -1,47 +1,44 @@ package com.lockarhythm.tasks; import com.google.gson.*; -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.Map; - -import com.lockarhythm.storage.GsonLocalDateTime; import com.lockarhythm.storage.GsonLocalDate; +import com.lockarhythm.storage.GsonLocalDateTime; +import java.lang.reflect.Type; import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Map; -/** - * Technique credit to https://www.baeldung.com/gson-list - */ +/** Technique credit to https://www.baeldung.com/gson-list */ public class TaskDeserializer implements JsonDeserializer { - private String taskTypeElementName; - private Gson gson; - private Map> taskTypeRegistry; - - public TaskDeserializer(String taskTypeElementName) { - this.gson = new GsonBuilder() - .registerTypeAdapter(LocalDateTime.class, new GsonLocalDateTime()) - .registerTypeAdapter(LocalDate.class, new GsonLocalDate()) - .create(); - - this.taskTypeElementName = taskTypeElementName; - this.taskTypeRegistry = new HashMap<>(); - - this.registerTaskType("DEADLINE", DeadlineTask.class); - this.registerTaskType("TODO", TodoTask.class); - this.registerTaskType("EVENT", EventTask.class); - } - - public void registerTaskType(String taskTypeName, Class taskType) { - taskTypeRegistry.put(taskTypeName, taskType); - } - - public Task deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { - JsonObject taskObject = json.getAsJsonObject(); - JsonElement taskTypeElement = taskObject.get(taskTypeElementName); - - Class taskType = taskTypeRegistry.get(taskTypeElement.getAsString()); - return gson.fromJson(taskObject, taskType); - } + private String taskTypeElementName; + private Gson gson; + private Map> taskTypeRegistry; + + public TaskDeserializer(String taskTypeElementName) { + this.gson = + new GsonBuilder() + .registerTypeAdapter(LocalDateTime.class, new GsonLocalDateTime()) + .registerTypeAdapter(LocalDate.class, new GsonLocalDate()) + .create(); + + this.taskTypeElementName = taskTypeElementName; + this.taskTypeRegistry = new HashMap<>(); + + this.registerTaskType("DEADLINE", DeadlineTask.class); + this.registerTaskType("TODO", TodoTask.class); + this.registerTaskType("EVENT", EventTask.class); + } + + public void registerTaskType(String taskTypeName, Class taskType) { + taskTypeRegistry.put(taskTypeName, taskType); + } + + public Task deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { + JsonObject taskObject = json.getAsJsonObject(); + JsonElement taskTypeElement = taskObject.get(taskTypeElementName); + + Class taskType = taskTypeRegistry.get(taskTypeElement.getAsString()); + return gson.fromJson(taskObject, taskType); + } } diff --git a/src/main/java/com/lockarhythm/tasks/TaskList.java b/src/main/java/com/lockarhythm/tasks/TaskList.java index a4418922..1709accc 100644 --- a/src/main/java/com/lockarhythm/tasks/TaskList.java +++ b/src/main/java/com/lockarhythm/tasks/TaskList.java @@ -1,7 +1,6 @@ package com.lockarhythm.tasks; import java.util.ArrayList; -import java.util.Comparator; import java.util.Collections; public class TaskList { @@ -47,37 +46,35 @@ public Task deleteTask(int index) { return list.remove(index); } - /** - * Returns a copy of TaskList sorted by the task date. - */ + /** Returns a copy of TaskList sorted by the task date. */ public TaskList sortByTaskDate(boolean isAscending) { ArrayList copy = new ArrayList(list); - Collections.sort(copy, (a, b) -> { - if (isAscending) { - return TaskDate.compare(a.getTaskDate(), b.getTaskDate()); - } - return TaskDate.compare(b.getTaskDate(), a.getTaskDate()); - }); + Collections.sort( + copy, + (a, b) -> { + if (isAscending) { + return TaskDate.compare(a.getTaskDate(), b.getTaskDate()); + } + return TaskDate.compare(b.getTaskDate(), a.getTaskDate()); + }); return new TaskList(copy); } - /** - * Returns a copy of TaskList sorted by the "done" field. - */ + /** Returns a copy of TaskList sorted by the "done" field. */ public TaskList sortByDone(boolean isAscending) { ArrayList copy = new ArrayList(list); - Collections.sort(copy, (a, b) -> { - if (isAscending) { - return Boolean.compare(b.isDone(), a.isDone()); - } - return Boolean.compare(a.isDone(), b.isDone()); - }); + Collections.sort( + copy, + (a, b) -> { + if (isAscending) { + return Boolean.compare(b.isDone(), a.isDone()); + } + return Boolean.compare(a.isDone(), b.isDone()); + }); return new TaskList(copy); } - /** - * Returns a new TaskList that matches the given "query" string. - */ + /** Returns a new TaskList that matches the given "query" string. */ public TaskList find(String query) { ArrayList filtered = new ArrayList(); for (Task task : list) { From c401a189948a103ae9d83dbeb174efbcce98fdd5 Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Tue, 26 Oct 2021 20:20:26 +0800 Subject: [PATCH 30/32] Fix code quality feedback #16 (#17) Co-authored-by: Goh Yisheng --- src/main/java/com/lockarhythm/query/sort/SortResponder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/lockarhythm/query/sort/SortResponder.java b/src/main/java/com/lockarhythm/query/sort/SortResponder.java index 69b4dc9d..a4f5a744 100644 --- a/src/main/java/com/lockarhythm/query/sort/SortResponder.java +++ b/src/main/java/com/lockarhythm/query/sort/SortResponder.java @@ -5,7 +5,7 @@ import com.lockarhythm.tasks.TaskList; public class SortResponder extends RegexQueryInterpreter { - private static final boolean DEFAULT_SORT_ORDER = true; + private static final boolean IS_ASCENDING_DEFAULT = true; private TaskList list; @@ -31,7 +31,7 @@ public Result onMatch(String[] groups) { private boolean parseIsAscending(String value) { if (value == null) { - return DEFAULT_SORT_ORDER; + return IS_ASCENDING_DEFAULT; } String s = value.trim(); if (s.equals("desc")) { @@ -39,7 +39,7 @@ private boolean parseIsAscending(String value) { } else if (s.equals("asc")) { return true; } else { - return DEFAULT_SORT_ORDER; + return IS_ASCENDING_DEFAULT; } } From fd53b7493e1c2d04d7624e41b9ddaf1e58466ffc Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Tue, 26 Oct 2021 21:09:47 +0800 Subject: [PATCH 31/32] Implement A-Assertions (#18) * Add assertions. * Create QA build. * Fix build. * Update readme. --- BUILD | 20 +++++++++++++++++-- README.md | 3 +++ .../lockarhythm/query/sort/SortResponder.java | 1 + .../java/com/lockarhythm/ui/TerminalUI.java | 1 + text-ui-test/runtest.sh | 4 ++-- 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/BUILD b/BUILD index d1bae7dc..ee30cb50 100644 --- a/BUILD +++ b/BUILD @@ -1,7 +1,7 @@ load("@rules_java//java:defs.bzl", "java_binary", "java_test") java_binary( - name = "TerminalDuke", + name = "TerminalDuke", # Production Build. srcs = glob([ "src/main/java/com/lockarhythm/**/*.java", ]), @@ -9,7 +9,23 @@ java_binary( "@maven//:org_apache_commons_commons_lang3", "@maven//:com_google_code_gson_gson", "@maven//:org_ocpsoft_prettytime_prettytime_nlp", - ] + ], +) + +java_binary( + name = "TerminalDukeQA", # QA Build. has assertions enabled. + main_class = "com.lockarhythm.application.TerminalDuke", + srcs = glob([ + "src/main/java/com/lockarhythm/**/*.java", + ]), + deps = [ + "@maven//:org_apache_commons_commons_lang3", + "@maven//:com_google_code_gson_gson", + "@maven//:org_ocpsoft_prettytime_prettytime_nlp", + ], + jvm_flags = [ + "-enableassertions", + ], ) [ diff --git a/README.md b/README.md index a54cf64b..0dfacebc 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,9 @@ bazel run //:TerminalDuke # Use bazelisk for M1 Macs (Apple Silicon) USE_BAZEL_VERSION=ac9353fab161efae4af72e73fbb657a762b3620d bazelisk run //:TerminalDuke + +# For QA build, for example, having assertions enabled, build TerminalDukeQA instead +USE_BAZEL_VERSION=ac9353fab161efae4af72e73fbb657a762b3620d bazelisk run //:TerminalDukeQA ``` To build jar file for deployment purposes: diff --git a/src/main/java/com/lockarhythm/query/sort/SortResponder.java b/src/main/java/com/lockarhythm/query/sort/SortResponder.java index a4f5a744..52ca5e65 100644 --- a/src/main/java/com/lockarhythm/query/sort/SortResponder.java +++ b/src/main/java/com/lockarhythm/query/sort/SortResponder.java @@ -51,6 +51,7 @@ private TaskList sortBy(String sortKey, boolean isAscending) { case "date": return list.sortByTaskDate(isAscending); default: + assert false : "sortKey is not a valid value. Check command regex."; } ; return list.sortByTaskDate(isAscending); diff --git a/src/main/java/com/lockarhythm/ui/TerminalUI.java b/src/main/java/com/lockarhythm/ui/TerminalUI.java index 4c60aad5..396b678d 100644 --- a/src/main/java/com/lockarhythm/ui/TerminalUI.java +++ b/src/main/java/com/lockarhythm/ui/TerminalUI.java @@ -25,6 +25,7 @@ public void print(String... strings) { } public void print(Result res) { + assert res != null; print(res.getText()); } } diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index 7489bc9c..bbc3283f 100755 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -19,14 +19,14 @@ then fi # compile the code into the bin folder, terminates if error occurred -if ! USE_BAZEL_VERSION=ac9353fab161efae4af72e73fbb657a762b3620d bazelisk build //:TerminalDuke +if ! USE_BAZEL_VERSION=ac9353fab161efae4af72e73fbb657a762b3620d bazelisk build //:TerminalDukeQA then echo "********** BUILD FAILURE **********" exit 1 fi # run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -../bazel-bin/TerminalDuke < input.txt > ACTUAL.TXT +../bazel-bin/TerminalDukeQA < input.txt > ACTUAL.TXT # convert to UNIX format cp EXPECTED.TXT EXPECTED-UNIX.TXT From ff37085622d1a6d97c8d40a3b7a8f8bcb0fd47f2 Mon Sep 17 00:00:00 2001 From: lockarhythm <88638946+lockarhythm@users.noreply.github.com> Date: Sun, 14 Nov 2021 23:37:20 +0800 Subject: [PATCH 32/32] Add java docs (#25) --- .../lockarhythm/application/Application.java | 14 +++++++++++ .../lockarhythm/application/TerminalDuke.java | 3 +++ .../java/com/lockarhythm/storage/Storage.java | 22 ++++++++++++++++++ .../java/com/lockarhythm/ui/TerminalUI.java | 23 +++++++++++++++++++ src/main/java/com/lockarhythm/ui/UI.java | 23 +++++++++++++++++++ 5 files changed, 85 insertions(+) diff --git a/src/main/java/com/lockarhythm/application/Application.java b/src/main/java/com/lockarhythm/application/Application.java index bc803d17..5ca8f930 100644 --- a/src/main/java/com/lockarhythm/application/Application.java +++ b/src/main/java/com/lockarhythm/application/Application.java @@ -7,6 +7,11 @@ import com.lockarhythm.ui.UI; import java.io.IOException; +/** + * Application contains the common logic of all Duke Applications (GUI, TUI). + * + *

The application architecture is heavily inspired by the Ports & Adapters architecture. + */ abstract class Application { static String logo = " \t____ _ \n" @@ -15,6 +20,15 @@ abstract class Application { + "\t| |_| | |_| | < __/\n" + "\t|____/ \\__,_|_|\\_\\___|\n"; + /** + * run should be supplied with concrete implementations of its UI, QueryInterpreter and Storage. + * + *

For example, UI could be the Terminal UI or GUI. Storage could be in-memory or disk. + * + * @param ui a concrete UI implementation + * @param q a concrete QueryInterpreter implementation + * @param storage any Storage implementation + */ public static void run(UI ui, QueryInterpreter q, Storage storage) { Result result; diff --git a/src/main/java/com/lockarhythm/application/TerminalDuke.java b/src/main/java/com/lockarhythm/application/TerminalDuke.java index a7c6e835..d961ba6e 100644 --- a/src/main/java/com/lockarhythm/application/TerminalDuke.java +++ b/src/main/java/com/lockarhythm/application/TerminalDuke.java @@ -9,6 +9,9 @@ import java.nio.file.Paths; import java.util.ArrayList; +/** + * TerminalDuke is the entrypoint of the Terminal UI application of Duke. + */ final class TerminalDuke extends Application { public static void main(String[] args) { Path path = Paths.get(".", "tasks.json"); diff --git a/src/main/java/com/lockarhythm/storage/Storage.java b/src/main/java/com/lockarhythm/storage/Storage.java index 4413ea66..c770d632 100644 --- a/src/main/java/com/lockarhythm/storage/Storage.java +++ b/src/main/java/com/lockarhythm/storage/Storage.java @@ -15,6 +15,9 @@ import java.time.LocalDateTime; import java.util.ArrayList; +/** + * Storage handles all concerns relating to persisting tasks to disk. + */ public class Storage { private String filePath; @@ -22,6 +25,11 @@ public class Storage { private Gson gson; + /** + * Storage constructor takes in filePath as the destination path to write bytes to. + * + * @param filePath a valid location path to persist the tasks to. + */ public Storage(String filePath) { this.filePath = filePath; @@ -34,10 +42,21 @@ public Storage(String filePath) { .create(); } + /** + * registerList is meant to allow top-level Application to pass a reference a the Task List so that Storage can save a reference. + * + * @param list an array list of tasks to be persisted. + */ public void registerList(ArrayList list) { this.list = list; } + /** + * load reads the array list of tasks from disk. In order to handle persisting of abstract classes like Task with GSON, we need to pass its class in as parameter. + * + * @param type to pass the abstract type that is expected. + * @return an array list of tasks from disk. + */ public ArrayList load(Class type) { String content; try { @@ -55,6 +74,9 @@ public ArrayList load(Class type) { } } + /** + * overwrite writes the current array list to disk. + */ public void overwrite() throws IOException { FileOutputStream fo = new FileOutputStream(filePath); String js = gson.toJson(list); diff --git a/src/main/java/com/lockarhythm/ui/TerminalUI.java b/src/main/java/com/lockarhythm/ui/TerminalUI.java index 396b678d..084eea25 100644 --- a/src/main/java/com/lockarhythm/ui/TerminalUI.java +++ b/src/main/java/com/lockarhythm/ui/TerminalUI.java @@ -4,17 +4,35 @@ import java.util.Arrays; import java.util.Scanner; +/** + * TerminalUI is an adapter of UI port. + */ public final class TerminalUI implements UI { private static Scanner in = new Scanner(System.in); + /** + * nextLine reads the next line from standard input. + * + * @return the next line as a string + */ public String nextLine() { return in.nextLine(); } + /** + * hasNext returns true if another line is available in standard input. + * + * @return true if another line is available. False otherwise. + */ public boolean hasNext() { return in.hasNext(); } + /** + * print formats the given strings and prints to standard output. + * + * @param strings the list of strings to be printed + */ public void print(String... strings) { System.out.println("\t____________________________________________________________"); for (String s : strings) { @@ -24,6 +42,11 @@ public void print(String... strings) { System.out.println("\t____________________________________________________________\n"); } + /** + * print formats the given Result and prints to standard output. This is a helper function to handle the common operation of printing results from query interpreters. + * + * @param res the task Result to print. + */ public void print(Result res) { assert res != null; print(res.getText()); diff --git a/src/main/java/com/lockarhythm/ui/UI.java b/src/main/java/com/lockarhythm/ui/UI.java index 3a65d437..33c625f3 100644 --- a/src/main/java/com/lockarhythm/ui/UI.java +++ b/src/main/java/com/lockarhythm/ui/UI.java @@ -2,12 +2,35 @@ import com.lockarhythm.query.Result; +/** + * UI is a port. An example adapter is the TerminalUI. + */ public interface UI { + /** + * nextLine returns the next line from the input. + * + * @return the next line as a string. + */ public String nextLine(); + /** + * hasNext returns true if there are more line(s) to be consumed in the next call to nextLine. Returns false otherwise. + * + * @return true if a new line exists. False otherwise. + */ public boolean hasNext(); + /** + * print should print the given strings to the output. + * + * @param strings a list of strings to be printed + */ public void print(String... strings); + /** + * print is a helper function to print Results to output. + * + * @param res a task result to print. + */ public void print(Result res); }